Research library / Run story

313 Million Records. 525K ops/sec. Zero Latency Violations.

At-Least-Once Baseline · Optimization complete — three runs, one diagnosis, one result.

Records Written
313,479,978
Avg Throughput
525K ops/s
Peak Throughput
737K ops/s
Windows > 50ms MAX
Zero
Data Loss
Zero

Hardware: Intel i9-8950HK · 6 Cores / 12 Threads · 32GB RAM · GTX 1050 Ti Max-Q · JVM: 8GB heap · G1GC · 5 GC threads

The optimization journey · Three runs

From 264K to 525K ops/sec: What the Data Said

This result did not come from guessing. It came from reading three log files, parsing 654 GC pause events, and making two specific changes based on what the data showed. The optimization took two intermediate runs to isolate the cause before the third run confirmed the fix.

RunAvg EPSPeak EPSRecordsKey Change
Run 1 — Baseline (6GB heap, 3 GC threads batch=1000)264K664K157MOriginal config -38.6% drift
Run 2 — Partial fix (6GB heap, 3 GC threads batch=2000)278K534K162Mbatch=2000 +55.9% drift*
Run 3 — Optimized (8GB heap, 5 GC threads batch=2000)525K737K313M8GB + 5 GC threads -9.7% drift

* Run 2 improved drift direction but started slow due to large initial heap allocation from batch=2000. The GC root cause was still present.

The Diagnosis

Run 1's GC log showed 654 pause events in 10 minutes — average 90ms, P99 249ms, two catastrophic pauses at 2,571ms and 1,762ms. The live set averaged 1.6GB against a 6GB heap (26% occupancy), growing toward 3GB by the end. With only 3 GC threads on a 6-core machine under heavy allocation, G1 could not complete evacuation fast enough. The result was a -38.6% throughput decline across the run as GC pressure accumulated. The fix was structural: 8GB heap gives G1 working room, 5 GC threads complete evacuations faster.

Act 1 of 5 · The runner

heap=8g gc_threads=5. One Line Change. 99% Improvement.

The runner output tells the complete provenance story. One command, one CSV row, the JVM configuration printed before launch: heap=8g gc_threads=5. These two numbers in the runner output represent the entire optimization — everything else in the config was carried over from Run 2.

The Grafana time range (from=1773675525456, to=1773676129881) is written at actual start and stop time by the runner. It locks the dashboard to exactly this run window — not a prior run, not a manually selected interval. The 10.07 minute runtime matches the 10-minute configured duration.

Terminal output of the benchmark runner script showing the test configuration, topic reset, completion status, and Grafana time range.
Image 5 — Runner confirms heap=8g gc_threads=5. Topic reset. Pipeline started. Completed 10.07 minutes. Grafana timestamps printed automatically.
Terminal output of the machine profile script listing the CPU, RAM, and GPU of the benchmark machine.
Image 4 — Hardware: Intel i9-8950HK, 6 Cores / 12 Threads, 32GB RAM, GTX 1050 Ti Max-Q. Same machine as all three runs — the improvement is purely from JVM configuration.

Act 2 of 5 · Post-run verification

313,479,978 Records. 12 Partitions. Even Distribution.

After the runner completes, the post-run script queries the Kafka topic directly. 313,479,978 records written, distributed across all 12 partitions within a 1.3M record variance — the tightest spread across any of the three runs. This confirms all 12 pipeline workers were active and producing at comparable rates throughout the full 10 minutes.

The WireEvent sample records show the expected structure: 512-byte payload, unique UUID key, vector=null — correct for the at-least-once baseline profile which does not run ONNX inference. Each UUID key confirms unique identity across all 313M events. No consumer groups are registered because this is a pure producer pipeline — the Kafka topic is the output artifact, not a consumed stream.

Terminal output of the post-run script listing per-partition record counts and sample messages after the run.
Image 6 — 313,479,978 total records. 12 partitions, evenly distributed (25.4M–26.7M per partition). WireEvent samples: 512 bytes, UUID keys, vector=null. Zero consumer lag (producer-only pipeline).

Why even partition distribution matters

Less than 1.3M variance across 12 partitions at 313M total records — the tightest spread of the three optimization runs. This confirms all 12 pipeline workers were equally loaded throughout the run. Uneven distribution would indicate key skew or a worker starvation problem. The UUID key strategy distributes records uniformly by design.

Act 3 of 5 · Executive dashboard

261K ops/sec Snapshot. P50 through P99: Sub-Millisecond.

The Grafana executive summary captures a moment during the run: 261K ops/sec at the snapshot instant, 100% integrity, zero loss rate. The throughput chart tells the real story — the pipeline ramps from zero, reaches 400–600K ops/sec by the 5-minute mark, and sustains it through the end of the run with the characteristic variance of a CPU-bound G1GC workload.

The latency panel is the most important change from the previous runs. P50, P95, and P99 are all 0.001ms. P99.9 is 0.006ms — down from 0.019ms in Run 2. The MAX latency chart has a ceiling of 80ms compared to 300ms in the previous runs. Zero windows exceeded 50ms throughout the entire 10-minute run. This is the GC fix working exactly as intended.

The Kafka sink panel shows burst send rates reaching 10–25M ops/s on transactional batch commits — identical pattern to the exactly-once runs but without the 4-second queue time spikes that EOS transactions produce. Write latency stabilizes under 250ms after the initial warmup.

Grafana executive dashboard for the run, with throughput, integrity, latency percentile, load, and Kafka sink panels.
Image 1 — Executive summary: 261K ops/s at snapshot, 100% integrity, 0 loss. P50/P95/P99: 0.001ms. P99.9: 0.006ms. MAX latency chart ceiling: 80ms. Kafka sink burst reaching 25M ops/s.

Act 4 of 5 · Security & health

261K Auth Decisions/sec. Zero Errors. Zero Denials. Zero Record Loss.

The security panel shows auth decisions at 261K/sec — tracking throughput exactly, because PERMIT_ALL security evaluates every record. The Security Events chart confirms the pipeline ramped up at 10:39 and held throughput steadily through 10:48. Zero denials, zero auth errors, zero security errors throughout.

The Errors & Health panel shows four flat lines at zero: source errors, auth errors, DLQ errors, and security denials. The Record Loss panel confirms zero dropped records and zero DLQ routes for the entire run. The Source Starvation panel shows admitted/s tracking the throughput ramp — no starvation events, the source kept pace.

Grafana security and health dashboard for the run, with auth decision, source starvation, error rate, and record loss panels.
Image 2 — Auth decisions at 261K/sec tracking throughput. Zero errors across all categories. Zero record loss. Zero DLQ routes. Source starvation: none detected.

Act 5 of 5 · Pipeline integrity & JVM

100% Integrity. GC Pause Time: 2.52ms. Heap Cycling Cleanly.

The pipeline integrity gauges show 100%, 0%, 0% — the same result as every other StreamKernel run. What changed is the JVM panel beneath it. GC collection rate is 0.111 ops/s at the snapshot moment and GC pause time is 2.52ms — not the 90ms average that the baseline run showed.

The JVM Heap Over Time chart shows the 8GB maximum (yellow flat line) with the heap cycling cleanly between 1GB and 5.3GB. G1 is collecting efficiently at each cycle — the heap drops from 4-5GB back to under 1GB on each collection. This is the evacuation-to-free-space ratio that the baseline run could not achieve: with 6GB heap and a 3GB live set, G1 had nowhere to evacuate into. With 8GB and 5 threads, each collection completes before the next allocation wave arrives.

Thread count is stable at 24 live threads throughout — no thread leaks, no executor exhaustion. The thread count chart shows the transition from warmup (22 threads) to steady-state (24 threads) at exactly the moment throughput ramped up at 10:39.

Grafana pipeline integrity and JVM dashboard for the run, with integrity gauges, heap over time, garbage collection, and thread count panels.
Image 3 — Integrity: 100%. Drop: 0%. DLQ: 0%. Heap: 847MB at snapshot, max 8GB. GC collection: 0.111 ops/s. GC pause: 2.52ms. 24 live threads stable.

Why 2.52ms GC pause time matters

The baseline run averaged 90ms GC pause time with a P99 of 249ms and two pauses exceeding 2,500ms. Those pauses stopped the pipeline completely while G1 evacuated the young generation. At 2.52ms, the GC is completing between batch dispatch cycles — invisible to throughput. The -9.7% drift across the run (from 552K to 498K first-to-last third) is normal G1 variance, not accumulating GC debt.

Summary · Three-run optimization result

The Numbers Across All Three Runs

MetricRun 1 — BaselineRun 2 — PartialRun 3 — Optimized
JVM Config6GB heap, 3 GC threads6GB heap, 3 GC threads8GB heap, 5 GC threads
batch.size100020002000
Avg EPS264K ops/s278K ops/s525K ops/s
Peak EPS664K ops/s534K ops/s737K ops/s
Total Records157,090,000162,350,000313,479,978
Throughput Drift-38.6% (declining)+55.9% (improving)-9.7% (stable)
GC Pauses > 100ms226 windows~130 windows0 windows
GC Max Pause2,571ms270ms47ms
Avg MAX Latency57.3ms55.9ms2.5ms
Windows > 50ms MAX52470
Pipeline Integrity100%100%100%
Data LossZeroZeroZero

The two-change story

HeapGb: 6 → 8. GcThreads: 3 → 5. That is the complete list of changes between Run 1 and Run 3. No algorithm changes, no architectural changes, no Kafka tuning. The same pipeline, same config, same machine — just giving G1GC the heap headroom and parallel threads it needed to keep pace with a 500K+ ops/sec allocation rate. The 99% improvement in average throughput came entirely from unblocking the GC.

Commercial path

Want to turn this paper into a concrete evaluation?

Bring the action you have in mind and we will map it to what the runtime does today.