Numbers, including the ones that are not flattering
A single headline throughput figure tells you nothing, so there is not one here. What follows is a latency distribution, a throughput table per workload, and the cost of a checkpoint as state grows.
How long after a window closes does its answer land
Three engines, one broker, back to back in one session. Ubik is measured on its exactly-once path: a transactional sink, and a read_committed consumer waiting for a commit the other two never make. The other two ran in their default delivery configuration.
| Engine | p50 | p90 | p99 | Max | Measured on |
|---|---|---|---|---|---|
| Ubik | 28 | 30 | 31 | 31 | exactly-once path |
| Proton | 27 | 28 | 28 | 28 | default delivery |
| Flink | 281 | 481 | 534 | 538 | default delivery |
Read the spread. Over fifty thousand windows Ubik's slowest was 31ms and its fastest 23ms, on the exactly-once path. A flat tail is what you live with on call.
One dev workstation, governor not pinned. 2026-07-24. 50 000 events/s from a paced live producer, 10 000 keys, TUMBLE 5 seconds, one partition, zero watermark delay, 50 000 closed windows measured per engine. Absolute figures move day to day on an unpinned box, so they are not comparable across runs. What holds is the shape: a flat distribution at exactly-once, and an order of magnitude under the incumbent. Regenerate with bench/compare/run.sh.
What a checkpoint costs as state grows
The distribution above was measured at ten thousand keys, where a checkpoint costs under eight milliseconds. It does not stay there. A snapshot writes about 72 bytes per open group at about 8.8 ms per MiB, linearly. If a million open groups is your cardinality, plan for a six hundred millisecond pause.
| Open groups | State on disk | Checkpoint pause |
|---|---|---|
| 10 000 | 0.69 MiB | 7.8 ms |
| 100 000 | 6.95 MiB | 61 ms |
| 500 000 | 34.8 MiB | 306.4 ms |
| 993 194 | 69.14 MiB | 605.5 ms |
This is on the page because you would otherwise find it in production. The cause is known: the serializer boxes every key and unboxes it again when the packed bytes are already portable. It is being fixed.
Intel i9-11900K, 6 workers, powersave governor. 2026-07-25. The real windowed operator, bound by the real planner, snapshotted at maximum open state. Swept by group cardinality. The write is CPU-bound rather than disk-bound: the same state to tmpfs and to encrypted btrfs differ by 8%. The pause is the serializer, and it is being worked on. Reproduce with ubik-agg-ceiling --checkpoint.
Throughput, per workload shape
A mono-node ceiling is a number per shape. Stateless work runs in the tens of millions, windowed aggregates in the hundreds of thousands to low millions, a sliding window at the low end. Six pinned cores of one machine.
| Workload | Events/s | What saturates next |
|---|---|---|
| Filter and project, from a file | 17 693 541 | decode fans out across workers |
| Count, from a Kafka topic | 3 120 970 | this is what the source itself delivers |
| SESSION window, gap-based | 4 680 412 | scales radix-by-key |
| TUMBLE window, from a file | 1 886 252 | the windowed-aggregate operator |
| TUMBLE window, from a Kafka topic | 1 399 353 | the shape most jobs actually run |
| TUMBLE window, state spilled to NVMe | 1 122 861 | state larger than the memory budget |
| HOP window, sliding | 759 798 | the known low end; measure first if this is your workload |
AMD EPYC 9575F, 6 of 8 cores pinned, 31 GiB RAM, Linux 6.8. 2026-07-24. 5M rows, 3 repetitions, best-of wall clock, a real Redpanda broker for the Kafka rows. Every figure regenerates from bench/ in the engine repo, and a regression over 5% fails the merge gate. Each row is the ceiling for one workload shape.