2 CPUs • 1 GB heap memory • OpenTelemetry export is performed with ◦ Default span batch size of 512 ◦ 10% sampled spans ◦ JVM defaults… Meaning G1 GC • Running as a Java process (java -jar …) • No quarkus native compilation (for now) But we did many other combinations…
version Sample ratio (1%, 10%) Export Batch size Heap memory size G1 GC G1 GC with -Xms set Parallel GC Parallel GC with -Xms 500-1500 X X X 1000-3000 X X X X 1000-4000 X X X X X X X 1500-6000 X X X X X X X 2000-8000 X X X X X X X • Batch sizes (Nr. of spans): 256, 512, 1024 • Heap (MB): 256, 364, 512, 1024, 2048, 4096 • Later runs executed for: Baseline, MM, MM+OTel and OTel Java agent. • 2 and 4 CPU… But that is story for another day.
1 service • Removed JMS, after fixing a bug showing up under load: https://github.com/messaginghub/pooled-jms/pull/30 • We fixed 4 bugs in Quarkus • Optimized hibernate: ◦ merge->persist ◦ No Session on read ◦ Use projection to directly to get a DTO. • Streamline Connection pools quarkus.datasource.jdbc.max-size=50 # This number is a bit arbitrary and could be tuned quarkus.datasource.jdbc.detect-statement-leaks=false # small memory optimization allowed because we use Hibernate quarkus.thread-pool.max-threads=50 quarkus.thread-pool.core-threads=50 quarkus.thread-pool.keep-alive-time=1d # make sure we reuse, not recreate quarkus.vertx.event-loops-pool-size=1 # Because we use only 2 CPU
GC is optimized for predictable pause times: ◦ Memory divided in regions (young and old generation per region) ◦ Incremental and concurrent compaction on the regions • Parallel GC is optimized for throughput. ◦ Stop the world ◦ Scans all heap • However: ◦ For small heaps, < ~8GB! The scanning speed is big, outperforming G1. ◦ Probably not valid for all types of load. We plan to research that.
the observability impact • The Execution context matters • Micrometer metrics + bridge performance penalty is minimal • OpenTelemetry tracing introduces a performance penalty under heavy load • To mitigate the penalty: ◦ Reduce sampling. Below 10% is recommended. ◦ For fairly constant load the Parallel GC works best ◦ Locking the minimum heap memory seems to penalize the G1 GC. • The extension overperforms the agent in most of the tested contexts… • The extension's performance is more predictable (less variance between runs) • The Java agent, on small heaps, works best with the Parallel GC on.