Measured, with the box named

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.

Enginep50p90p99MaxMeasured on
Ubik28303131exactly-once path
Proton27282828default delivery
Flink281481534538default 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 groupsState on diskCheckpoint pause
10 0000.69 MiB7.8 ms
100 0006.95 MiB61 ms
500 00034.8 MiB306.4 ms
993 19469.14 MiB605.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.

WorkloadEvents/sWhat saturates next
Filter and project, from a file17 693 541decode fans out across workers
Count, from a Kafka topic3 120 970this is what the source itself delivers
SESSION window, gap-based4 680 412scales radix-by-key
TUMBLE window, from a file1 886 252the windowed-aggregate operator
TUMBLE window, from a Kafka topic1 399 353the shape most jobs actually run
TUMBLE window, state spilled to NVMe1 122 861state larger than the memory budget
HOP window, sliding759 798the 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.