diff --git a/README.md b/README.md index 64147ff5..c7d380b3 100644 --- a/README.md +++ b/README.md @@ -110,9 +110,9 @@ Full semantics: [Outcomes & Delivery](https://shove.rs/concepts/outcomes). The charts are generated from a committed results document — never hand-copied — and each one carries its own provenance (shove version, generation date, hardware) in the caption. The harness runs against every backend; the currently -published document measures the in-process, Kafka, NATS, RabbitMQ and Redis -backends, and a plotted series appears only for a backend the document actually -contains. SQS is absent because it runs only against LocalStack. See +published document measures all six backends, and a plotted series appears only +for a backend the document actually contains. The SQS series measures LocalStack +rather than the AWS service and runs a smaller, recorded corpus. See [Measurement methodology](https://shove.rs/ops/performance#measurement-methodology) for what is measured and what is still pending. diff --git a/benches/results/bench-results.json b/benches/results/bench-results.json index 1d305b89..aa90992c 100644 --- a/benches/results/bench-results.json +++ b/benches/results/bench-results.json @@ -1,6 +1,6 @@ { "schema_version": 6, - "generated_at": "2026-09-08T12:34:58Z", + "generated_at": "2026-09-08T16:10:59Z", "shove_version": "0.14.0", "rust_version": "rustc 1.91.1 (ed61e7d7e 2025-11-07)", "hardware": { @@ -11526,7 +11526,7 @@ "published": 999942, "published_at_window_end": 999942, "processed_at_window_end": 415042, - "achieved_publish_msg_per_sec": 99976.59496143069, + "achieved_publish_msg_per_sec": 99976.59496143067, "lag_at_window_end": 584900, "peak_lag": 585051, "drain_secs": 7.11318075, @@ -11615,7 +11615,7 @@ "consumers": 1, "handler": "zero (no-op)", "handler_cost": "framework", - "throughput_msg_per_sec": 24997.250126494182, + "throughput_msg_per_sec": 24997.250126494186, "dispatch_p50_ms": 0.809542, "dispatch_p95_ms": 5.515083, "dispatch_p99_ms": 39.537708, @@ -11634,7 +11634,7 @@ "published": 249991, "published_at_window_end": 249991, "processed_at_window_end": 249984, - "achieved_publish_msg_per_sec": 24997.950094295662, + "achieved_publish_msg_per_sec": 24997.950094295666, "lag_at_window_end": 7, "peak_lag": 1521, "drain_secs": 0.012859584, @@ -11689,7 +11689,7 @@ "consumers": 1, "handler": "zero (no-op)", "handler_cost": "framework", - "throughput_msg_per_sec": 10330.801278734461, + "throughput_msg_per_sec": 10330.80127873446, "dispatch_p50_ms": 0.0, "dispatch_p95_ms": 0.0, "dispatch_p99_ms": 0.0, @@ -11769,7 +11769,7 @@ "e2e_p99_ms": 18594.4215, "scaling_efficiency": 1.0, "peak_rss_mb": 513.46875, - "cpu_pct": 0.0019248656007548402, + "cpu_pct": 0.00192486560075484, "duration_secs": 10.219801, "setup_secs": 0.003325125, "load": { @@ -11910,7 +11910,7 @@ "e2e_p99_ms": 121.805, "scaling_efficiency": 1.0, "peak_rss_mb": 0.03125, - "cpu_pct": 0.0074358304613442215, + "cpu_pct": 0.007435830461344221, "duration_secs": 10.0005105, "setup_secs": 0.255776083, "load": { @@ -11949,7 +11949,7 @@ "e2e_p99_ms": 21.683708, "scaling_efficiency": 1.0, "peak_rss_mb": 0.046875, - "cpu_pct": 0.0073930300817217996, + "cpu_pct": 0.0073930300817218, "duration_secs": 10.002407292, "setup_secs": 0.26020625, "load": { @@ -11998,7 +11998,7 @@ "published": 999857, "published_at_window_end": 999857, "processed_at_window_end": 536000, - "achieved_publish_msg_per_sec": 99908.68081757419, + "achieved_publish_msg_per_sec": 99908.6808175742, "lag_at_window_end": 463857, "peak_lag": 464357, "drain_secs": 5.306837542, @@ -12151,7 +12151,7 @@ "published": 998837, "published_at_window_end": 998837, "processed_at_window_end": 425500, - "achieved_publish_msg_per_sec": 99840.73603526563, + "achieved_publish_msg_per_sec": 99840.73603526564, "lag_at_window_end": 573337, "peak_lag": 573837, "drain_secs": 7.605652083, @@ -12171,7 +12171,7 @@ "handler_cost": "framework", "max_batch_size": 500, "max_batch_age_ms": 250, - "throughput_msg_per_sec": 12287.392293703559, + "throughput_msg_per_sec": 12287.39229370356, "dispatch_p50_ms": 0.0, "dispatch_p95_ms": 0.0, "dispatch_p99_ms": 0.0, @@ -12354,7 +12354,7 @@ "consumers": 1, "handler": "zero (no-op)", "handler_cost": "framework", - "throughput_msg_per_sec": 24992.642555409526, + "throughput_msg_per_sec": 24992.64255540953, "dispatch_p50_ms": 0.668959, "dispatch_p95_ms": 1.393584, "dispatch_p99_ms": 4.785583, @@ -12373,7 +12373,7 @@ "published": 249992, "published_at_window_end": 249992, "processed_at_window_end": 249992, - "achieved_publish_msg_per_sec": 24992.642555409526, + "achieved_publish_msg_per_sec": 24992.64255540953, "lag_at_window_end": 0, "peak_lag": 144, "drain_secs": 3.34e-7, @@ -12400,7 +12400,7 @@ "e2e_p99_ms": 6722.049458, "scaling_efficiency": 1.0, "peak_rss_mb": 0.015625, - "cpu_pct": 0.0030812440914528274, + "cpu_pct": 0.003081244091452827, "duration_secs": 10.0009345, "setup_secs": 0.007422917, "load": { @@ -12410,7 +12410,7 @@ "published": 999797, "published_at_window_end": 999797, "processed_at_window_end": 414937, - "achieved_publish_msg_per_sec": 99970.35777006639, + "achieved_publish_msg_per_sec": 99970.3577700664, "lag_at_window_end": 584860, "peak_lag": 584917, "drain_secs": 6.745520083, @@ -12555,7 +12555,7 @@ "published": 999854, "published_at_window_end": 999854, "processed_at_window_end": 299825, - "achieved_publish_msg_per_sec": 99982.55591621093, + "achieved_publish_msg_per_sec": 99982.55591621091, "lag_at_window_end": 700029, "peak_lag": 700029, "drain_secs": 9.670435209, @@ -12616,7 +12616,7 @@ "e2e_p99_ms": 23.675291, "scaling_efficiency": 1.0, "peak_rss_mb": 0.0, - "cpu_pct": 0.009418246224611201, + "cpu_pct": 0.0094182462246112, "duration_secs": 10.000514291, "setup_secs": 0.007588583, "load": { @@ -12891,7 +12891,7 @@ "e2e_p50_ms": 0.651333, "e2e_p95_ms": 1.609625, "e2e_p99_ms": 7.145291, - "scaling_efficiency": 1.0000477042112739, + "scaling_efficiency": 1.000047704211274, "peak_rss_mb": 0.953125, "cpu_pct": 0.0066620959501269734, "duration_secs": 10.000318, @@ -12999,7 +12999,7 @@ "e2e_p50_ms": 0.993584, "e2e_p95_ms": 2.218916, "e2e_p99_ms": 6.441291, - "scaling_efficiency": 0.9999745522628961, + "scaling_efficiency": 0.999974552262896, "peak_rss_mb": 0.0, "cpu_pct": 0.007639459699212781, "duration_secs": 10.00066075, @@ -13036,7 +13036,7 @@ "e2e_p50_ms": 0.756208, "e2e_p95_ms": 5.178083, "e2e_p99_ms": 10.860416, - "scaling_efficiency": 0.9998431855299769, + "scaling_efficiency": 0.9998431855299768, "peak_rss_mb": 0.0, "cpu_pct": 0.0079588760737852, "duration_secs": 10.001388292, @@ -13073,7 +13073,7 @@ "e2e_p50_ms": 7620.923834, "e2e_p95_ms": 9018.027916, "e2e_p99_ms": 9128.07675, - "scaling_efficiency": 0.9295776241149429, + "scaling_efficiency": 0.9295776241149428, "peak_rss_mb": 0.0, "cpu_pct": 0.003164486523876151, "duration_secs": 10.004132833, @@ -13085,7 +13085,7 @@ "published": 999641, "published_at_window_end": 999641, "processed_at_window_end": 298116, - "achieved_publish_msg_per_sec": 99922.80357399369, + "achieved_publish_msg_per_sec": 99922.80357399367, "lag_at_window_end": 701525, "peak_lag": 701625, "drain_secs": 9.145857292, @@ -13103,7 +13103,7 @@ "consumers": 2, "handler": "zero (no-op)", "handler_cost": "framework", - "throughput_msg_per_sec": 10867.180755616655, + "throughput_msg_per_sec": 10867.180755616657, "dispatch_p50_ms": 0.0, "dispatch_p95_ms": 0.0, "dispatch_p99_ms": 0.0, @@ -13213,7 +13213,7 @@ "handler_cost": "framework", "max_batch_size": 500, "max_batch_age_ms": 250, - "throughput_msg_per_sec": 98701.35747210549, + "throughput_msg_per_sec": 98701.35747210548, "dispatch_p50_ms": 0.0, "dispatch_p95_ms": 0.0, "dispatch_p99_ms": 0.0, @@ -13288,7 +13288,7 @@ "handler_cost": "framework", "max_batch_size": 500, "max_batch_age_ms": 250, - "throughput_msg_per_sec": 24927.614512654134, + "throughput_msg_per_sec": 24927.614512654138, "dispatch_p50_ms": 20.718792, "dispatch_p95_ms": 38.581916, "dispatch_p99_ms": 41.200458, @@ -13409,7 +13409,7 @@ "e2e_p50_ms": 100.687833, "e2e_p95_ms": 189.619, "e2e_p99_ms": 197.990208, - "scaling_efficiency": 0.9999968085530697, + "scaling_efficiency": 0.9999968085530696, "peak_rss_mb": 0.0, "cpu_pct": 0.008237452037395227, "duration_secs": 10.000479625, @@ -13499,7 +13499,7 @@ "published": 999693, "published_at_window_end": 999693, "processed_at_window_end": 455500, - "achieved_publish_msg_per_sec": 99789.36813381441, + "achieved_publish_msg_per_sec": 99789.3681338144, "lag_at_window_end": 544193, "peak_lag": 545193, "drain_secs": 6.856132084, @@ -13519,7 +13519,7 @@ "handler_cost": "framework", "max_batch_size": 500, "max_batch_age_ms": 250, - "throughput_msg_per_sec": 11959.757215208021, + "throughput_msg_per_sec": 11959.75721520802, "dispatch_p50_ms": 0.0, "dispatch_p95_ms": 0.0, "dispatch_p99_ms": 0.0, @@ -13564,7 +13564,7 @@ "e2e_p99_ms": 200.855708, "scaling_efficiency": 0.9927957576071276, "peak_rss_mb": 0.015625, - "cpu_pct": 0.009901009531405219, + "cpu_pct": 0.00990100953140522, "duration_secs": 10.000582917, "setup_secs": 0.257124875, "load": { @@ -13746,7 +13746,7 @@ "e2e_p50_ms": 4556.83875, "e2e_p95_ms": 5069.140542, "e2e_p99_ms": 5092.957375, - "scaling_efficiency": 1.2652321978357495, + "scaling_efficiency": 1.2652321978357497, "peak_rss_mb": 0.03125, "cpu_pct": 0.0027086344519577383, "duration_secs": 10.001150667, @@ -13758,7 +13758,7 @@ "published": 999913, "published_at_window_end": 999913, "processed_at_window_end": 525003, - "achieved_publish_msg_per_sec": 99979.79565484733, + "achieved_publish_msg_per_sec": 99979.79565484732, "lag_at_window_end": 474910, "peak_lag": 474981, "drain_secs": 5.098691417, @@ -13792,7 +13792,7 @@ "drain": { "corpus": 3145728, "published": 3145728, - "fill_secs": 14.905488375000001, + "fill_secs": 14.905488375, "producers": 8, "unique_at_start": 141, "unique_at_end": 2831156, @@ -13854,7 +13854,7 @@ "e2e_p50_ms": 0.748583, "e2e_p95_ms": 4.034667, "e2e_p99_ms": 15.65175, - "scaling_efficiency": 0.9998657002843561, + "scaling_efficiency": 0.999865700284356, "peak_rss_mb": 0.0, "cpu_pct": 0.008010319362435825, "duration_secs": 10.000739458, @@ -13903,7 +13903,7 @@ "published": 999842, "published_at_window_end": 999842, "processed_at_window_end": 388875, - "achieved_publish_msg_per_sec": 99963.18440986301, + "achieved_publish_msg_per_sec": 99963.184409863, "lag_at_window_end": 610967, "peak_lag": 611003, "drain_secs": 7.768021458, @@ -13928,7 +13928,7 @@ "e2e_p50_ms": 0.0, "e2e_p95_ms": 0.0, "e2e_p99_ms": 0.0, - "scaling_efficiency": 0.9126470286923047, + "scaling_efficiency": 0.9126470286923049, "peak_rss_mb": 0.375, "cpu_pct": 0.0, "duration_secs": 4.382545667, @@ -13964,7 +13964,7 @@ "e2e_p99_ms": 65.277917, "scaling_efficiency": 1.0000041475012296, "peak_rss_mb": 0.484375, - "cpu_pct": 0.009088461398919463, + "cpu_pct": 0.009088461398919464, "duration_secs": 10.001673375, "setup_secs": 0.223973625, "load": { @@ -13999,7 +13999,7 @@ "e2e_p50_ms": 17804.993916, "e2e_p95_ms": 23105.672542, "e2e_p99_ms": 23738.285042, - "scaling_efficiency": 1.4756805432845561, + "scaling_efficiency": 1.475680543284556, "peak_rss_mb": 16.84375, "cpu_pct": 0.001973809493290126, "duration_secs": 10.159710334, @@ -14011,7 +14011,7 @@ "published": 194206, "published_at_window_end": 194206, "processed_at_window_end": 12750, - "achieved_publish_msg_per_sec": 19115.308765258742, + "achieved_publish_msg_per_sec": 19115.308765258746, "lag_at_window_end": 181456, "peak_lag": 181767, "drain_secs": 23.683451083, @@ -14073,14 +14073,14 @@ "consumers": 2, "handler": "zero (no-op)", "handler_cost": "setup_bound", - "throughput_msg_per_sec": 12501.059996129723, + "throughput_msg_per_sec": 12501.059996129725, "dispatch_p50_ms": 214.071209, "dispatch_p95_ms": 295.482541, "dispatch_p99_ms": 335.412209, "e2e_p50_ms": 214.07125, "e2e_p95_ms": 295.482583, "e2e_p99_ms": 335.41225, - "scaling_efficiency": 1.6825717319635827, + "scaling_efficiency": 1.682571731963583, "peak_rss_mb": 0.0, "cpu_pct": 0.0, "duration_secs": 0.799932166, @@ -14136,7 +14136,7 @@ "e2e_p50_ms": 1.0305, "e2e_p95_ms": 1.971, "e2e_p99_ms": 3.0445, - "scaling_efficiency": 1.0001862169251685, + "scaling_efficiency": 1.0001862169251683, "peak_rss_mb": 0.0, "cpu_pct": 0.00844960029841755, "duration_secs": 10.000433625, @@ -14222,7 +14222,7 @@ "published": 999895, "published_at_window_end": 999895, "processed_at_window_end": 430898, - "achieved_publish_msg_per_sec": 99983.09649260035, + "achieved_publish_msg_per_sec": 99983.09649260037, "lag_at_window_end": 568997, "peak_lag": 568997, "drain_secs": 6.164499416, @@ -14318,9 +14318,9 @@ "e2e_p50_ms": 0.765417, "e2e_p95_ms": 5.694083, "e2e_p99_ms": 22.141, - "scaling_efficiency": 0.9999103996509273, + "scaling_efficiency": 0.9999103996509272, "peak_rss_mb": 0.0, - "cpu_pct": 0.0036295013671248516, + "cpu_pct": 0.003629501367124851, "duration_secs": 10.001356125, "setup_secs": 0.003353292, "load": { @@ -14401,7 +14401,7 @@ "drain": { "corpus": 49152, "published": 49152, - "fill_secs": 1.9865243750000001, + "fill_secs": 1.986524375, "producers": 8, "unique_at_start": 14, "unique_at_end": 44237, @@ -14426,7 +14426,7 @@ "e2e_p50_ms": 4.322083, "e2e_p95_ms": 14.486542, "e2e_p99_ms": 22.410917, - "scaling_efficiency": 0.9993326615666733, + "scaling_efficiency": 0.9993326615666732, "peak_rss_mb": 0.0, "cpu_pct": 0.006931796247561447, "duration_secs": 10.000974708, @@ -14465,7 +14465,7 @@ "e2e_p99_ms": 28533.6645, "scaling_efficiency": 0.8796039022399285, "peak_rss_mb": 0.15625, - "cpu_pct": 0.0010507112454635423, + "cpu_pct": 0.0010507112454635425, "duration_secs": 11.779791542, "setup_secs": 0.008854833, "load": { @@ -14502,7 +14502,7 @@ "e2e_p50_ms": 0.0, "e2e_p95_ms": 0.0, "e2e_p99_ms": 0.0, - "scaling_efficiency": 0.9612585320175533, + "scaling_efficiency": 0.9612585320175532, "peak_rss_mb": 0.953125, "cpu_pct": 0.0, "duration_secs": 60.650247, @@ -14618,7 +14618,7 @@ "e2e_p99_ms": 9546.211958, "scaling_efficiency": 0.6950060555181652, "peak_rss_mb": 15.125, - "cpu_pct": 0.0019177211578175453, + "cpu_pct": 0.0019177211578175451, "duration_secs": 10.033949125, "setup_secs": 0.259520958, "load": { @@ -14844,7 +14844,7 @@ "e2e_p50_ms": 132.800291, "e2e_p95_ms": 248.875083, "e2e_p99_ms": 276.768792, - "scaling_efficiency": 0.9877193808195779, + "scaling_efficiency": 0.987719380819578, "peak_rss_mb": 0.015625, "cpu_pct": 0.011737929423781192, "duration_secs": 10.011163708, @@ -14915,7 +14915,7 @@ "e2e_p50_ms": 1.038334, "e2e_p95_ms": 2.2315, "e2e_p99_ms": 4.336375, - "scaling_efficiency": 0.9999316352922525, + "scaling_efficiency": 0.9999316352922524, "peak_rss_mb": 0.0, "cpu_pct": 0.004708788408965454, "duration_secs": 10.00254525, @@ -15060,7 +15060,7 @@ "e2e_p50_ms": 0.985583, "e2e_p95_ms": 2.249, "e2e_p99_ms": 4.721375, - "scaling_efficiency": 1.0001694924377567, + "scaling_efficiency": 1.0001694924377569, "peak_rss_mb": 0.0, "cpu_pct": 0.006849176472143981, "duration_secs": 10.001183666, @@ -15146,7 +15146,7 @@ "published": 999220, "published_at_window_end": 999220, "processed_at_window_end": 189822, - "achieved_publish_msg_per_sec": 99866.09620766915, + "achieved_publish_msg_per_sec": 99866.09620766916, "lag_at_window_end": 809398, "peak_lag": 809556, "drain_secs": 14.109838416, @@ -15180,7 +15180,7 @@ "drain": { "corpus": 49152, "published": 49152, - "fill_secs": 2.8779992500000002, + "fill_secs": 2.87799925, "producers": 8, "unique_at_start": 91, "unique_at_end": 44237, @@ -15205,7 +15205,7 @@ "e2e_p50_ms": 48.50025, "e2e_p95_ms": 534.0625, "e2e_p99_ms": 549.687666, - "scaling_efficiency": 0.9498915595723769, + "scaling_efficiency": 0.9498915595723768, "peak_rss_mb": 0.0, "cpu_pct": 0.008229323658562384, "duration_secs": 10.006911667, @@ -15372,7 +15372,7 @@ "consumers": 8, "handler": "zero (no-op)", "handler_cost": "framework", - "throughput_msg_per_sec": 24996.309282605158, + "throughput_msg_per_sec": 24996.30928260516, "dispatch_p50_ms": 0.647125, "dispatch_p95_ms": 7.485584, "dispatch_p99_ms": 104.603542, @@ -15409,7 +15409,7 @@ "consumers": 8, "handler": "zero (no-op)", "handler_cost": "framework", - "throughput_msg_per_sec": 15938.195787683933, + "throughput_msg_per_sec": 15938.195787683931, "dispatch_p50_ms": 12821.782291, "dispatch_p95_ms": 18680.2875, "dispatch_p99_ms": 19216.345584, @@ -15418,7 +15418,7 @@ "e2e_p99_ms": 19216.345667, "scaling_efficiency": 0.38408166812229366, "peak_rss_mb": 0.0, - "cpu_pct": 0.0018627699959376566, + "cpu_pct": 0.0018627699959376568, "duration_secs": 10.005963167, "setup_secs": 0.007372375, "load": { @@ -15524,7 +15524,7 @@ "e2e_p50_ms": 0.863583, "e2e_p95_ms": 18.68875, "e2e_p99_ms": 108.130125, - "scaling_efficiency": 1.0000125162809135, + "scaling_efficiency": 1.0000125162809137, "peak_rss_mb": 0.0, "cpu_pct": 0.003615733178855054, "duration_secs": 10.000334833, @@ -15573,7 +15573,7 @@ "published": 998797, "published_at_window_end": 998797, "processed_at_window_end": 110301, - "achieved_publish_msg_per_sec": 99794.74265984303, + "achieved_publish_msg_per_sec": 99794.74265984305, "lag_at_window_end": 888496, "peak_lag": 888594, "drain_secs": 17.130430917, @@ -15607,7 +15607,7 @@ "drain": { "corpus": 49152, "published": 49152, - "fill_secs": 2.9766336669999998, + "fill_secs": 2.976633667, "producers": 8, "unique_at_start": 27, "unique_at_end": 44237, @@ -15632,7 +15632,7 @@ "e2e_p50_ms": 253.494792, "e2e_p95_ms": 563.414667, "e2e_p99_ms": 579.609334, - "scaling_efficiency": 0.9474317493776951, + "scaling_efficiency": 0.9474317493776953, "peak_rss_mb": 0.0, "cpu_pct": 0.009018040698634022, "duration_secs": 10.004803584, @@ -15746,7 +15746,7 @@ "e2e_p50_ms": 80.292208, "e2e_p95_ms": 150.616958, "e2e_p99_ms": 157.592, - "scaling_efficiency": 0.9979901010689285, + "scaling_efficiency": 0.9979901010689284, "peak_rss_mb": 0.0, "cpu_pct": 0.006498896630747424, "duration_secs": 10.002466334, @@ -15860,7 +15860,7 @@ "e2e_p50_ms": 131.131167, "e2e_p95_ms": 245.307042, "e2e_p99_ms": 253.993458, - "scaling_efficiency": 0.9875198787430943, + "scaling_efficiency": 0.9875198787430944, "peak_rss_mb": 0.0, "cpu_pct": 0.004220269274278993, "duration_secs": 10.001218167, @@ -15899,7 +15899,7 @@ "e2e_p50_ms": 80.803625, "e2e_p95_ms": 151.037833, "e2e_p99_ms": 159.793625, - "scaling_efficiency": 0.9939280586795309, + "scaling_efficiency": 0.9939280586795308, "peak_rss_mb": 0.0, "cpu_pct": 0.005716607443133121, "duration_secs": 10.001314667, @@ -15938,7 +15938,7 @@ "e2e_p50_ms": 2911.355417, "e2e_p95_ms": 4230.988709, "e2e_p99_ms": 4335.37375, - "scaling_efficiency": 0.9679515740317993, + "scaling_efficiency": 0.9679515740317992, "peak_rss_mb": 2.84375, "cpu_pct": 0.004270338128903028, "duration_secs": 10.056202125, @@ -16131,7 +16131,7 @@ "published": 998496, "published_at_window_end": 998496, "processed_at_window_end": 200624, - "achieved_publish_msg_per_sec": 99672.85635338207, + "achieved_publish_msg_per_sec": 99672.85635338209, "lag_at_window_end": 797872, "peak_lag": 798672, "drain_secs": 12.396842333, @@ -16149,7 +16149,7 @@ "consumers": 8, "handler": "zero (no-op)", "handler_cost": "framework", - "throughput_msg_per_sec": 62831.715690833444, + "throughput_msg_per_sec": 62831.71569083344, "dispatch_p50_ms": 0.0, "dispatch_p95_ms": 0.0, "dispatch_p99_ms": 0.0, @@ -16276,7 +16276,7 @@ "published": 998627, "published_at_window_end": 998627, "processed_at_window_end": 219990, - "achieved_publish_msg_per_sec": 99572.27877913837, + "achieved_publish_msg_per_sec": 99572.27877913836, "lag_at_window_end": 778637, "peak_lag": 779344, "drain_secs": 12.310594416, @@ -16384,7 +16384,7 @@ "published": 126973, "published_at_window_end": 126973, "processed_at_window_end": 23862, - "achieved_publish_msg_per_sec": 12413.710389992015, + "achieved_publish_msg_per_sec": 12413.710389992017, "lag_at_window_end": 103111, "peak_lag": 104325, "drain_secs": 13.899615875, @@ -26566,6 +26566,2044 @@ } ], "unsupported": [] + }, + { + "backend": "sqs", + "broker": { + "name": "AWS SQS (LocalStack)", + "version": "4.5", + "deployment": "docker localstack" + }, + "representative": false, + "results": [ + { + "flow": "publish_single", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "no_handler", + "throughput_msg_per_sec": 256.40363811954927, + "dispatch_p50_ms": 3.6055, + "dispatch_p95_ms": 6.332167, + "dispatch_p99_ms": 8.353209, + "e2e_p50_ms": 3.6055, + "e2e_p95_ms": 6.332167, + "e2e_p99_ms": 8.353209, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.296875, + "cpu_pct": 0.1496581220858443, + "duration_secs": 19.500503334, + "setup_secs": 0.0 + }, + { + "flow": "publish_single", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "no_handler", + "throughput_msg_per_sec": 267.2995172913235, + "dispatch_p50_ms": 3.555208, + "dispatch_p95_ms": 5.844916, + "dispatch_p99_ms": 7.311334, + "e2e_p50_ms": 3.555208, + "e2e_p95_ms": 5.844916, + "e2e_p99_ms": 7.311334, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.5, + "cpu_pct": 0.0041484850151422255, + "duration_secs": 18.705608041, + "setup_secs": 0.0 + }, + { + "flow": "publish_single", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "no_handler", + "throughput_msg_per_sec": 200.7954726778425, + "dispatch_p50_ms": 4.809041, + "dispatch_p95_ms": 7.297291, + "dispatch_p99_ms": 8.902917, + "e2e_p50_ms": 4.809041, + "e2e_p95_ms": 7.297291, + "e2e_p99_ms": 8.902917, + "scaling_efficiency": 1.0, + "peak_rss_mb": 62.984375, + "cpu_pct": 0.002489860765697676, + "duration_secs": 24.900959834, + "setup_secs": 0.0 + }, + { + "flow": "publish_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "no_handler", + "throughput_msg_per_sec": 1868.439965669284, + "dispatch_p50_ms": 242.175209, + "dispatch_p95_ms": 432.388292, + "dispatch_p99_ms": 432.388292, + "e2e_p50_ms": 242.175209, + "e2e_p95_ms": 432.388292, + "e2e_p99_ms": 432.388292, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.265625, + "cpu_pct": 0.0, + "duration_secs": 2.67602925, + "setup_secs": 0.0 + }, + { + "flow": "publish_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "no_handler", + "throughput_msg_per_sec": 1864.716786424752, + "dispatch_p50_ms": 243.864292, + "dispatch_p95_ms": 490.457167, + "dispatch_p99_ms": 490.457167, + "e2e_p50_ms": 243.864292, + "e2e_p95_ms": 490.457167, + "e2e_p99_ms": 490.457167, + "scaling_efficiency": 1.0, + "peak_rss_mb": 1.96875, + "cpu_pct": 0.0, + "duration_secs": 2.681372333, + "setup_secs": 0.0 + }, + { + "flow": "publish_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "no_handler", + "throughput_msg_per_sec": 352.2190083705288, + "dispatch_p50_ms": 1409.746834, + "dispatch_p95_ms": 1584.952125, + "dispatch_p99_ms": 1584.952125, + "e2e_p50_ms": 1409.746834, + "e2e_p95_ms": 1584.952125, + "e2e_p99_ms": 1584.952125, + "scaling_efficiency": 1.0, + "peak_rss_mb": 245.390625, + "cpu_pct": 0.014138052073168423, + "duration_secs": 14.195713125, + "setup_secs": 0.0 + }, + { + "flow": "consume_fifo", + "mode": "fifo", + "payload_bytes": 64, + "tier": "moderate", + "messages": 800, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "setup_bound", + "throughput_msg_per_sec": 211.32509221633333, + "dispatch_p50_ms": 1987.676125, + "dispatch_p95_ms": 3220.601125, + "dispatch_p99_ms": 3365.030292, + "e2e_p50_ms": 1987.676417, + "e2e_p95_ms": 3220.601334, + "e2e_p99_ms": 3365.030375, + "scaling_efficiency": 1.0, + "peak_rss_mb": 1.765625, + "cpu_pct": 0.0, + "duration_secs": 3.785636583, + "setup_secs": null + }, + { + "flow": "consume_fifo", + "mode": "fifo", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 800, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "setup_bound", + "throughput_msg_per_sec": 24.583568308636135, + "dispatch_p50_ms": 31255.166291, + "dispatch_p95_ms": 31861.110292, + "dispatch_p99_ms": 32211.149834, + "e2e_p50_ms": 31255.166458, + "e2e_p95_ms": 31861.110417, + "e2e_p99_ms": 32211.15025, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.046875, + "cpu_pct": 0.010103837157204386, + "duration_secs": 32.542061834, + "setup_secs": null + }, + { + "flow": "consume_fifo", + "mode": "fifo", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 800, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "setup_bound", + "throughput_msg_per_sec": 23.90095161825594, + "dispatch_p50_ms": 31660.373959, + "dispatch_p95_ms": 32425.124584, + "dispatch_p99_ms": 32538.900584, + "e2e_p50_ms": 31660.37425, + "e2e_p95_ms": 32425.124792, + "e2e_p99_ms": 32538.90075, + "scaling_efficiency": 1.0, + "peak_rss_mb": 8.78125, + "cpu_pct": 0.0070627266401604724, + "duration_secs": 33.471470625, + "setup_secs": null + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 2491.2581833403906, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.0, + "duration_secs": 21.671780292, + "setup_secs": 0.011217375, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 30.437430416, + "producers": 8, + "unique_at_start": 10, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 20413, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 127.78171378415803, + "dispatch_p50_ms": 9692.771167, + "dispatch_p95_ms": 10728.827459, + "dispatch_p99_ms": 10903.162334, + "e2e_p50_ms": 9692.772375, + "e2e_p95_ms": 10728.828542, + "e2e_p99_ms": 10903.163709, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.875, + "cpu_pct": 0.011415582459776426, + "duration_secs": 10.95618425, + "setup_secs": 0.512921042, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 20413, + "published_at_window_end": 20413, + "processed_at_window_end": 1400, + "achieved_publish_msg_per_sec": 1863.1486596257269, + "lag_at_window_end": 19013, + "peak_lag": 19273, + "drain_secs": 8.394540166, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 2315.2161044270956, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.0, + "duration_secs": 23.319637375, + "setup_secs": 0.010083834, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 28.345713125, + "producers": 8, + "unique_at_start": 10, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 20218, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 129.17722110438794, + "dispatch_p50_ms": 10656.33, + "dispatch_p95_ms": 11559.621875, + "dispatch_p99_ms": 11721.278292, + "e2e_p50_ms": 10656.331042, + "e2e_p95_ms": 11559.622625, + "e2e_p99_ms": 11721.280375, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.328125, + "cpu_pct": 0.015079603329437875, + "duration_secs": 10.682998041, + "setup_secs": 0.511343875, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 20218, + "published_at_window_end": 20218, + "processed_at_window_end": 1380, + "achieved_publish_msg_per_sec": 1892.539895861243, + "lag_at_window_end": 18838, + "peak_lag": 19108, + "drain_secs": 8.733931959, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 1162.8070179365022, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.0009309514142563424, + "duration_secs": 38.034686167, + "setup_secs": 0.015784, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 127.244715375, + "producers": 8, + "unique_at_start": 10, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 5911, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 96.92793545758404, + "dispatch_p50_ms": 12400.518667, + "dispatch_p95_ms": 15050.910792, + "dispatch_p99_ms": 16410.203667, + "e2e_p50_ms": 12400.5195, + "e2e_p95_ms": 15050.911125, + "e2e_p99_ms": 16410.203917, + "scaling_efficiency": 1.0, + "peak_rss_mb": 280.65625, + "cpu_pct": 0.010901242051508022, + "duration_secs": 14.753228708, + "setup_secs": 0.518435125, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 5911, + "published_at_window_end": 5911, + "processed_at_window_end": 1430, + "achieved_publish_msg_per_sec": 400.6580604823631, + "lag_at_window_end": 4481, + "peak_lag": 5351, + "drain_secs": 4.8683545, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 2099.5877901613553, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0, + "duration_secs": 25.714571333, + "setup_secs": 0.008590917, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 29.272717666, + "producers": 8, + "unique_at_start": 10, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 20147, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 91.24935962368842, + "dispatch_p50_ms": 12525.475, + "dispatch_p95_ms": 13836.752208, + "dispatch_p99_ms": 14147.59475, + "e2e_p50_ms": 12525.475083, + "e2e_p95_ms": 13836.75225, + "e2e_p99_ms": 14147.594833, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.15625, + "cpu_pct": 0.009494271678068326, + "duration_secs": 11.715150708, + "setup_secs": 0.511569, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 20147, + "published_at_window_end": 20147, + "processed_at_window_end": 1069, + "achieved_publish_msg_per_sec": 1719.7388665467265, + "lag_at_window_end": 19078, + "peak_lag": 19297, + "drain_secs": 9.613429292, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 1948.5333449459135, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0, + "duration_secs": 27.708019542, + "setup_secs": 0.008458959, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 29.029272375, + "producers": 8, + "unique_at_start": 10, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 21921, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 93.8844959583458, + "dispatch_p50_ms": 14383.948292, + "dispatch_p95_ms": 15671.662666, + "dispatch_p99_ms": 16009.37725, + "e2e_p50_ms": 14383.948334, + "e2e_p95_ms": 15671.66275, + "e2e_p99_ms": 16009.377291, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.15625, + "cpu_pct": 0.010590288121645757, + "duration_secs": 13.633774, + "setup_secs": 0.509897917, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 21921, + "published_at_window_end": 21921, + "processed_at_window_end": 1280, + "achieved_publish_msg_per_sec": 1607.845340549139, + "lag_at_window_end": 20641, + "peak_lag": 20871, + "drain_secs": 10.520412375, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 1009.1712240685617, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0007029474855270452, + "duration_secs": 43.825070459, + "setup_secs": 0.017121584, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 119.642741125, + "producers": 8, + "unique_at_start": 10, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 5692, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 83.0613906748168, + "dispatch_p50_ms": 12836.0035, + "dispatch_p95_ms": 15418.879584, + "dispatch_p99_ms": 16418.706375, + "e2e_p50_ms": 12836.003875, + "e2e_p95_ms": 15418.879709, + "e2e_p99_ms": 16418.706417, + "scaling_efficiency": 1.0, + "peak_rss_mb": 57.71875, + "cpu_pct": 0.011669207842028143, + "duration_secs": 14.447145542, + "setup_secs": 0.510944125, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 5692, + "published_at_window_end": 5692, + "processed_at_window_end": 1200, + "achieved_publish_msg_per_sec": 393.98786310088104, + "lag_at_window_end": 4492, + "peak_lag": 5252, + "drain_secs": 4.877179625, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "dlq_drain", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "setup_bound", + "throughput_msg_per_sec": 575.3645830044466, + "dispatch_p50_ms": 4377.702667, + "dispatch_p95_ms": 8234.511, + "dispatch_p99_ms": 8597.324625, + "e2e_p50_ms": 4377.70275, + "e2e_p95_ms": 8234.511417, + "e2e_p99_ms": 8597.32475, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.0, + "duration_secs": 8.690142125, + "setup_secs": null + }, + { + "flow": "dlq_drain", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "setup_bound", + "throughput_msg_per_sec": 544.101925340911, + "dispatch_p50_ms": 4684.121792, + "dispatch_p95_ms": 8831.756458, + "dispatch_p99_ms": 9110.10125, + "e2e_p50_ms": 4684.122083, + "e2e_p95_ms": 8831.756792, + "e2e_p99_ms": 9110.101417, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0, + "duration_secs": 9.189454709, + "setup_secs": null + }, + { + "flow": "dlq_drain", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 5000, + "consumers": 1, + "handler": "zero (no-op)", + "handler_cost": "setup_bound", + "throughput_msg_per_sec": 398.1169096608007, + "dispatch_p50_ms": 6199.544542, + "dispatch_p95_ms": 11900.020333, + "dispatch_p99_ms": 12417.228458, + "e2e_p50_ms": 6199.545083, + "e2e_p95_ms": 11900.020625, + "e2e_p99_ms": 12417.2285, + "scaling_efficiency": 1.0, + "peak_rss_mb": 0.0, + "cpu_pct": 0.8591184168030775, + "duration_secs": 12.559124917, + "setup_secs": null + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 2982.7184673118127, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.1972739265877494, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.0012373460499895852, + "duration_secs": 18.097584667, + "setup_secs": 0.013635583, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 29.911947375, + "producers": 8, + "unique_at_start": 20, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 23588, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 298.5815969876513, + "dispatch_p50_ms": 8392.946833, + "dispatch_p95_ms": 9921.409375, + "dispatch_p99_ms": 10222.237292, + "e2e_p50_ms": 8392.9475, + "e2e_p95_ms": 9921.410167, + "e2e_p99_ms": 10222.238334, + "scaling_efficiency": 2.336653564468538, + "peak_rss_mb": 0.046875, + "cpu_pct": 0.011281275616380647, + "duration_secs": 10.502991583, + "setup_secs": 0.517874042, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 23588, + "published_at_window_end": 23588, + "processed_at_window_end": 3136, + "achieved_publish_msg_per_sec": 2245.8363232604333, + "lag_at_window_end": 20452, + "peak_lag": 20962, + "drain_secs": 6.994974042, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 2818.8799585947872, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.2175450720149186, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0014473567126302685, + "duration_secs": 19.149449708, + "setup_secs": 0.013757708, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 30.206704875, + "producers": 8, + "unique_at_start": 20, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 23957, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 295.0686827598515, + "dispatch_p50_ms": 8936.151375, + "dispatch_p95_ms": 10382.555208, + "dispatch_p99_ms": 10678.396791, + "e2e_p50_ms": 8936.152042, + "e2e_p95_ms": 10382.556958, + "e2e_p99_ms": 10678.397625, + "scaling_efficiency": 2.284216057886916, + "peak_rss_mb": 0.234375, + "cpu_pct": 0.013000615484851385, + "duration_secs": 11.048275166, + "setup_secs": 0.763985583, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 23957, + "published_at_window_end": 23957, + "processed_at_window_end": 3260, + "achieved_publish_msg_per_sec": 2168.3927708213996, + "lag_at_window_end": 20697, + "peak_lag": 21227, + "drain_secs": 6.927755125, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 1419.7047679148134, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.2209289641493544, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.0017841167204644542, + "duration_secs": 31.145207792, + "setup_secs": 0.024892416, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 124.946429208, + "producers": 8, + "unique_at_start": 20, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 5608, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 190.36541264420023, + "dispatch_p50_ms": 10054.402708, + "dispatch_p95_ms": 14548.644625, + "dispatch_p99_ms": 15116.33225, + "e2e_p50_ms": 10054.404417, + "e2e_p95_ms": 14548.6455, + "e2e_p99_ms": 15116.333, + "scaling_efficiency": 1.9639891404424343, + "peak_rss_mb": 10.109375, + "cpu_pct": 0.015314126921191882, + "duration_secs": 15.391451416, + "setup_secs": 0.516641458, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 5608, + "published_at_window_end": 5608, + "processed_at_window_end": 2930, + "achieved_publish_msg_per_sec": 364.35810037838735, + "lag_at_window_end": 2678, + "peak_lag": 4398, + "drain_secs": 2.258867917, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 2569.356374184396, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.223743244376049, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0017977810759773317, + "duration_secs": 21.009152542, + "setup_secs": 0.012788125, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 31.521217417, + "producers": 8, + "unique_at_start": 20, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 24600, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 239.39281581299738, + "dispatch_p50_ms": 9800.782917, + "dispatch_p95_ms": 10766.92575, + "dispatch_p99_ms": 10983.327667, + "e2e_p50_ms": 9800.782958, + "e2e_p95_ms": 10766.925792, + "e2e_p99_ms": 10983.327792, + "scaling_efficiency": 2.623501324286015, + "peak_rss_mb": 0.09375, + "cpu_pct": 0.016429798735667786, + "duration_secs": 11.069672208, + "setup_secs": 0.512203, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 24600, + "published_at_window_end": 24600, + "processed_at_window_end": 2650, + "achieved_publish_msg_per_sec": 2222.288026037636, + "lag_at_window_end": 21950, + "peak_lag": 22370, + "drain_secs": 8.516635875, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 2571.0166599265203, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.3194624903880643, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.0012035433794753106, + "duration_secs": 20.995585459, + "setup_secs": 0.009664584, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 34.263526875, + "producers": 8, + "unique_at_start": 20, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 23632, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 244.45894022198675, + "dispatch_p50_ms": 9673.048125, + "dispatch_p95_ms": 10680.694834, + "dispatch_p99_ms": 10958.648959, + "e2e_p50_ms": 9673.048292, + "e2e_p95_ms": 10680.695042, + "e2e_p99_ms": 10958.649, + "scaling_efficiency": 2.6038265181766227, + "peak_rss_mb": 0.109375, + "cpu_pct": 0.014147309532051816, + "duration_secs": 10.92617025, + "setup_secs": 0.511361375, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 23632, + "published_at_window_end": 23632, + "processed_at_window_end": 2671, + "achieved_publish_msg_per_sec": 2162.8804475200263, + "lag_at_window_end": 20961, + "peak_lag": 21391, + "drain_secs": 8.059748875, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 1346.5603560933143, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.3343229810542347, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0015292586413090611, + "duration_secs": 32.836998208, + "setup_secs": 0.021791833, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 127.344701208, + "producers": 8, + "unique_at_start": 20, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 6081, + "consumers": 2, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 150.34721980339978, + "dispatch_p50_ms": 11497.883708, + "dispatch_p95_ms": 15493.140458, + "dispatch_p99_ms": 16605.495042, + "e2e_p50_ms": 11497.88375, + "e2e_p95_ms": 15493.140542, + "e2e_p99_ms": 16605.49525, + "scaling_efficiency": 1.810073471945651, + "peak_rss_mb": 1.296875, + "cpu_pct": 0.01026542418879428, + "duration_secs": 16.561663084, + "setup_secs": 0.524309833, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 6081, + "published_at_window_end": 6081, + "processed_at_window_end": 2490, + "achieved_publish_msg_per_sec": 367.17327053191735, + "lag_at_window_end": 3591, + "peak_lag": 5151, + "drain_secs": 2.76532625, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 3187.4238526117724, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.2794434049135492, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.004462492351363526, + "duration_secs": 16.929031875, + "setup_secs": 0.01934425, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 25.352446291, + "producers": 8, + "unique_at_start": 40, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 22587, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 265.7573336566714, + "dispatch_p50_ms": 19204.243166, + "dispatch_p95_ms": 20992.452917, + "dispatch_p99_ms": 21381.895125, + "e2e_p50_ms": 19204.244208, + "e2e_p95_ms": 20992.4545, + "e2e_p99_ms": 21381.895708, + "scaling_efficiency": 2.079775938093727, + "peak_rss_mb": 0.15625, + "cpu_pct": 0.009896799438504975, + "duration_secs": 22.72750075, + "setup_secs": 0.528685583, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 22587, + "published_at_window_end": 22587, + "processed_at_window_end": 6040, + "achieved_publish_msg_per_sec": 993.8180290237148, + "lag_at_window_end": 16547, + "peak_lag": 17707, + "drain_secs": 5.604868, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 3081.3933697423704, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.33093120933731, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.0037721860465130454, + "duration_secs": 17.511558417, + "setup_secs": 0.021454875, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 26.254199042, + "producers": 8, + "unique_at_start": 40, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 22112, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 534.9633725121276, + "dispatch_p50_ms": 7442.257833, + "dispatch_p95_ms": 9603.474583, + "dispatch_p99_ms": 9990.280666, + "e2e_p50_ms": 7442.258541, + "e2e_p95_ms": 9603.476125, + "e2e_p99_ms": 9990.28175, + "scaling_efficiency": 4.141313522140443, + "peak_rss_mb": 0.078125, + "cpu_pct": 0.01897592714346428, + "duration_secs": 11.29796975, + "setup_secs": 0.77205625, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 22112, + "published_at_window_end": 22112, + "processed_at_window_end": 6044, + "achieved_publish_msg_per_sec": 1957.1657996340448, + "lag_at_window_end": 16068, + "peak_lag": 17158, + "drain_secs": 5.301972209, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 1638.5210489242907, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.4091083246401304, + "peak_rss_mb": 0.0625, + "cpu_pct": 0.005318383924753215, + "duration_secs": 26.973715125, + "setup_secs": 0.037970708, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 132.868751042, + "producers": 8, + "unique_at_start": 40, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 5648, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 338.4291116681347, + "dispatch_p50_ms": 5755.982292, + "dispatch_p95_ms": 10736.353292, + "dispatch_p99_ms": 11797.490125, + "e2e_p50_ms": 5755.982708, + "e2e_p95_ms": 10736.354917, + "e2e_p99_ms": 11797.491, + "scaling_efficiency": 3.49155390621347, + "peak_rss_mb": 1.875, + "cpu_pct": 0.01772753412718079, + "duration_secs": 16.588407458, + "setup_secs": 1.279909625, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 5648, + "published_at_window_end": 5648, + "processed_at_window_end": 5614, + "achieved_publish_msg_per_sec": 340.47873578582556, + "lag_at_window_end": 34, + "peak_lag": 3244, + "drain_secs": 0.441549042, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 2786.62440336036, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.3272245230318305, + "peak_rss_mb": 0.046875, + "cpu_pct": 0.002777067057679312, + "duration_secs": 19.363930042, + "setup_secs": 0.022359542, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 25.201170292, + "producers": 8, + "unique_at_start": 40, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 23355, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 450.31349102066264, + "dispatch_p50_ms": 8638.354625, + "dispatch_p95_ms": 10074.287875, + "dispatch_p99_ms": 10369.777584, + "e2e_p50_ms": 8638.354709, + "e2e_p95_ms": 10074.288083, + "e2e_p99_ms": 10369.777625, + "scaling_efficiency": 4.934977000142814, + "peak_rss_mb": 0.140625, + "cpu_pct": 0.01623179517960003, + "duration_secs": 11.412049833, + "setup_secs": 1.025795667, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 23355, + "published_at_window_end": 23355, + "processed_at_window_end": 5139, + "achieved_publish_msg_per_sec": 2046.5210318714878, + "lag_at_window_end": 18216, + "peak_lag": 19086, + "drain_secs": 6.601874333, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 2707.7079113281357, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.3896133306373029, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.003360475119828949, + "duration_secs": 19.92829425, + "setup_secs": 0.024625542, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 39.308799541, + "producers": 8, + "unique_at_start": 40, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 21600, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 432.0337954489803, + "dispatch_p50_ms": 8303.355208, + "dispatch_p95_ms": 9925.724708, + "dispatch_p99_ms": 10263.840333, + "e2e_p50_ms": 8303.35525, + "e2e_p95_ms": 9925.724791, + "e2e_p99_ms": 10263.840458, + "scaling_efficiency": 4.60175869337002, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.013747808306581195, + "duration_secs": 11.133388292, + "setup_secs": 0.53242375, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 21600, + "published_at_window_end": 21600, + "processed_at_window_end": 4810, + "achieved_publish_msg_per_sec": 1940.1101833051923, + "lag_at_window_end": 16790, + "peak_lag": 17700, + "drain_secs": 6.222083666, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 1591.4764075796038, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.5770132655620401, + "peak_rss_mb": 0.0, + "cpu_pct": 0.0029755177179534137, + "duration_secs": 27.771068292, + "setup_secs": 0.048528208, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 119.319650792, + "producers": 8, + "unique_at_start": 40, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 6196, + "consumers": 4, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 317.66790216376813, + "dispatch_p50_ms": 7139.751125, + "dispatch_p95_ms": 12550.745541, + "dispatch_p99_ms": 13618.906708, + "e2e_p50_ms": 7139.75125, + "e2e_p95_ms": 12550.745666, + "e2e_p99_ms": 13618.906916, + "scaling_efficiency": 3.8244953471514793, + "peak_rss_mb": 1.671875, + "cpu_pct": 0.013736983930543773, + "duration_secs": 18.069184708, + "setup_secs": 0.526596666, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 6196, + "published_at_window_end": 6196, + "processed_at_window_end": 5740, + "achieved_publish_msg_per_sec": 342.9042372485553, + "lag_at_window_end": 456, + "peak_lag": 3596, + "drain_secs": 0.253554708, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 3237.9236869934803, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.299714220166425, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.009712519956827904, + "duration_secs": 16.64338175, + "setup_secs": 0.056714792, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 42.822539958, + "producers": 8, + "unique_at_start": 110, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 64, + "tier": "moderate", + "messages": 19614, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 925.5995083541906, + "dispatch_p50_ms": 4906.325083, + "dispatch_p95_ms": 6880.957, + "dispatch_p99_ms": 7419.77875, + "e2e_p50_ms": 4906.326166, + "e2e_p95_ms": 6880.957541, + "e2e_p99_ms": 7419.779375, + "scaling_efficiency": 7.243599110884232, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.019383986920982338, + "duration_secs": 11.246764833, + "setup_secs": 0.551918458, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 19614, + "published_at_window_end": 19614, + "processed_at_window_end": 10410, + "achieved_publish_msg_per_sec": 1743.968180293861, + "lag_at_window_end": 9204, + "peak_lag": 11284, + "drain_secs": 3.033010583, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 3329.9368259993485, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.438283372179353, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.009300719483539638, + "duration_secs": 16.177484083, + "setup_secs": 0.047113792, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 24.905171916, + "producers": 8, + "unique_at_start": 130, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 19056, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 907.0389318449008, + "dispatch_p50_ms": 4877.545167, + "dispatch_p95_ms": 7143.276292, + "dispatch_p99_ms": 7558.449959, + "e2e_p50_ms": 4877.545917, + "e2e_p95_ms": 7143.277208, + "e2e_p99_ms": 7558.451125, + "scaling_efficiency": 7.021663139137541, + "peak_rss_mb": 0.109375, + "cpu_pct": 0.01975684234464053, + "duration_secs": 11.293892291, + "setup_secs": 0.547771583, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 19056, + "published_at_window_end": 19056, + "processed_at_window_end": 10244, + "achieved_publish_msg_per_sec": 1687.2836670476795, + "lag_at_window_end": 8812, + "peak_lag": 10902, + "drain_secs": 2.848022459, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 1851.9462978681267, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.59265146262581, + "peak_rss_mb": 0.046875, + "cpu_pct": 0.010584839760467514, + "duration_secs": 23.843563958, + "setup_secs": 0.0757715, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 137.344449917, + "producers": 8, + "unique_at_start": 80, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "consume_batch", + "mode": "batch", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 4767, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "max_batch_size": 10, + "max_batch_age_ms": 250, + "throughput_msg_per_sec": 322.0683052175525, + "dispatch_p50_ms": 5118.961875, + "dispatch_p95_ms": 10781.917708, + "dispatch_p99_ms": 11804.954916, + "e2e_p50_ms": 5118.9635, + "e2e_p95_ms": 10781.918666, + "e2e_p99_ms": 11804.956416, + "scaling_efficiency": 3.322760396134617, + "peak_rss_mb": 0.15625, + "cpu_pct": 0.021317126044277674, + "duration_secs": 14.68011575, + "setup_secs": 1.557869, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 4767, + "published_at_window_end": 4767, + "processed_at_window_end": 4728, + "achieved_publish_msg_per_sec": 324.72496001947394, + "lag_at_window_end": 39, + "peak_lag": 3007, + "drain_secs": 0.336131042, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 60000, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 2822.2434158348415, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.3441892875638934, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.006498385787845766, + "duration_secs": 19.080565375, + "setup_secs": 0.066568125, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 26.13182475, + "producers": 8, + "unique_at_start": 150, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 64, + "tier": "moderate", + "messages": 20648, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 811.3984088080505, + "dispatch_p50_ms": 6260.98425, + "dispatch_p95_ms": 7911.444458, + "dispatch_p99_ms": 8267.755917, + "e2e_p50_ms": 6260.984417, + "e2e_p95_ms": 7911.444583, + "e2e_p99_ms": 8267.755959, + "scaling_efficiency": 8.892099759979144, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.02450053303081392, + "duration_secs": 11.4444395, + "setup_secs": 0.537413, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 20648, + "published_at_window_end": 20648, + "processed_at_window_end": 9286, + "achieved_publish_msg_per_sec": 1804.1949542395676, + "lag_at_window_end": 11362, + "peak_lag": 13162, + "drain_secs": 4.187839209, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 60000, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 1340.3286487961716, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 0.6878653897674899, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.006472875049361823, + "duration_secs": 40.191635125, + "setup_secs": 0.053285792, + "method": "drain", + "drain": { + "corpus": 60000, + "published": 60000, + "fill_secs": 25.733693875, + "producers": 8, + "unique_at_start": 130, + "unique_at_end": 54000, + "unique_final": 60000, + "deliveries": 60000, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 1024, + "tier": "moderate", + "messages": 18810, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 769.7178230788728, + "dispatch_p50_ms": 5852.250042, + "dispatch_p95_ms": 7798.174125, + "dispatch_p99_ms": 8199.2385, + "e2e_p50_ms": 5852.250084, + "e2e_p95_ms": 7798.174166, + "e2e_p99_ms": 8199.238542, + "scaling_efficiency": 8.198561596585419, + "peak_rss_mb": 0.03125, + "cpu_pct": 0.02274168721867111, + "duration_secs": 11.544490375, + "setup_secs": 1.03545975, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 18810, + "published_at_window_end": 18810, + "processed_at_window_end": 8886, + "achieved_publish_msg_per_sec": 1629.3486666794504, + "lag_at_window_end": 9924, + "peak_lag": 11754, + "drain_secs": 3.757779292, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 49152, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 1614.6936918550182, + "dispatch_p50_ms": 0.0, + "dispatch_p95_ms": 0.0, + "dispatch_p99_ms": 0.0, + "e2e_p50_ms": 0.0, + "e2e_p95_ms": 0.0, + "e2e_p99_ms": 0.0, + "scaling_efficiency": 1.6000195540111022, + "peak_rss_mb": 0.125, + "cpu_pct": 0.008552209076492368, + "duration_secs": 27.346982417, + "setup_secs": 0.076882209, + "method": "drain", + "drain": { + "corpus": 49152, + "published": 49152, + "fill_secs": 125.15148625, + "producers": 8, + "unique_at_start": 80, + "unique_at_end": 44237, + "unique_final": 49152, + "deliveries": 49152, + "duplicates": 0 + } + }, + { + "flow": "supervisor", + "mode": "parallel", + "payload_bytes": 65536, + "tier": "moderate", + "messages": 4355, + "consumers": 8, + "handler": "zero (no-op)", + "handler_cost": "framework", + "throughput_msg_per_sec": 318.18144100189323, + "dispatch_p50_ms": 5433.393375, + "dispatch_p95_ms": 11174.629416, + "dispatch_p99_ms": 11779.855375, + "e2e_p50_ms": 5433.3935, + "e2e_p95_ms": 11174.6295, + "e2e_p99_ms": 11779.855458, + "scaling_efficiency": 3.8306779890980316, + "peak_rss_mb": 0.015625, + "cpu_pct": 0.028809751628010263, + "duration_secs": 13.668301917, + "setup_secs": 1.036279916, + "load": { + "offered_msg_per_sec": 5000, + "window_secs": 10, + "producers": 8, + "published": 4355, + "published_at_window_end": 4355, + "processed_at_window_end": 4349, + "achieved_publish_msg_per_sec": 318.620412868072, + "lag_at_window_end": 6, + "peak_lag": 3531, + "drain_secs": 0.011062083, + "producer_bound": false, + "sustained": false + }, + "method": "offered_load" + } + ], + "failures": [], + "unsupported": [ + { + "flow": "consumer_group", + "reason": "SQS does not implement HasCoordinatedGroups — it has no broker-side group coordinator, so shove uses ConsumerSupervisor (N independent pollers) instead" + }, + { + "flow": "broadcast", + "reason": "SQS does not implement HasBroadcast — per-process fan-out needs a real queue plus an SNS subscription whose lifecycle shove does not manage, and a leaked queue costs money forever" + } + ] } ] } diff --git a/docs/pages/ops/performance.mdx b/docs/pages/ops/performance.mdx index 59107e75..f28b023b 100644 --- a/docs/pages/ops/performance.mdx +++ b/docs/pages/ops/performance.mdx @@ -10,12 +10,13 @@ an explicit "not supported" marker, never as a zero. And a backend the results document does not contain is never plotted, so absence from a chart means "not measured here", not "slow". -The document these charts render from measures the **in-process**, **Kafka**, -**NATS**, **RabbitMQ** and **Redis** backends. SQS is the one backend absent: it -runs only against LocalStack (non-representative by design), needs a LocalStack -Pro auth token, and is not in this document. The harness refuses to merge runs -measured on different hosts into one document, so completing the set means -measuring SQS on the same host as the published runs. +The document these charts render from measures all six backends: **in-process**, +**Kafka**, **NATS**, **RabbitMQ**, **Redis** and **SQS**. The SQS rows measure +LocalStack, which is non-representative of the AWS service by design, and run a +smaller corpus than the other backends; the rows and the charts record both +(see the benchmark runbook's "The SQS corpus deviation"). Every run was measured +on the same host: the harness refuses to merge runs from different hosts into +one document. ### Throughput vs consumer count diff --git a/docs/public/bench/dispatch-latency-1kib-dark.svg b/docs/public/bench/dispatch-latency-1kib-dark.svg index b19457dc..9ece4971 100644 --- a/docs/public/bench/dispatch-latency-1kib-dark.svg +++ b/docs/public/bench/dispatch-latency-1kib-dark.svg @@ -7,20 +7,23 @@ Dispatch latency percentiles consume_parallel — 1 KiB payload — sustained offered load — lower is better - + dispatch = publish → handler entry, measured while a paced producer held the offered rate and the consumers kept up (a - + sustained offered-load rung); the backlog a corpus-mode row would add is absent by construction - + highest sustained rung per backend: inmemory at 100k msg/s, 1 consumer; kafka at 100k msg/s, 1 consumer; nats at 25k - + msg/s, 1 consumer; rabbitmq at 5.0k msg/s, 1 consumer; redis at 5.0k msg/s, 1 consumer + +no bar for sqs: no rung sustained + -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -28,186 +31,186 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + milliseconds - - - - - - - - - - - - - + + + + + + + + + + + + + 0.0 - - + + 5.0 - - + + 10 - - + + 15 - - + + 20 - - + + 25 - - + + 30 - - + + 35 - - + + 40 - - + + 45 - - + + 50 - - - - - - + + + + + + 0.9 - - - - - + + + + + 3.9 - - - - - + + + + + 20 - - - - - + + + + + 8.0 - - - - - + + + + + 13 - - - - - + + + + + 21 - - - - - + + + + + 0.8 - - - - - + + + + + 5.5 - - - - - + + + + + 40 - - - - - + + + + + 1.3 - - - - - + + + + + 15 - - - - - + + + + + 47 - - - - - + + + + + 1.1 - - - - - + + + + + 2.2 - - - - - + + + + + 3.1 - + inmemory - + kafka - + nats - + rabbitmq - + redis - + backend diff --git a/docs/public/bench/dispatch-latency-1kib.svg b/docs/public/bench/dispatch-latency-1kib.svg index b9a9fa2c..d2836519 100644 --- a/docs/public/bench/dispatch-latency-1kib.svg +++ b/docs/public/bench/dispatch-latency-1kib.svg @@ -7,20 +7,23 @@ Dispatch latency percentiles consume_parallel — 1 KiB payload — sustained offered load — lower is better - + dispatch = publish → handler entry, measured while a paced producer held the offered rate and the consumers kept up (a - + sustained offered-load rung); the backlog a corpus-mode row would add is absent by construction - + highest sustained rung per backend: inmemory at 100k msg/s, 1 consumer; kafka at 100k msg/s, 1 consumer; nats at 25k - + msg/s, 1 consumer; rabbitmq at 5.0k msg/s, 1 consumer; redis at 5.0k msg/s, 1 consumer + +no bar for sqs: no rung sustained + -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -28,186 +31,186 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + milliseconds - - - - - - - - - - - - - + + + + + + + + + + + + + 0.0 - - + + 5.0 - - + + 10 - - + + 15 - - + + 20 - - + + 25 - - + + 30 - - + + 35 - - + + 40 - - + + 45 - - + + 50 - - - - - - + + + + + + 0.9 - - - - - + + + + + 3.9 - - - - - + + + + + 20 - - - - - + + + + + 8.0 - - - - - + + + + + 13 - - - - - + + + + + 21 - - - - - + + + + + 0.8 - - - - - + + + + + 5.5 - - - - - + + + + + 40 - - - - - + + + + + 1.3 - - - - - + + + + + 15 - - - - - + + + + + 47 - - - - - + + + + + 1.1 - - - - - + + + + + 2.2 - - - - - + + + + + 3.1 - + inmemory - + kafka - + nats - + rabbitmq - + redis - + backend diff --git a/docs/public/bench/dispatch-latency-64kib-dark.svg b/docs/public/bench/dispatch-latency-64kib-dark.svg index 2d8a5bc6..0f0e72a9 100644 --- a/docs/public/bench/dispatch-latency-64kib-dark.svg +++ b/docs/public/bench/dispatch-latency-64kib-dark.svg @@ -7,20 +7,23 @@ Dispatch latency percentiles consume_parallel — 64 KiB payload — sustained offered load — lower is better - + dispatch = publish → handler entry, measured while a paced producer held the offered rate and the consumers kept up (a - + sustained offered-load rung); the backlog a corpus-mode row would add is absent by construction - + highest sustained rung per backend: inmemory at 25k msg/s, 1 consumer; kafka at 5.0k msg/s, 1 consumer; nats at 5.0k - + msg/s, 1 consumer; rabbitmq at 5.0k msg/s, 2 consumers; redis at 5.0k msg/s, 1 consumer + +no bar for sqs: no rung sustained + -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -28,171 +31,171 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + milliseconds - - - - - - - - - - + + + + + + + + + + 0.0 - - + + 10 - - + + 20 - - + + 30 - - + + 40 - - + + 50 - - + + 60 - - + + 70 - - - - - - + + + + + + 0.5 - - - - - + + + + + 13 - - - - - + + + + + 45 - - - - - + + + + + 8.3 - - - - - + + + + + 12 - - - - - + + + + + 18 - - - - - + + + + + 4.2 - - - - - + + + + + 35 - - - - - + + + + + 57 - - - - - + + + + + 2.1 - - - - - + + + + + 5.9 - - - - - + + + + + 67 - - - - - + + + + + 1.4 - - - - - + + + + + 2.6 - - - - - + + + + + 3.4 - + inmemory - + kafka - + nats - + rabbitmq - + redis - + backend diff --git a/docs/public/bench/dispatch-latency-64kib.svg b/docs/public/bench/dispatch-latency-64kib.svg index 5f247ee0..4ea53e94 100644 --- a/docs/public/bench/dispatch-latency-64kib.svg +++ b/docs/public/bench/dispatch-latency-64kib.svg @@ -7,20 +7,23 @@ Dispatch latency percentiles consume_parallel — 64 KiB payload — sustained offered load — lower is better - + dispatch = publish → handler entry, measured while a paced producer held the offered rate and the consumers kept up (a - + sustained offered-load rung); the backlog a corpus-mode row would add is absent by construction - + highest sustained rung per backend: inmemory at 25k msg/s, 1 consumer; kafka at 5.0k msg/s, 1 consumer; nats at 5.0k - + msg/s, 1 consumer; rabbitmq at 5.0k msg/s, 2 consumers; redis at 5.0k msg/s, 1 consumer + +no bar for sqs: no rung sustained + -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -28,171 +31,171 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + milliseconds - - - - - - - - - - + + + + + + + + + + 0.0 - - + + 10 - - + + 20 - - + + 30 - - + + 40 - - + + 50 - - + + 60 - - + + 70 - - - - - - + + + + + + 0.5 - - - - - + + + + + 13 - - - - - + + + + + 45 - - - - - + + + + + 8.3 - - - - - + + + + + 12 - - - - - + + + + + 18 - - - - - + + + + + 4.2 - - - - - + + + + + 35 - - - - - + + + + + 57 - - - - - + + + + + 2.1 - - - - - + + + + + 5.9 - - - - - + + + + + 67 - - - - - + + + + + 1.4 - - - - - + + + + + 2.6 - - - - - + + + + + 3.4 - + inmemory - + kafka - + nats - + rabbitmq - + redis - + backend diff --git a/docs/public/bench/dispatch-latency-dark.svg b/docs/public/bench/dispatch-latency-dark.svg index ff630dfd..9707d226 100644 --- a/docs/public/bench/dispatch-latency-dark.svg +++ b/docs/public/bench/dispatch-latency-dark.svg @@ -7,20 +7,23 @@ Dispatch latency percentiles consume_parallel — 64 B payload — sustained offered load — lower is better - + dispatch = publish → handler entry, measured while a paced producer held the offered rate and the consumers kept up (a - + sustained offered-load rung); the backlog a corpus-mode row would add is absent by construction - + highest sustained rung per backend: inmemory at 100k msg/s, 1 consumer; kafka at 100k msg/s, 1 consumer; nats at 25k - + msg/s, 1 consumer; rabbitmq at 25k msg/s, 1 consumer; redis at 5.0k msg/s, 1 consumer + +no bar for sqs: no rung sustained + -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -28,161 +31,161 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + milliseconds - - - - - + + + + + - - + + 0.0 - - + + 5.0 - - + + 10 - - + + 15 - - + + 20 - + 25 - - - - - + + + + + 0.9 - - - - - + + + + + 3.5 - - - - - + + + + + 7.1 - - - - - + + + + + 8.2 - - - - - + + + + + 13 - - - - - + + + + + 22 - - - - - + + + + + 0.7 - - - - - + + + + + 1.4 - - - - - + + + + + 6.4 - - - - - + + + + + 2.7 - - - - - + + + + + 6.5 - - - - - + + + + + 14 - - - - - + + + + + 1.1 - - - - - + + + + + 2.3 - - - - - + + + + + 4.3 - + inmemory - + kafka - + nats - + rabbitmq - + redis - + backend diff --git a/docs/public/bench/dispatch-latency.svg b/docs/public/bench/dispatch-latency.svg index 5d94fe6b..2138ae8e 100644 --- a/docs/public/bench/dispatch-latency.svg +++ b/docs/public/bench/dispatch-latency.svg @@ -7,20 +7,23 @@ Dispatch latency percentiles consume_parallel — 64 B payload — sustained offered load — lower is better - + dispatch = publish → handler entry, measured while a paced producer held the offered rate and the consumers kept up (a - + sustained offered-load rung); the backlog a corpus-mode row would add is absent by construction - + highest sustained rung per backend: inmemory at 100k msg/s, 1 consumer; kafka at 100k msg/s, 1 consumer; nats at 25k - + msg/s, 1 consumer; rabbitmq at 25k msg/s, 1 consumer; redis at 5.0k msg/s, 1 consumer + +no bar for sqs: no rung sustained + -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -28,161 +31,161 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + milliseconds - - - - - + + + + + - - + + 0.0 - - + + 5.0 - - + + 10 - - + + 15 - - + + 20 - + 25 - - - - - + + + + + 0.9 - - - - - + + + + + 3.5 - - - - - + + + + + 7.1 - - - - - + + + + + 8.2 - - - - - + + + + + 13 - - - - - + + + + + 22 - - - - - + + + + + 0.7 - - - - - + + + + + 1.4 - - - - - + + + + + 6.4 - - - - - + + + + + 2.7 - - - - - + + + + + 6.5 - - - - - + + + + + 14 - - - - - + + + + + 1.1 - - - - - + + + + + 2.3 - - - - - + + + + + 4.3 - + inmemory - + kafka - + nats - + rabbitmq - + redis - + backend diff --git a/docs/public/bench/framework-overhead-dark.svg b/docs/public/bench/framework-overhead-dark.svg index 00e1286c..9bb3ec9d 100644 --- a/docs/public/bench/framework-overhead-dark.svg +++ b/docs/public/bench/framework-overhead-dark.svg @@ -35,7 +35,7 @@ from the moment every worker was assigned until 90 % of a corpus of 6.0M message been consumed; no producer ran in the window -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) diff --git a/docs/public/bench/framework-overhead.svg b/docs/public/bench/framework-overhead.svg index c5e79bfc..c58469a1 100644 --- a/docs/public/bench/framework-overhead.svg +++ b/docs/public/bench/framework-overhead.svg @@ -35,7 +35,7 @@ from the moment every worker was assigned until 90 % of a corpus of 6.0M message been consumed; no producer ran in the window -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) diff --git a/docs/public/bench/parallel-vs-sequenced-1kib-dark.svg b/docs/public/bench/parallel-vs-sequenced-1kib-dark.svg index 0c2bf86f..0ff34aa0 100644 --- a/docs/public/bench/parallel-vs-sequenced-1kib-dark.svg +++ b/docs/public/bench/parallel-vs-sequenced-1kib-dark.svg @@ -7,41 +7,59 @@ Parallel vs sequenced consume 1 KiB payload — least-parallel measurement per mode — what ordering costs — higher is better - + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + redis / sequenced (fifo): failed to run — absent, not zero; see failures[] in the results document - + kafka, nats, rabbitmq / sequenced (fifo): lower bound (muted bar) — this window cannot separate setup from drain, so - + the true rate is at least the bar shown + +sqs / sequenced (fifo): lower bound from a window mixing setup and drain; shape only, so its height is not the bound + + +nats, rabbitmq, sqs / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured + + +modes share) + + +inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound + + +sqs / batch: up to 10 messages or 250 ms per batch + -nats, rabbitmq / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured modes +inmemory, kafka, nats, rabbitmq, redis / batch: up to 500 messages or 250 ms per batch -share) +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_batch / consume_parallel point is the processing -inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound +rate of the consumers from the moment every worker was assigned until 90 % of its corpus, published before they -batch bars: up to 500 messages or 250 ms per batch +started, had been consumed; no producer ran in the window -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_batch / consume_parallel point is the processing rate of +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 3.1M; sqs 60k — a smaller corpus is a shorter -the consumers from the moment every worker was assigned until 90 % of a corpus of 3.1M messages, published before they +window, not a different measurement -started, had been consumed; no producer ran in the window +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) redis: 1 cell in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -49,156 +67,164 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second - - - - - - - - - - + + + + + + + + + + 0.0 - - + + 200k - - + + 400k - - + + 600k - - + + 800k - - + + 1.0M - - + + 1.2M - - + + 1.4M - - - - - - + + + + + + 637k - + n/s - - - - - + + + + + 1.3M - - - - - + + + + + 81k - + ≥1.6k - - - - - + + + + + 86k - - - - - + + + + + 53k - - - - - + + + + + ≥22k - - - - - + + + + + 45k - - - - - + + + + + 28k - - + + ≥10.0k - - - - - + + + + + 30k - - - - - + + 11k - + n/s - - - - - + + + + + 99k - + + + + + + + + + inmemory - + kafka - + nats - + rabbitmq - + redis - + +sqs + + backend diff --git a/docs/public/bench/parallel-vs-sequenced-1kib.svg b/docs/public/bench/parallel-vs-sequenced-1kib.svg index 5b267f87..eb74ff52 100644 --- a/docs/public/bench/parallel-vs-sequenced-1kib.svg +++ b/docs/public/bench/parallel-vs-sequenced-1kib.svg @@ -7,41 +7,59 @@ Parallel vs sequenced consume 1 KiB payload — least-parallel measurement per mode — what ordering costs — higher is better - + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + redis / sequenced (fifo): failed to run — absent, not zero; see failures[] in the results document - + kafka, nats, rabbitmq / sequenced (fifo): lower bound (muted bar) — this window cannot separate setup from drain, so - + the true rate is at least the bar shown + +sqs / sequenced (fifo): lower bound from a window mixing setup and drain; shape only, so its height is not the bound + + +nats, rabbitmq, sqs / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured + + +modes share) + + +inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound + + +sqs / batch: up to 10 messages or 250 ms per batch + -nats, rabbitmq / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured modes +inmemory, kafka, nats, rabbitmq, redis / batch: up to 500 messages or 250 ms per batch -share) +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_batch / consume_parallel point is the processing -inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound +rate of the consumers from the moment every worker was assigned until 90 % of its corpus, published before they -batch bars: up to 500 messages or 250 ms per batch +started, had been consumed; no producer ran in the window -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_batch / consume_parallel point is the processing rate of +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 3.1M; sqs 60k — a smaller corpus is a shorter -the consumers from the moment every worker was assigned until 90 % of a corpus of 3.1M messages, published before they +window, not a different measurement -started, had been consumed; no producer ran in the window +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) redis: 1 cell in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -49,156 +67,164 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second - - - - - - - - - - + + + + + + + + + + 0.0 - - + + 200k - - + + 400k - - + + 600k - - + + 800k - - + + 1.0M - - + + 1.2M - - + + 1.4M - - - - - - + + + + + + 637k - + n/s - - - - - + + + + + 1.3M - - - - - + + + + + 81k - + ≥1.6k - - - - - + + + + + 86k - - - - - + + + + + 53k - - - - - + + + + + ≥22k - - - - - + + + + + 45k - - - - - + + + + + 28k - - + + ≥10.0k - - - - - + + + + + 30k - - - - - + + 11k - + n/s - - - - - + + + + + 99k - + + + + + + + + + inmemory - + kafka - + nats - + rabbitmq - + redis - + +sqs + + backend diff --git a/docs/public/bench/parallel-vs-sequenced-64kib-dark.svg b/docs/public/bench/parallel-vs-sequenced-64kib-dark.svg index 8bd2dcea..e995e504 100644 --- a/docs/public/bench/parallel-vs-sequenced-64kib-dark.svg +++ b/docs/public/bench/parallel-vs-sequenced-64kib-dark.svg @@ -1,21 +1,27 @@ - - - + + + Parallel vs sequenced consume 64 KiB payload — least-parallel measurement per mode — what ordering costs — higher is better - + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + redis / sequenced (fifo): failed to run — absent, not zero; see failures[] in the results document - + inmemory, kafka, nats, rabbitmq / sequenced (fifo): lower bound (muted bar) — this window cannot separate setup from - + drain, so the true rate is at least the bar shown + +sqs / sequenced (fifo): lower bound from a window mixing setup and drain; shape only, so its height is not the bound + kafka / parallel: measured at 2 workers (no worker count is shared by the measured modes, so the bars are not directly @@ -29,204 +35,221 @@ rabbitmq / sequenced (fifo): measured at 8 workers (no worker count is shared by not directly comparable) -nats / parallel, batch; inmemory, nats / sequenced (fifo): measured at 8 workers (the least-parallel count the +nats, sqs / parallel, batch; inmemory, nats, sqs / sequenced (fifo): measured at 8 workers (the least-parallel count -measured modes share) +the measured modes share) inmemory / parallel, batch: measured, but every window under 1 s — too short to publish as a rate, even a lower bound -batch bars: up to 500 messages or 250 ms per batch +sqs / batch: up to 10 messages or 250 ms per batch -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_batch / consume_parallel point is the processing rate of +kafka, nats, rabbitmq, redis / batch: up to 500 messages or 250 ms per batch -the consumers from the moment every worker was assigned until 90 % of a corpus of 49k messages, published before they +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_batch / consume_parallel point is the processing -started, had been consumed; no producer ran in the window +rate of the consumers from the moment every worker was assigned until 90 % of a corpus of 49k messages, published -inmemory: 1 cell in this slice failed to run — absent, not zero; see failures[] +before they started, had been consumed; no producer ran in the window +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +inmemory: 1 cell in this slice failed to run — absent, not zero; see failures[] + + nats, rabbitmq, redis: 2 cells in this slice failed to run — absent, not zero; see failures[] - -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) + +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) - + Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) - + 12 recorded failure(s) in the dataset — see failures[] - + messages / second - - - - - - - - - - - - + + + + + + + + + + + + 0.0 - - + + 5.0k - - + + 10k - - + + 15k - - + + 20k - - + + 25k - - + + 30k - - + + 35k - - + + 40k - - + + 45k - - + + n/s - - - - - + + + + + ≥30k - + n/s - - - - - + + + + + 41k - - - - - + + + + + ≥1.5k - - - - - + + + + + 30k - - - - - + + + + + 7.6k - - - - - + + + + + ≥6.4k - - - - - + + + + + 8.6k - - - - - + + + + + 1.4k - - - - - + + + + + ≥3.2k - - - - - + + + + + 4.3k - - - - - + + + + + 7.1k - + n/s - - - - - + + + + + 17k - + + + + + + + + + inmemory - + kafka - + nats - + rabbitmq - + redis - + +sqs + + backend diff --git a/docs/public/bench/parallel-vs-sequenced-64kib.svg b/docs/public/bench/parallel-vs-sequenced-64kib.svg index 49ef0f55..085a7491 100644 --- a/docs/public/bench/parallel-vs-sequenced-64kib.svg +++ b/docs/public/bench/parallel-vs-sequenced-64kib.svg @@ -1,21 +1,27 @@ - - - + + + Parallel vs sequenced consume 64 KiB payload — least-parallel measurement per mode — what ordering costs — higher is better - + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + redis / sequenced (fifo): failed to run — absent, not zero; see failures[] in the results document - + inmemory, kafka, nats, rabbitmq / sequenced (fifo): lower bound (muted bar) — this window cannot separate setup from - + drain, so the true rate is at least the bar shown + +sqs / sequenced (fifo): lower bound from a window mixing setup and drain; shape only, so its height is not the bound + kafka / parallel: measured at 2 workers (no worker count is shared by the measured modes, so the bars are not directly @@ -29,204 +35,221 @@ rabbitmq / sequenced (fifo): measured at 8 workers (no worker count is shared by not directly comparable) -nats / parallel, batch; inmemory, nats / sequenced (fifo): measured at 8 workers (the least-parallel count the +nats, sqs / parallel, batch; inmemory, nats, sqs / sequenced (fifo): measured at 8 workers (the least-parallel count -measured modes share) +the measured modes share) inmemory / parallel, batch: measured, but every window under 1 s — too short to publish as a rate, even a lower bound -batch bars: up to 500 messages or 250 ms per batch +sqs / batch: up to 10 messages or 250 ms per batch -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_batch / consume_parallel point is the processing rate of +kafka, nats, rabbitmq, redis / batch: up to 500 messages or 250 ms per batch -the consumers from the moment every worker was assigned until 90 % of a corpus of 49k messages, published before they +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_batch / consume_parallel point is the processing -started, had been consumed; no producer ran in the window +rate of the consumers from the moment every worker was assigned until 90 % of a corpus of 49k messages, published -inmemory: 1 cell in this slice failed to run — absent, not zero; see failures[] +before they started, had been consumed; no producer ran in the window +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +inmemory: 1 cell in this slice failed to run — absent, not zero; see failures[] + + nats, rabbitmq, redis: 2 cells in this slice failed to run — absent, not zero; see failures[] - -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) + +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) - + Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) - + 12 recorded failure(s) in the dataset — see failures[] - + messages / second - - - - - - - - - - - - + + + + + + + + + + + + 0.0 - - + + 5.0k - - + + 10k - - + + 15k - - + + 20k - - + + 25k - - + + 30k - - + + 35k - - + + 40k - - + + 45k - - + + n/s - - - - - + + + + + ≥30k - + n/s - - - - - + + + + + 41k - - - - - + + + + + ≥1.5k - - - - - + + + + + 30k - - - - - + + + + + 7.6k - - - - - + + + + + ≥6.4k - - - - - + + + + + 8.6k - - - - - + + + + + 1.4k - - - - - + + + + + ≥3.2k - - - - - + + + + + 4.3k - - - - - + + + + + 7.1k - + n/s - - - - - + + + + + 17k - + + + + + + + + + inmemory - + kafka - + nats - + rabbitmq - + redis - + +sqs + + backend diff --git a/docs/public/bench/parallel-vs-sequenced-dark.svg b/docs/public/bench/parallel-vs-sequenced-dark.svg index 7899c87f..a3e9053c 100644 --- a/docs/public/bench/parallel-vs-sequenced-dark.svg +++ b/docs/public/bench/parallel-vs-sequenced-dark.svg @@ -7,44 +7,62 @@ Parallel vs sequenced consume 64 B payload — least-parallel measurement per mode — what ordering costs — higher is better - + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + redis / sequenced (fifo): failed to run — absent, not zero; see failures[] in the results document - + kafka, nats, rabbitmq / sequenced (fifo): lower bound (muted bar) — this window cannot separate setup from drain, so - + the true rate is at least the bar shown - + +sqs / sequenced (fifo): lower bound from a window mixing setup and drain; shape only, so its height is not the bound + + redis / parallel, batch: measured at 2 workers (the least-parallel count the measured modes share) + +nats, rabbitmq, sqs / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured + + +modes share) + + +inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound + + +sqs / batch: up to 10 messages or 250 ms per batch + -nats, rabbitmq / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured modes +inmemory, kafka, nats, rabbitmq, redis / batch: up to 500 messages or 250 ms per batch -share) +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_batch / consume_parallel point is the processing -inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound +rate of the consumers from the moment every worker was assigned until 90 % of its corpus, published before they -batch bars: up to 500 messages or 250 ms per batch +started, had been consumed; no producer ran in the window -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_batch / consume_parallel point is the processing rate of +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 6.0M; sqs 60k — a smaller corpus is a shorter -the consumers from the moment every worker was assigned until 90 % of a corpus of 6.0M messages, published before they +window, not a different measurement -started, had been consumed; no producer ran in the window +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) redis: 2 cells in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -52,142 +70,148 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second - - - - + + + - - - + + + + 0.0 - - + + 500k - - + + 1.0M - - + + 1.5M - - + + 2.0M - - + + 2.5M - - - - - - + + + + + + 654k - + n/s - - - - - + + + + + 2.5M - - - - - + + + + + 114k - + ≥1.6k - - - - - + + + + + 112k - - - - - + + + + + 54k - - - - - + + ≥23k - - - - - + + + + + 65k - - - - - + + 26k - + ≥2.4k - - - - - + + 33k - - + + 20k - + n/s - - - - - + + + + + 311k - + + + + + + + + + + + + + inmemory - + kafka - + nats - + rabbitmq - + redis - + +sqs + + backend diff --git a/docs/public/bench/parallel-vs-sequenced.svg b/docs/public/bench/parallel-vs-sequenced.svg index 57848331..c5dcabc1 100644 --- a/docs/public/bench/parallel-vs-sequenced.svg +++ b/docs/public/bench/parallel-vs-sequenced.svg @@ -7,44 +7,62 @@ Parallel vs sequenced consume 64 B payload — least-parallel measurement per mode — what ordering costs — higher is better - + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + redis / sequenced (fifo): failed to run — absent, not zero; see failures[] in the results document - + kafka, nats, rabbitmq / sequenced (fifo): lower bound (muted bar) — this window cannot separate setup from drain, so - + the true rate is at least the bar shown - + +sqs / sequenced (fifo): lower bound from a window mixing setup and drain; shape only, so its height is not the bound + + redis / parallel, batch: measured at 2 workers (the least-parallel count the measured modes share) + +nats, rabbitmq, sqs / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured + + +modes share) + + +inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound + + +sqs / batch: up to 10 messages or 250 ms per batch + -nats, rabbitmq / parallel, sequenced (fifo), batch: measured at 8 workers (the least-parallel count the measured modes +inmemory, kafka, nats, rabbitmq, redis / batch: up to 500 messages or 250 ms per batch -share) +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_batch / consume_parallel point is the processing -inmemory / sequenced (fifo): measured, but every window under 1 s — too short to publish as a rate, even a lower bound +rate of the consumers from the moment every worker was assigned until 90 % of its corpus, published before they -batch bars: up to 500 messages or 250 ms per batch +started, had been consumed; no producer ran in the window -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_batch / consume_parallel point is the processing rate of +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 6.0M; sqs 60k — a smaller corpus is a shorter -the consumers from the moment every worker was assigned until 90 % of a corpus of 6.0M messages, published before they +window, not a different measurement -started, had been consumed; no producer ran in the window +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) redis: 2 cells in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -52,142 +70,148 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second - - - - + + + - - - + + + + 0.0 - - + + 500k - - + + 1.0M - - + + 1.5M - - + + 2.0M - - + + 2.5M - - - - - - + + + + + + 654k - + n/s - - - - - + + + + + 2.5M - - - - - + + + + + 114k - + ≥1.6k - - - - - + + + + + 112k - - - - - + + + + + 54k - - - - - + + ≥23k - - - - - + + + + + 65k - - - - - + + 26k - + ≥2.4k - - - - - + + 33k - - + + 20k - + n/s - - - - - + + + + + 311k - + + + + + + + + + + + + + inmemory - + kafka - + nats - + rabbitmq - + redis - + +sqs + + backend diff --git a/docs/public/bench/throughput-vs-consumers-1kib-dark.svg b/docs/public/bench/throughput-vs-consumers-1kib-dark.svg index b0315ebe..11b29f08 100644 --- a/docs/public/bench/throughput-vs-consumers-1kib-dark.svg +++ b/docs/public/bench/throughput-vs-consumers-1kib-dark.svg @@ -7,17 +7,29 @@ Throughput vs consumer count consume_parallel — 1 KiB payload — higher is better + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the + + +consumers from the moment every worker was assigned until 90 % of its corpus, published before they started, had been + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +consumed; no producer ran in the window -from the moment every worker was assigned until 90 % of a corpus of 3.1M messages, published before they started, had +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 3.1M; sqs 60k — a smaller corpus is a shorter -been consumed; no producer ran in the window +window, not a different measurement -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -25,183 +37,192 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + 10k - - + + 20k - - + + 30k - - + + 40k - - + + 50k - - + + 60k - - + + 70k - - + + 80k - - + + 90k - - + + 100k - - + + 100k - - + + 200k - - + + 300k - - + + 400k - - + + 500k - - + + 600k - - + + 700k - - + + 800k - - + + 900k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + kafka - - + + inmemory - - + + nats - - + + redis - - + + rabbitmq - + + +sqs + + 1 - + 2 - + 4 - + 8 - + consumers @@ -224,4 +245,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/docs/public/bench/throughput-vs-consumers-1kib.svg b/docs/public/bench/throughput-vs-consumers-1kib.svg index 5a0283b1..0aa82192 100644 --- a/docs/public/bench/throughput-vs-consumers-1kib.svg +++ b/docs/public/bench/throughput-vs-consumers-1kib.svg @@ -7,17 +7,29 @@ Throughput vs consumer count consume_parallel — 1 KiB payload — higher is better + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the + + +consumers from the moment every worker was assigned until 90 % of its corpus, published before they started, had been + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +consumed; no producer ran in the window -from the moment every worker was assigned until 90 % of a corpus of 3.1M messages, published before they started, had +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 3.1M; sqs 60k — a smaller corpus is a shorter -been consumed; no producer ran in the window +window, not a different measurement -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -25,183 +37,192 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + 10k - - + + 20k - - + + 30k - - + + 40k - - + + 50k - - + + 60k - - + + 70k - - + + 80k - - + + 90k - - + + 100k - - + + 100k - - + + 200k - - + + 300k - - + + 400k - - + + 500k - - + + 600k - - + + 700k - - + + 800k - - + + 900k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + kafka - - + + inmemory - - + + nats - - + + redis - - + + rabbitmq - + + +sqs + + 1 - + 2 - + 4 - + 8 - + consumers @@ -224,4 +245,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/docs/public/bench/throughput-vs-consumers-64kib-dark.svg b/docs/public/bench/throughput-vs-consumers-64kib-dark.svg index 7feb151c..78c5c401 100644 --- a/docs/public/bench/throughput-vs-consumers-64kib-dark.svg +++ b/docs/public/bench/throughput-vs-consumers-64kib-dark.svg @@ -7,35 +7,41 @@ Throughput vs consumer count consume_parallel — 64 KiB payload — higher is better - + inmemory: measured, but no drain rate is published for this slice — window under 1 s, too short to publish as a rate - + at consumers 1, 2, 4, 8 - + kafka: partial — not plotted: window under 1 s, too short to publish as a rate at consumers 1, 4, 8 - + rabbitmq: partial — not plotted: failed to run — absent, not zero; see failures[] in the results document (a failed - + drain; the cell's ladder rungs are not its ceiling) at consumers 8 + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the -from the moment every worker was assigned until 90 % of a corpus of 49k messages, published before they started, had +consumers from the moment every worker was assigned until 90 % of a corpus of 49k messages, published before they -been consumed; no producer ran in the window +started, had been consumed; no producer ran in the window rabbitmq, redis: 1 cell in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -43,135 +49,144 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + 1.0k - - + + 2.0k - - + + 3.0k - - + + 4.0k - - + + 5.0k - - + + 6.0k - - + + 7.0k - - + + 8.0k - - + + 9.0k - - + + 10k - - + + 10k - - + + 20k - - + + 30k - - + + 40k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + kafka - + rabbitmq - - + + redis - - + + nats - + + +sqs + + 1 - + 2 - + 4 - + 8 - + consumers @@ -194,4 +209,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/docs/public/bench/throughput-vs-consumers-64kib.svg b/docs/public/bench/throughput-vs-consumers-64kib.svg index b55bbd7a..9f02e1e2 100644 --- a/docs/public/bench/throughput-vs-consumers-64kib.svg +++ b/docs/public/bench/throughput-vs-consumers-64kib.svg @@ -7,35 +7,41 @@ Throughput vs consumer count consume_parallel — 64 KiB payload — higher is better - + inmemory: measured, but no drain rate is published for this slice — window under 1 s, too short to publish as a rate - + at consumers 1, 2, 4, 8 - + kafka: partial — not plotted: window under 1 s, too short to publish as a rate at consumers 1, 4, 8 - + rabbitmq: partial — not plotted: failed to run — absent, not zero; see failures[] in the results document (a failed - + drain; the cell's ladder rungs are not its ceiling) at consumers 8 + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the -from the moment every worker was assigned until 90 % of a corpus of 49k messages, published before they started, had +consumers from the moment every worker was assigned until 90 % of a corpus of 49k messages, published before they -been consumed; no producer ran in the window +started, had been consumed; no producer ran in the window rabbitmq, redis: 1 cell in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -43,135 +49,144 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + 1.0k - - + + 2.0k - - + + 3.0k - - + + 4.0k - - + + 5.0k - - + + 6.0k - - + + 7.0k - - + + 8.0k - - + + 9.0k - - + + 10k - - + + 10k - - + + 20k - - + + 30k - - + + 40k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + kafka - + rabbitmq - - + + redis - - + + nats - + + +sqs + + 1 - + 2 - + 4 - + 8 - + consumers @@ -194,4 +209,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/docs/public/bench/throughput-vs-consumers-dark.svg b/docs/public/bench/throughput-vs-consumers-dark.svg index e3810bb2..7bf4c4dc 100644 --- a/docs/public/bench/throughput-vs-consumers-dark.svg +++ b/docs/public/bench/throughput-vs-consumers-dark.svg @@ -7,17 +7,29 @@ Throughput vs consumer count consume_parallel — 64 B payload — higher is better + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the + + +consumers from the moment every worker was assigned until 90 % of its corpus, published before they started, had been + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +consumed; no producer ran in the window -from the moment every worker was assigned until 90 % of a corpus of 6.0M messages, published before they started, had +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 6.0M; sqs 60k — a smaller corpus is a shorter -been consumed; no producer ran in the window +window, not a different measurement -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -25,183 +37,192 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + 10k - - + + 20k - - + + 30k - - + + 40k - - + + 50k - - + + 60k - - + + 70k - - + + 80k - - + + 90k - - + + 100k - - + + 100k - - + + 200k - - + + 300k - - + + 400k - - + + 500k - - + + 600k - - + + 700k - - + + 800k - - + + 900k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + kafka - - + + inmemory - - + + +sqs + + + nats - - + + rabbitmq - - + + redis - + 1 - + 2 - + 4 - + 8 - + consumers @@ -224,4 +245,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/docs/public/bench/throughput-vs-consumers.svg b/docs/public/bench/throughput-vs-consumers.svg index 5608f03b..c7252756 100644 --- a/docs/public/bench/throughput-vs-consumers.svg +++ b/docs/public/bench/throughput-vs-consumers.svg @@ -7,17 +7,29 @@ Throughput vs consumer count consume_parallel — 64 B payload — higher is better + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the + + +consumers from the moment every worker was assigned until 90 % of its corpus, published before they started, had been + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +consumed; no producer ran in the window -from the moment every worker was assigned until 90 % of a corpus of 6.0M messages, published before they started, had +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 6.0M; sqs 60k — a smaller corpus is a shorter -been consumed; no producer ran in the window +window, not a different measurement -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -25,183 +37,192 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + 10k - - + + 20k - - + + 30k - - + + 40k - - + + 50k - - + + 60k - - + + 70k - - + + 80k - - + + 90k - - + + 100k - - + + 100k - - + + 200k - - + + 300k - - + + 400k - - + + 500k - - + + 600k - - + + 700k - - + + 800k - - + + 900k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + kafka - - + + inmemory - - + + +sqs + + + nats - - + + rabbitmq - - + + redis - + 1 - + 2 - + 4 - + 8 - + consumers @@ -224,4 +245,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/docs/public/bench/throughput-vs-payload-dark.svg b/docs/public/bench/throughput-vs-payload-dark.svg index 4d6bfd62..27a1fcd8 100644 --- a/docs/public/bench/throughput-vs-payload-dark.svg +++ b/docs/public/bench/throughput-vs-payload-dark.svg @@ -7,26 +7,38 @@ Throughput vs payload size consume_parallel — 1 consumer — higher is better - + inmemory: partial — not plotted: window under 1 s, too short to publish as a rate at payload size 64 KiB - + kafka: partial — not plotted: window under 1 s, too short to publish as a rate at payload size 64 KiB + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the + + +consumers from the moment every worker was assigned until 90 % of its corpus, published before they started, had been + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +consumed; no producer ran in the window -from the moment every worker was assigned until 90 % of a corpus of 49k / 3.1M / 6.0M messages, published before they +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 49k / 3.1M / 6.0M; sqs 49k / 60k — a smaller corpus -started, had been consumed; no producer ran in the window +is a shorter window, not a different measurement redis: 1 cell in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -34,84 +46,92 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - + + + + + 1.0k - - + + 10k - - + + 100k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inmemory - + kafka - - + + nats - - + + redis - - + + +sqs + + + rabbitmq - + 64 B - + 1 KiB - + 64 KiB - + payload size @@ -134,4 +154,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/docs/public/bench/throughput-vs-payload.svg b/docs/public/bench/throughput-vs-payload.svg index 7e5b4275..0e3a87c1 100644 --- a/docs/public/bench/throughput-vs-payload.svg +++ b/docs/public/bench/throughput-vs-payload.svg @@ -7,26 +7,38 @@ Throughput vs payload size consume_parallel — 1 consumer — higher is better - + inmemory: partial — not plotted: window under 1 s, too short to publish as a rate at payload size 64 KiB - + kafka: partial — not plotted: window under 1 s, too short to publish as a rate at payload size 64 KiB + +sqs: shape only — not representative (LocalStack, not AWS); magnitudes are not published + + +sqs: measured as supervisor — this backend's spelling of consume_parallel (the same run primitive) + + +drain (inmemory, kafka, nats, rabbitmq, redis, sqs): each consume_parallel point is the processing rate of the + + +consumers from the moment every worker was assigned until 90 % of its corpus, published before they started, had been + -drain (inmemory, kafka, nats, rabbitmq, redis): each consume_parallel point is the processing rate of the consumers +consumed; no producer ran in the window -from the moment every worker was assigned until 90 % of a corpus of 49k / 3.1M / 6.0M messages, published before they +corpus differs by backend: inmemory, kafka, nats, rabbitmq, redis 49k / 3.1M / 6.0M; sqs 49k / 60k — a smaller corpus -started, had been consumed; no producer ran in the window +is a shorter window, not a different measurement redis: 1 cell in this slice failed to run — absent, not zero; see failures[] -Generated 2026-09-08T12:34:58Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) +Generated 2026-09-08T16:10:59Z — shove 0.14.0 — Apple M4 Max (16c / 64 GB) Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e7d7e 2025-11-07) — handler: zero (no-op) @@ -34,84 +46,92 @@ Apple M4 Max — 16 cores — 64 GB RAM — macOS 26.6.2 — rustc 1.91.1 (ed61e 12 recorded failure(s) in the dataset — see failures[] - + messages / second — log scale - - - - - + + + + + 1.0k - - + + 10k - - + + 100k - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inmemory - + kafka - - + + nats - - + + redis - - + + +sqs + + + rabbitmq - + 64 B - + 1 KiB - + 64 KiB - + payload size @@ -134,4 +154,8 @@ rabbitmq redis + + +sqs (shape only) + diff --git a/tests/chartgen.rs b/tests/chartgen.rs index d31760c6..a7041cdb 100644 --- a/tests/chartgen.rs +++ b/tests/chartgen.rs @@ -2556,19 +2556,43 @@ fn a_caption_a_few_lines_over_budget_grows_the_canvas_instead_of_refusing() { "text baseline y={y} is outside the grown canvas of {height}: {content:?}" ); } - let ticks: Vec = texts(&svg) - .into_iter() - .filter(|(x, _, _, t)| *x < Y_TICK_BAND_X && (t.ends_with('k') || t == "0.0")) - .map(|(_, y, _, _)| y) - .collect(); - let span = - ticks.iter().cloned().fold(0.0, f64::max) - ticks.iter().cloned().fold(f64::MAX, f64::min); + // The plot's actual vertical extent is the y axis itself: plotters draws + // it as a two-point polyline with one x, and it is the longest such line + // on the canvas. Tick labels stop short of the boundaries, so a + // tick-based span would let an undersized plot through. + let axis_span = vertical_axis_span(&svg).expect("the chart draws a y axis"); assert!( - span >= chartgen::MIN_PLOT_PX as f64 * 0.8, - "the plot body spans only {span:.0}px on the grown canvas" + axis_span >= f64::from(chartgen::MIN_PLOT_PX), + "the plot body spans only {axis_span:.0}px on the grown canvas, under the \ + {}px minimum", + chartgen::MIN_PLOT_PX ); } +/// The vertical span of the longest two-point vertical polyline in `svg`: the +/// y axis, which bounds the plot body exactly. +fn vertical_axis_span(svg: &str) -> Option { + svg.split(" = points + .split_whitespace() + .filter_map(|p| { + let (x, y) = p.split_once(',')?; + Some((x.parse().ok()?, y.parse().ok()?)) + }) + .collect(); + match pts.as_slice() { + [(x1, y1), (x2, y2)] if x1 == x2 => Some((y1 - y2).abs()), + _ => None, + } + }) + .fold(None, |best: Option, span| { + Some(best.map_or(span, |b| b.max(span))) + }) +} + #[test] fn the_plot_body_keeps_a_minimum_height_under_a_tall_caption() { // The frame refuses a caption block that leaves the plot under a