Can I see it myself?
279 labs from 33 mechanisms. Each is a script you run with one command, next to the run we recorded.
BandThe Machine: jump to another band
The Machine
Write amplification: same-age vs. mixed-age writes
A flash translation layer simulator filled to 50–90% and overwritten with same-age (whole files) and mixed-age (random pages) writes.
SimulatePython with NumPyThe cost of utilization, u/(1-u)
A log-structured store with a greedy cleaner at 50% to 95% live pages: pages copied per page of new space, against u/(1 − u).
SimulatePythonSorted vs. shuffled branch benchmark
Sum the elements above a threshold over shuffled and sorted data, compiled at -O1, -O2, -O3 and with a branch the compiler cannot remove.
MeasureC compiler, Python
3 more labs
Put your own code on the roofline
Measure this machine's memory and compute ceilings, then place a scan, a filter and a hash aggregation on its roofline.
MeasurePython with NumPyReproduce incast on a laptop
One receiver asks N senders for 32 KB at once, N from 1 to 500, with synchronized and jittered replies: p50, p99, p999 on loopback.
MeasurePythonFit a USL curve to something real
N processes each scanning their own array (memory-bandwidth-bound): fit the Universal Scalability Law at N ≤ 8 and test the prediction beyond.
MeasurePython with NumPy, SciPy
Bytes in Motion
Head-of-line blocking: log versus queue
Simulate 12 partitions with 100 records/s each and one poison record.
SimulatePythonPer-record calls versus batches: find the natural batch size
Send 100,000 records to a local HTTP server in batches of 1 to 10,000 and fit the fixed cost per call.
MeasurePython with requestsAck before or after processing: loss versus duplicates
Kill a consumer at random moments: saving the offset first loses records, processing first duplicates them.
Break itPython
17 more labs
Bufferbloat: a bigger buffer buys latency, not throughput
A producer 20% faster than its consumer, with queues from 100 to 100,000: throughput stays put, latency grows with the queue.
SimulatePythonWhat an overflowing buffer loses
A keyed stream overloads a bounded buffer under drop-newest, drop-oldest and drop-oldest per key: count the records dropped and the entities left with a wrong value.
SimulatePythonBatch size: find the knee
Insert 200,000 rows into SQLite with one commit per batch, then bound trickle-traffic latency with a size-or-age rule.
MeasurePythonToken bucket versus fixed window
Two bursts straddling a one-second boundary against a fixed window, two token buckets and a sliding window.
SimulatePythonMeasure key skew and the effect of salting
Partition 1,000,000 events by seller, by order and by salted seller, when one seller owns 40% of them.
SimulatePythonHow many keys move when N changes? Modulo vs. ring vs. rendezvous
Add an eleventh node: how many of 100,000 keys move under modulo hashing, a ring with 1 or 128 virtual nodes, and rendezvous hashing.
SimulatePythonBreak per-key order in a real Kafka topic, then repair it
Per-key order in Kafka, broken three ways (a worker pool, lost acks without idempotence, growing the topic) and repaired twice.
Real system · Break itDocker, PythonA DLQ that does not become an outage amplifier
Write a small consumer with a retry-then-DLQ policy.
SimulatePythonChoose N by measurement
Measure one consumer, derive the partition count, then compare N and 2N partitions for recovery time and files per hour.
Real system · MeasureDocker, PythonWhat a rebalance repeats
Redeploy one consumer of a 12-member group on a 24-partition topic, many times: count what eager, cooperative and static membership process twice.
SimulatePythonThe length-prefix attack and the maximum frame size
A server that trusts a 4-byte length header, a header claiming 4 GB, and one wrong length in the middle of a stream.
Break itPythonBatch size is a compression parameter
Compress 100,000 small JSON events in batches of 1 to 10,000, then with a trained dictionary.
MeasurePython with zstandardCompress or not: the rate comparison
compress + transfer + decompress against sending raw, for five codecs and three link speeds.
MeasurePython with lz4, zstandardStale pooled connections: find out what your pool does
Exhaust a pool of 2, kill a proxy silently under a pooled connection, and read your HTTP library's defaults.
Break itPython with urllib3Nagle plus delayed ACK: reproduce the 40 ms cliff
Two small writes and a short reply: Nagle on the sender and delayed ACK on the receiver add about 40 ms per call.
MeasurePython (Linux shows the effect)Dedup window versus replay
A 60-second dedup window against a replay five minutes later, then idempotency by replacing the partition.
SimulatePythonReconciliation count and duplicate-rate metric
Count records per hour at source and sink, then plant a bug that swallows 1% of writes: errors stay at zero, the count gap does not.
SimulatePython
Bytes at Rest
The one ratio: bytes touched versus bytes needed on your own disk
Random 200-byte reads from a 1 GB file, as plain and as 4 KiB aligned reads with the page cache bypassed; then 1,000 small range GETs against 10 large ones from an S3-compatible store.
Real systemDocker, PythonRow versus column on the same table
10 million rows of 25 columns as CSV and as Parquet: file size, reading 2 columns, and reading one row by position.
MeasurePython with pandas, PyArrowCopy-on-write versus delete vector
5,000 row corrections on a table of 640,000-row files: copy-on-write rewrites against merge-on-read delta files, and scan time as deltas accumulate.
MeasurePython with DuckDB, PyArrow
4 more labs
Clustering payoff: sorted versus random files
20 million rows as 20 Parquet files, random and sorted by (depot_id, scan_time): files a one-depot query must open, then after one unsorted append.
MeasurePython with PyArrow, pandasThe composite-key rule (`C_a ≤ U`)
20 files sorted by (depot_id, scan_time) and by (user_id, scan_time): files a one-day query must open, alone and with a depot.
MeasurePython with PyArrow, pandasZ-order floor
A million 2-D points in 4,096 units: sorted on dimension 1, Z-ordered, and random; units a thin query on each dimension touches, and a 50/50 mix.
SimulatePython with NumPyQueue depth and the 0.7% reader
Random 4 KB reads from a 2 GB file with 1 to 128 requests in flight, page cache bypassed: IOPS against queue depth; then mmap.
MeasurePython
Kill vs. power loss: what survives?
Kill -9 a writer 50 times with and without fsync and count the acknowledged lines that survive. The power-loss half is not run here.
Break itPythonGroup commit: break the 1/latency ceiling
A log server syncing per commit against one with group commit, at 1 to 256 clients: commits per second and latency.
MeasurePythonGroup commit: measure fsync and the self-tuning batch
The cost of a durable write, 20 writes per flush, and pgbench with synchronous_commit on and off at 1 and 32 clients.
MeasurePython, Docker
5 more labs
Group commit and the square-root rule
Append 100-byte records with a flush after every 1, 10 and 100; then a timer batcher at λ = 2,000/s for waits W from 0.5 to 16 ms against W* = √(2·fsync/λ).
MeasurePythonWhat does fsync really cost on your machine?
Time 200-byte appends with no sync, with fsync, and on macOS with F_FULLFSYNC: the durability tax of your drive.
MeasurePythonCheckpoint interval versus WAL volume (Postgres)
Run pgbench for 120 s with checkpoints every 30 s and every 15 min, then without full-page writes; compare WAL bytes per transaction.
Real systemDocker, PythonRestore test and time-to-restore
Back up a 1 GB PostgreSQL database with WAL archiving, drop a table, restore to just before the drop, time it; then restore without the WAL.
Real systemDocker, PythonA checksum that proves identity
A toy block store with checksums next to the data and checksums in an index: which one catches a misdirected write and a lost write?
Break itPython
Hash table load factor: measure the hyperbola
Fill a linear-probing table to α = 0.5 … 0.95, count probes against the formulas, then time it against chaining.
MeasurePythonB-tree key order: UUIDv4 versus UUIDv7
Insert 5 million rows under a random UUID key and under a time-ordered key into PostgreSQL; compare index size, page fill, insert time and WAL.
Real systemDocker, PythonLSM versus B-tree write amplification, derived and measured
Write small rows and 8 KB rows into RocksDB and LMDB, measure device bytes per user byte, then turn on RocksDB blob files.
Real systemDocker
15 more labs
Monkey-style filter allocation
Split one Bloom-filter memory budget across LSM levels equally or Monkey-style, count wasted I/Os per million missing-key lookups.
SimulatePythonSparse index granule size
Index a sorted file of 100 million records every G records and time point lookups for G from 64 to 65,536.
MeasurePythonSliding maximum: naive, deque and panes
Compute a sliding maximum over a 10,000-value window naively, with a monotonic deque, and with panes; then feed the deque its best and worst shapes.
MeasurePython with NumPyTop-k with a bounded heap versus a full sort
Find the top 10 of 20 million numbers with sort | head and with a bounded heap; compare time and peak memory, then read DuckDB's plan.
MeasurePython, sortBloom filter: measure the false-positive curve and the cache cost
Measure a Bloom filter's false-positive rate at 5 to 20 bits per key against the formula, then time lookups in a 10 MB, a 1 GB and a blocked 1 GB filter.
MeasurePython with NumPyCount-Min Sketch: head versus tail
Count a Zipf stream of 10 million events with a Count-Min sketch, compare head and tail errors, then find heavy hitters with Misra-Gries and Space-Saving.
SimulatePython with NumPyHyperLogLog: error, merge and the intersection trap
Check HyperLogLog's error over 20 runs, merge two sketches, then estimate a 1% intersection with HLL and with a Theta sketch.
SimulatePythonDo not average percentiles
Compute the p99 of 100 shards by averaging shard p99s and by merging KLL, t-digest and REQ sketches, against the exact pooled p99.
SimulatePythonRoaring versus plain bitmap versus sorted array
Size sets of row IDs at five densities as a plain bitmap, a sorted array and a Roaring bitmap, then time a 1% intersection in each.
MeasurePythonElias-Fano and the posting-list size
Store a million sorted doc IDs as int32, delta + varint and Elias-Fano; compare size and random-access time.
MeasurePython with NumPyFalse sharing: measure the 64-byte penalty
Eight threads increment their own counters: packed in one line, padded to 64 and 128 bytes, and counted locally.
MeasureC compiler, PythonSPSC ring buffer: head and tail on one line, then on two
Move 100 million items through a single-producer single-consumer ring with head and tail on one line, on separate lines, and with batched publishing.
MeasureC compiler, PythonPersistent tree: measure the cost of path copying
Insert a million keys into a mutable and a persistent binary tree; compare time and memory, hold a snapshot, count shared nodes.
MeasurePythonReader-writer lock against plain mutex on a read-heavy workload
99% reads and 1% writes from 1 to 16 threads under a mutex, a reader-writer lock and an atomic snapshot pointer.
MeasureC compiler, PythonBuild a pruning cascade and count rows touched
Prune 1,000 point-in-range queries over 10 million rows through a zone map, a Bloom filter and a sparse index; then shuffle the rows.
SimulatePython with NumPy
Find your own break-even point D/B
Compress a 300 MB JSON log at zstd levels 1 to 19 and check, against a local disk and an object store, whether decoding beats reading.
MeasurePython with zstandardHow big is JSON really, after compression?
Encode 1,000 parcel-scan records as JSON and as Avro, raw and after zstd, with random and with sequential IDs.
MeasurePython with fastavro, zstandardMake `memcmp` sort your numbers correctly
Sort 10,000 integers and floats by their byte encodings, with and without the sign-bit flip and the float rule, and test composite-key escaping.
MeasurePython
7 more labs
Varint and zigzag: where the bet wins and loses
Bytes per value under varint and zigzag + varint for small counts, IDs, random 64-bit values and small signed numbers.
MeasurePythonRow order versus codec
Write 2 million rows to Parquet shuffled and sorted under NONE, SNAPPY and ZSTD; compare sizes and the depot column's row-group statistics.
MeasurePython with PyArrow, NumPyOne outlier sets the width
Bit-pack a block of 128 values, add one outlier, then patch it out with PFOR.
MeasurePython with NumPyEncoding by hand: dictionary, RLE and delta
A 10-million-value country column and a sorted timestamp column: dictionary, RLE before and after sorting, delta, and gzip, computed by hand.
MeasurePython with NumPyAre your floats decimals in disguise?
A million latitude-like doubles with six decimals: zlib on the raw bytes against scaling to integers and frame-of-reference.
MeasurePython with NumPyBlock size, ratio and point-lookup cost
Compress 200,000 JSON rows in blocks from 1 KB to 1 MB; compare ratio and the cost of decoding one block for a point lookup.
MeasurePython with zstandardDoes a trained dictionary rescue small messages?
Compress 100-byte JSON messages one at a time with and without a trained zstd dictionary, then decode with the wrong one.
MeasurePython with zstandard
Find the crossover on your hardware
Index scan against sequential scan on 5 million PostgreSQL rows, scattered and clustered, at two random_page_cost settings.
Real systemDocker, PythonComposite order and the range column
Indexes on (a, b, c) and (a, c, b) for a = 1 AND b > 5 AND c = 3 in PostgreSQL: index condition, filter and buffers.
Real systemDocker, PythonThe partial index and the parameter
A partial index over 1,000 pending rows in 5 million: its size, and whether literal and prepared queries can use it.
Real systemDocker, Python
3 more labs
Zone maps and clustering
50 million timestamped rows in Parquet, random and sorted: row groups a one-hour query must read.
MeasurePython with PyArrowInverted index intersection
Intersect posting lists for a common and a rare term over a million documents: query order, smallest first, and skips.
MeasurePythonChoose nlist for IVF
IVF over a million random 128-dimensional vectors: queries per second and recall@10 for nlist 100 to 16,000 and nprobe 1 to 32.
MeasurePython with FAISS
Concurrency & Consistency
Conflict grows with the square of concurrency (uniform versus skewed keys)
L concurrent transactions touching k random keys out of N: conflicting pairs against L²k²/2N, uniform and Zipf-skewed.
SimulatePython with NumPyPhantoms and gap locks depend on the index
MySQL/InnoDB: SELECT ... FOR UPDATE on a price range, with and without an index on price; which inserts wait, and how many locks are held.
Real systemDocker, PythonReproduce a deadlock, then remove it by ordering locks
Two PostgreSQL sessions move money between the same two wallets in opposite orders, 20 times; then with a fixed lock order; then with deadlock_timeout = 100ms.
Break itDocker, Python
7 more labs
One old snapshot pins the version space
Two minutes of random updates on a million-row PostgreSQL table, without and with an old Repeatable Read snapshot open: size and dead tuples.
Real systemDocker, PythonPessimistic versus optimistic crossover
16 workers doing read-modify-write on a PostgreSQL counter table of K rows, with SELECT FOR UPDATE and with a version column: throughput and goodput from K = 1,000,000 to 1.
Real systemDocker, PythonWrite skew under snapshot isolation, and its fix
Two on-call agents each check that two are on shift and each go off shift: REPEATABLE READ against SERIALIZABLE; then 50 clients and the retry rate.
Real systemDocker, PythonThe idempotency key: the unknown outcome made harmless
A top-up whose acknowledgment is lost, retried naively and with an idempotency key in SQLite; then a replay after the key has expired.
Break itPythonSavepoint cliff in Postgres (more than 64 subtransactions)
A PostgreSQL transaction holding 0 to 200 subtransactions open while pgbench reads the same rows: read throughput across the 64-subtransaction cache.
Real systemDocker, PythonContended compare-and-swap: n²T/2 and the value of jitter
n writers each need one successful conditional write on a shared pointer: total time and wasted attempts with immediate retry and with jittered backoff.
SimulatePythonAvailability of unanimity: 0.999ⁿ versus a majority
Atomic commit needs every participant; consensus needs a majority: a^n against the binomial majority, then 10 million simulated rounds with and without rack failures.
SimulatePython with NumPy
Anomaly count equals rate times window
Count conflicting operations inside a window and compare with λ × W, from one SQL statement to replica lag under load.
SimulatePython with NumPyMeasure the Dekker pattern and the price of `r + w ≥ d`
Two regions with an 80 ms delay run the Dekker test: local reads and writes can both read 0; only waiting for the other copy removes it.
Simulate · Break itPythonCount the shapes with a tiny dependency-graph checker
Build ww, wr and rw edges from small histories and classify lost update, write skew and read skew by their cycles.
SimulatePython
9 more labs
Reproduce the lost update at Read Committed, then fix it three ways
Lose an update under Read Committed in PostgreSQL, then fix it four ways.
Real system · Break itDocker, PythonSame name, different promise: compare engines
One phantom test at the level PostgreSQL and MySQL both call Repeatable Read, with different results.
Real system · Break itDocker, PythonWrite skew under Repeatable Read, and the two cures
Two transactions each check a total, then withdraw from different accounts: write skew under Repeatable Read, and the two cures.
Real system · Break itDocker, PythonRead-your-writes with a replica, and a lost token
Write on a PostgreSQL primary, read from a lagging replica, then carry a log-position token, then lose it.
Real system · Break itDocker, PythonAnomaly rate equals window times rate
Stale reads from a lagging replica against λ × W, for three lags, then reads routed to the primary after a write.
Real systemDocker, PythonLWW versus a G-counter under clock skew
Three replicas, one clock 3 s ahead, 1,000 increments: last-writer-wins and max lose them; a G-counter keeps all.
SimulatePythonR + W > N is not recency
A toy N = 3 register: a failed W = 2 write leaves reads with R = 2 going back and forth.
SimulatePythonCount I-confluence in your own schema
Classify every constraint of a schema as invariant-confluent or not, and price the escrow for its bounds.
SimulatePythonRun an Elle-style check on your own database
A list-append workload against PostgreSQL at three isolation levels, with the dependency graph rebuilt and searched for cycles.
Real system · Break itDocker, Python
Query Engines
Same answer, different cost
The same report two ways on 20 million orders in DuckDB, then with the filter forced above the join: time and rows out of the join.
MeasurePython with DuckDBBlocking versus streaming
Time to first row and total time for LIMIT 10, a full ORDER BY and ORDER BY … LIMIT 10 over 20 million rows in DuckDB.
MeasurePython with DuckDB, PyArrowThe NULL traps
NOT IN against NOT EXISTS before and after one NULL; a LEFT JOIN filtered in WHERE against in ON; a SUM after a join that fans out.
Break itPython with DuckDB
8 more labs
Join algorithms and the memory threshold
A 20-million-row probe joined to a 5-million-row build side with a payload in DuckDB, under memory limits from 4 GB to 100 MB, and at 1 and 8 threads.
MeasurePython with DuckDBAs-of join versus range join
The price at order time for 1,000 products × 1,000 price changes: a range join with ROW_NUMBER against DuckDB's ASOF JOIN.
MeasurePython with DuckDBWhen does partial aggregation pay?
Local pre-aggregation of 100,000 rows per partition for G from 100 to 4 million groups: measured reduction against N/(P·G), and its cost.
SimulatePythonSketches versus sampling for COUNT DISTINCT
Distinct users in 20 million events: exact, HyperLogLog, and "count distinct in a 1% sample × 100"; then the same sample for a SUM.
MeasurePython with DuckDBFloat summation is not associative
One million doubles of very different magnitudes summed in five random orders with a plain loop, against math.fsum.
MeasurePythonThe sort that is not an I/O problem: count the passes
DuckDB sorts 20 million rows (about 2 GB) under a 500 MB memory limit: temp files, wall and CPU time, random and pre-sorted input, and the one-pass capacity M²/B.
MeasurePython with DuckDBNormalized keys and sorting a pointer array
Sort 300,000 three-column rows with a typed comparison and with normalized byte keys; then sort prefix + index pairs.
MeasurePythonDigest collisions and Bloom-filter data loss
The birthday formula with 32-bit digests; then a Bloom filter used as a dedup over a million distinct keys, and as a negative oracle in front of an exact set.
SimulatePython
Estimate versus actual on a correlated filter
A PostgreSQL table where postcode implies city: the row estimate against the actual count, with per-column statistics, dependencies and an MCV list.
Real systemDocker, PythonJoin elimination with and without an enforced constraint
PostgreSQL removes a LEFT JOIN to a unique key it does not need; an inner join stays. Then an unenforced key and an orphan row change the answer.
Real systemDocker, PythonFind the index-versus-scan crossover on your machine
Force index and sequential scans across selectivities from 0.01% to 50%, on clustered and shuffled data, and compare with what the planner picks.
Real systemDocker, Python
9 more labs
A partition with no statistics
Load a new day into a partitioned PostgreSQL table and compare the plan before and after ANALYZE on that partition alone.
Real systemDocker, PythonCross the dynamic-programming cliff
Plan joins of 8 to 16 tables in a chain and a star with PostgreSQL's exhaustive search and with GEQO; count distinct plans.
Real systemDocker, PythonFour pairs of equal queries
NOT IN against NOT EXISTS with a NULL, a function on a partition key against a range, and a CTE materialized or not, in PostgreSQL.
Real systemDocker, PythonMin/max skipping on sorted versus unsorted data
10 million store_ids in Parquet, random and sorted: how many of 100 row groups a filter must read, and what one outlier does.
MeasurePython with PyArrowPushdown and projection: count the bytes
DuckDB on Parquet: a filter on sorted and random files, all columns against one, and a filter wrapped in an expression.
MeasurePython with DuckDBTop-N heap against full sort
The top 100 of 20 million numbers with a sort and with a heap; then a PostgreSQL LIMIT that makes the plan worse.
Real systemDocker, PythonBloom filter size and false-positive rate
Bloom filters for a million keys at 5, 10 and 15 bits per key: false-positive rate and size.
MeasurePython with NumPyLate materialization crossover
Filter a 100-million-element array with a boolean mask and with a position list, across selectivities, and find where gathering loses.
MeasurePython with NumPyParameter sniffing in PostgreSQL
A prepared statement on a 95%/0.002% skewed column: custom plans, generic plans, and when PostgreSQL switches between them.
Real systemDocker, Python
Barriers multiply variance
Reproduce E[max]/E[mean] ≈ 1 + c·√(2 ln n) and the BSP rule that the penalty multiplies by the number of rounds.
SimulatePython with NumPyMeasure the cost of a boundary: tuple-at-a-time against batches
See the "fixed cost per control transfer" with your own numbers.
MeasurePython with NumPyFind the best batch size on your machine
Check the claim "batch size is chosen so the working set stays in cache, about L1 size ÷ row width".
MeasurePython with NumPy
7 more labs
Compute a compilation break-even
Turn "compilation has a break-even data volume" into a measured number.
MeasurePython with NumPy, numbaPer-row UDF versus vectorized UDF
Score 5 million rows with x * 0.3 + 2.0 per row and vectorized in blocks of 100 to 1,000,000; and the time to read and write the data.
MeasurePython with pandas, NumPy, PyArrowWhole-query restart against task retry
Check that expected whole-query attempts grow like e^(nTλ).
SimulatePythonThe Zipf ceiling on data-parallel speedup
Turn "the largest partition is your serial fraction" into a number.
SimulatePython with NumPyTriage plot, salting and the join product
Classify three kinds of long tail, then fix skew by salting, and see that join skew multiplies.
SimulatePython with NumPy, matplotlibMany small reads against few large reads: the transfer floor
Measure the minimum efficient transfer size on your own disk, then compute a shuffle width ceiling from it.
MeasurePythonWhen does shuffle compression pay? And a non-deterministic partition function
Measure codec throughput and ratio on shuffle-like data, then apply C > BW · r/(r−1).
Measure · Break itPython
Arena versus `malloc`: the 50× claim
100 million 24-byte objects with malloc/free and with a bump arena; then fragmentation: resident memory against live bytes.
MeasureC compiler, PythonGC cost against headroom: `1 / (H/L − 1)`
A Java program with a live set of small objects at heap sizes from 1.25 to 7 times the live set: GC share of time against 1/(H/L − 1); then the live set off-heap.
MeasureJava 17+, PythonFind your NUMA effect (first touch) and measure it honestly
Scan a 1 GB array with all cores after single-threaded, parallel and interleaved first touch, in a Linux container: a single-node control run.
Real systemDocker
6 more labs
LRU, random and Clock on a cyclic loop
LRU, random and Clock replaying a loop over 100 pages for pools of 1 to 100 frames; then a one-time scan against a hot set under LRU, 2Q and a scan ring.
SimulatePythonScan resistance in a real database (PostgreSQL)
PostgreSQL with a 128 MB buffer pool: a hot table's pages after a sequential scan of a 1 GB table, and after reading the same table through an index.
Real systemDocker, PythonGrant quantization: count the passes
The external sort pass count ⌈log_(M/B − 1)(N/M)⌉ for 1 TB across memory grants; then PostgreSQL sorting 5 million rows from work_mem 64 kB to 1 GB.
Real systemDocker, PythonThe spill cliff: 5% spilled, how much slower?
Measure memory and disk bandwidth, then spill 0%, 5% and 20% of an 800 MB array to a file and back: the slowdown against (1 − f) + 2·f·r.
MeasurePython with NumPySort versus hash under a hot key
50 million rows with 30% on one key under a 500 MB DuckDB limit: SUM/COUNT against median by key, and a grace partitioner's largest partition under 5 hash seeds.
MeasurePython with DuckDBQueueing on the spill device
Rate-limited sequential writers sharing one SSD at 20% to 100% of its measured throughput: completion latency against S/(1 − ρ).
MeasurePython
Measure leverage and the cost of a cold restart
See that hit rate is leverage, and that a restart is a load spike on the layer below.
SimulatePython with NumPyDraw a miss-ratio curve with SHARDS-style sampling
Check that one pass over a trace gives the miss ratio for every LRU size, and that key sampling is accurate.
SimulatePython with NumPy, numbaFind the plateau and the knee
See that adding cache memory can do nothing, then a lot.
SimulatePython with NumPy
4 more labs
Negative cache and Bloom filter
Measure the effect of absent-key lookups and the false-positive rate of a Bloom filter.
MeasurePythonThe race between a reader and a writer: update versus delete
Use two threads and a dictionary as the "cache" plus a second dictionary as the "store".
Break itPythonStampede with and without protection
Write a small Python program with a fake "database" function that sleeps 2 seconds and counts how many times it is called.
SimulatePythonScan resistance: LRU against an admission filter
Generate a Zipf-distributed request stream (alpha = 1) over 100,000 keys with numpy.random.zipf or a precomputed cumulative table, 2 million requests, cache size C = 5,000.
SimulatePython with NumPy
Distribution & Failure
A shorter timeout makes a failure detector wrong more often
Measure the false-suspicion rate of a heartbeat detector against its timeout ([The arithmetic worth carrying](/mechanisms/coordination/how-it-works/no-shared-present/the-arithmetic), [Failure detection: liveness is a relation, not an attribute](/mechanisms/co
SimulatePythonFan-out arithmetic and rebalancing time
Check two formulas from [The arithmetic worth carrying](/mechanisms/coordination/how-it-works/no-shared-present/the-arithmetic) and [State has mass](/mechanisms/coordination/how-it-works/distribution-of-data#the-arithmetic-everything-divides-by) with real numbers.
SimulatePython with NumPyWhy two majorities always intersect, and why a lock with a TTL is not enough
See quorum intersection and a stale lock holder ([Consensus: what it delivers and what it costs](/mechanisms/coordination/how-it-works/deciding-without-observing/consensus) and [Locks, leases and fences: why mutual exclusion is unavailable](/mechanisms/co
Simulate · Break itPython
4 more labs
Fencing tokens versus a paused leader (about 30 minutes)
Claim to test: a lease alone leaks at the boundary, and a fence checked by the resource closes the gap (What coordination forbids, item 3).
Break itPythonEscrow removes coordination
Measure the message count of a global budget with and without escrow ([Coordination avoidance, which is where this article should end](/mechanisms/coordination/how-it-works/deciding-without-observing/coordination-avoidance)).
SimulatePythonFan-out tail and power-of-two-choices (about 45 minutes)
Claim to test: at N = 100 and per-shard p99, about 63% of requests contain a p99 event, and two random choices beat greedy choice on stale load information (see Scatter-gather and Service discovery).
SimulatePython with NumPyHedging: when it helps and when it does not (about 40 minutes)
Claim to test: hedging at p95 turns a 1-in-20 slow tail into about 1-in-400 if slowness is attempt-local, and does nothing if it is request-local (see Hedging).
SimulatePython
Checkpoint interval optimum
A 30-day job with Poisson failures: sweep the checkpoint interval, find the optimum against √(2·C·MTBF), then get the MTBF wrong by 4×.
SimulatePythonRetry storm: no jitter versus full jitter
400 clients retry against a server that was down for 2 s: exponential backoff with no jitter, equal jitter and full jitter.
SimulatePythonThe catch-up condition
A 60-second outage at 500 arrivals per second, recovered at throughputs from 525 to 2,000: recovery time against utilisation.
SimulatePython
6 more labs
Replay determinism check
An aggregation run twice over the same events, with now() and a mutable lookup, then with event time and a versioned lookup: compare output hashes.
MeasurePythonDuplicates versus an idempotency key
A small payment service, 1,000 charges and 20% lost replies: a new key per attempt, an intent key with a stored outcome, and suppression with an error.
Real systemPythonDedup window size and the Bloom filter trap
The memory of exact deduplication for an hour and a week at 100,000 keys/s, then a Bloom filter alone and in front of an exact set.
MeasurePythonIdempotent sink with a conditional insert
Redelivered batches into SQLite: a plain insert duplicates, a keyed conditional insert does not, and check-then-insert races.
Break itPythonCommit ordering and the offset in the sink
A consumer adds 1..10,000 to a total and crashes 20 times: position saved before processing, after, and in the same transaction.
Break itPythonAuto-commit versus commit-after-process
Kafka auto-commit against commit-after-process, each killed once mid-run: silent loss against a bounded number of duplicates.
Real system · Break itDocker, Python with kafka-python
Measure retry amplification and a retry budget
A backend stalls for 3 seconds; clients time out and retry. Peak offered load with and without a 10% retry budget.
SimulatePythonFind the metastable band
A queue whose work per request triples when it is long: healthy and degraded states at the same load, and the shed-and-ramp exit.
SimulatePythonAvailability ceiling and the benefit of cells
30 hard dependencies at 0.999 against a^N, soft dependencies with a fallback, and a poison tenant with no cells, 10 cells and shuffle sharding.
SimulatePython
5 more labs
Bulkheads: one slow dependency, one shared pool
One pool of 10 threads shared by a dependency that hangs and one that is fast, against two pools of 5.
SimulatePythonGray failure: dead node versus slow node
A 200-node scatter-gather query with one dead node, one slow node, peer-relative ejection, an all-slow fleet and an eviction cap.
SimulatePythonAutoscaler flapping and the effect of dead time
An autoscaler targeting 60% utilisation with a provisioning delay of 0, 2 and 5 minutes, then with hysteresis and slow scale-down.
SimulatePythonRecovery time arithmetic and replay storms
Stop a queue for 60 s at ρ = 0.5, 0.8 and 0.9, time the drain, watch the catch-up overload a downstream stage, then drop stale items.
SimulatePythonA circuit breaker that protects the caller
A dependency that hangs for 30 s: without a breaker every thread blocks; with one the service answers fast and recovers.
SimulatePython
Resources & Cost
FIFO versus SRPT, on heavy-tailed and on steady job sizes
One server at 90% load, FIFO against preemptive SRPT, with heavy-tailed and with steady job sizes: mean, p99 and the largest jobs.
SimulatePythonHead-of-line blocking: one queue versus two
Four workers, one queue: four 5-second jobs in front of 200 tiny ones, then a separate pool, then the big jobs cut into chunks.
MeasurePythonStranded resources and DRF
How many (1 CPU, 16 GB) tasks fit a (32 CPU, 256 GB) machine; then dominant resource fairness for two tenants on 9 CPU and 18 GB.
SimulatePython
4 more labs
The power of two choices
100,000 tasks onto 100,000 machines: one random choice, the lesser loaded of two, and of three. The maximum load over five seeds.
SimulatePythonThroughput versus goodput past the knee
A service of 100/s receives 150/s and clients give up after 2 s: unbounded queue, bounded queue, and bounded queue with a deadline check.
SimulatePythonThe multiplexing dividend and its collapse under correlation
16 tenants with partly correlated demand: the 99th percentile of the summed demand against the sum of 99th percentiles, for ρ from 0 to 1.
SimulatePython with NumPyAn autoscaler that arrives too late
A reactive autoscaler with a 240-second delay against two 90-second bursts: unserved demand and idle capacity for 0% to 200% headroom.
SimulatePython
Price a workload from the metering definition, not the rate
Bill one workload three ways — quantity × rate, with a minimum increment, and batched — then compare effective prices.
SimulatePythonFind the small-file crossover on your own bucket
Read 1,000 small objects and one large one from an S3-compatible store: concurrency changes the time, never the request count.
Real systemDocker, PythonMeasure the top-1% share of your query log
How much of a warehouse bill the most expensive 1% of queries carry, for cost distributions of different tail weights.
SimulatePython with NumPy
3 more labs
Simulate the spot gang
A job that needs 100 spot nodes at once: its hourly survival, and how checkpointing sets its completion time.
SimulatePython with NumPyTest the commitment-sizing rule
Find the commitment level that minimises a year of cost under skewed demand, and how a falling price moves it.
SimulatePython with NumPyDry-run before you run
Estimate a query's scanned bytes from Parquet metadata four ways: every column, two columns, a partition filter, an uncorrelated filter.
MeasurePython with PyArrow
Time & Change
Measure your skew distribution and price δ
A million heavy-tailed arrival skews: p50 to p99.99, and for each used as the watermark delay, the share of late records and the extra wait.
MeasurePython with NumPyWatermark with an idle partition
Three partitions, one goes quiet for 10 minutes and then sends its backlog: window closing delay and late events with and without an idleness timeout.
SimulatePythonRetained-window memory
Tumbling windows of size W kept until the watermark passes their end plus allowed lateness L: windows held per key against 1 + L/W.
SimulatePython
6 more labs
Trigger mode versus sink
A window fires twice (3,100, then 3,260) in accumulating or discarding mode, into an overwriting sink or an additive counter.
SimulatePythonBitemporal table in SQLite or Postgres
A rate table with valid time and transaction time in SQLite: a correction, two as-of questions, and the same history with one updated_at column.
MeasurePythonEvent time versus availability time
Features with available_time = valid_time + 9 h: how many events get a different value when joined on availability, and what that does to a model.
MeasurePython with pandas, scikit-learnLeakage inflates the score (naive join versus as-of join)
20,000 deliveries with a drifting courier speed feature: a naive join on the latest feature value against an as-of join, scored offline and on a later period.
MeasurePython with pandas, scikit-learnThe daylight-saving key bug
02:30 on 26 October 2025 happens twice in Berlin: two instants, one local text. Then month arithmetic in two orders.
MeasurePythonReplay determinism test
A transformation that reads current_timestamp, run twice and "tomorrow"; then with a run_date parameter. Output hashes compared.
MeasurePython
Key-bounded versus time-bounded state
A per-user counter and a 600-second join buffer over the same stream at two rates, then the buffer without eviction.
SimulatePythonThe indivisible key
Hash partitioning over 8, 32 and 128 instances with one key carrying 30% of traffic; then salting it into 8 sub-keys.
SimulatePython with NumPySliding window with panes, and floating-point drift
A 24-hour sliding count refreshed every minute, kept per window and with 1-minute panes; then a float sliding sum by add-new, subtract-old.
MeasurePython
2 more labs
Aligned versus unaligned checkpoints under backpressure (optional, needs a local stream engine)
A model of barrier checkpoints behind a bounded buffer and a slowing sink: duration and size, aligned against unaligned.
SimulatePythonReplay determinism
An exactly-once loop with checkpoints, crashed at random and restarted 50 times: a pure computation and one that reads random.random(); then an upsert sink.
Break itPython
Find your own break-even point
Time a full rebuild against an incremental merge as the changed fraction grows, and find where they cross.
MeasurePython with DuckDBTrigger overhead
Time single-row and bulk inserts with and without a shadow-table trigger that records every change.
Real systemDocker, PythonWatch the log retention problem happen
Create a logical replication slot, never read it, and watch the database keep its write-ahead log until the slot is dropped.
Real systemDocker, Python
4 more labs
Aliasing in a polling pipeline
Poll a fast-changing row every few seconds and count how many of its changes, and its delete, the poller ever sees.
Break itPythonWhat query-based CDC misses
A poller on updated_at against three changes it cannot see: intermediate updates, a delete, and a slow commit.
Real system · Break itDocker, PythonSum versus max under deletes
Maintain SUM and MAX per group under inserts and deletes, and count the rescans and memory each needs.
SimulatePythonInject loss, then catch it
Drop 0.1% of the changes in a toy CDC pipeline, catch it with a count + checksum, and crash it under three cursor-commit orders.
Break itPython
Measure the per-file overhead
The same data as 800 files of 0.5 MB, 8 files and 1 file, scanned by DuckDB from local disk and from an S3-compatible store; F and b fitted.
Real systemDocker, Python with DuckDBZ-order versus sort: count the files touched
10,000 files over four columns, clustered by a sort on A and by a Z-order on A–D: files read by a filter on A and on D.
SimulatePython with NumPyOverlap depth before and after a blind compaction
200 small files with narrow key ranges in arrival order; overlap depth before and after compacting by time and by key range.
SimulatePython with NumPy
4 more labs
Retention window versus storage
A model of a table format: 10% rewritten daily for 10 days, storage after expiry and vacuum for 1-, 3- and 7-day windows; then a long query after a short vacuum.
SimulatePythonFind the square-root interval by simulation
A read penalty that grows by r per hour since the last compaction and a compaction cost C: the cheapest interval against √(2C/r), then with 4r.
SimulatePythonFind the compaction interval
Measure the scan cost per unmerged delta file and the cost of a rewrite, compute the compaction interval τ*, and compare a day at τ*/4, τ* and 4τ*.
MeasurePython with DuckDB, PyArrowVerify a rewrite
Compact 50 Parquet files into one sorted file; compare count and an order-independent fingerprint; then drop a row and replace a row with a duplicate.
Break itPython with DuckDB
Meaning
SCD Type 2: as-was against as-is, and a late arrival
Keep a customer's history with SCD type 2, answer as-was and as-is, place a late fact, and count the cost of a late correction.
SimulatePythonAverage of ratios against ratio of sums
Compute a margin rate as an average of daily ratios and as a ratio of sums, at day, month and year level.
SimulatePython with NumPyReproduce a fan trap and a chasm trap
Sum a measure across a join to a finer grain and across two independent branches, then fix both by aggregating first.
Break itPython
3 more labs
Cost of a dimension change: star against OBT
Rename one product category in a star and in one big table: one dimension row against a 20-million-row rewrite.
MeasurePython with DuckDBThe definition-variant experiment
Count "active customers in September" three reasonable ways and trace each difference to an unwritten edge case.
SimulatePython with DuckDBEntity resolution: blocking and the threshold
Find typo duplicates among 22,000 customers: blocking cuts the pairs to compare, and the threshold trades false merges for misses.
SimulatePython
Positional versus named CSV
Read a headerless CSV by position, insert a column in the middle, read it again; then add a header and read by name.
Break itPythonRename is drop plus add, in real Parquet files
Two Parquet files, one with weight and a newer one with weight_kg, read together with union_by_name; then COALESCE.
MeasurePython with DuckDBInference is a function of the sample
Infer a schema from the first 1,000, 18,000 and 20,000 records of a file whose postal_code turns from integer to string, in file order and shuffled.
MeasurePython
3 more labs
Build a tiny schema-diff gate
A compatibility classifier for schema changes and ten test cases, checked against the last version only and against every earlier version.
MeasurePythonReader-side default rewrites history
1,000 stored events without delivery_priority, counted under a reader default of STANDARD and then EXPRESS; then with the default written at write time.
MeasurePythonDead-letter rate threshold
100,000 records; a schema change makes 40% invalid halfway. A pure dead-letter queue against a 2% windowed halt threshold.
SimulatePython
Per-value checks ride on the parse
Time parsing a 5-million-row CSV alone, with checks in the same pass, and with a separate checking pass.
MeasurePythonThe false-alarm floor of a cross-table check
Two loaders on different schedules: the orphan-order rate a referential check reports, and how allowed lateness removes it.
SimulatePythonP-values measure your row count
A 0.07% shift tested with KS at 10³ to 10⁷ rows: the p-value collapses with n, the PSI does not.
SimulatePython with NumPy, SciPy
3 more labs
Alert fatigue by arithmetic
12,000 checks at a 0.001 false-alarm rate for 90 days, with and without a Bonferroni correction, against weekly real defects.
SimulatePython with NumPy, SciPyHow many rows must you sample, and what does blocking cost?
How large a sample finds a rare defect, against 1 − (1 − p)ⁿ and n ≈ 3/p, and how blocking caps the recall of duplicate search.
SimulatePython with NumPyFind five bad rows among a million with a Merkle tree
Find 5 changed rows among a million with a Merkle tree, then see why a XOR table hash cannot see duplicated rows.
SimulatePython
Planning time versus partition count
Read the metadata of 1,000 to 1,000,000 partitions from SQLite, with and without a filter, and size the planning slots with Little's Law.
MeasurePythonA file in the wrong folder gives wrong answers, silently
Copy one day's Parquet file into the next day's Hive folder and read the table; then store the date inside the file and count mismatches.
Break itPython with DuckDBThe pointer swap: two writers, one winner
Two writers commit table versions through a pointer: with an exclusive create (compare-and-swap), then with a plain write.
Break itPython
3 more labs
Read the statistics inside a Parquet file
Read Parquet footer statistics with parquet_metadata: files skippable for a one-hour range, sorted against random; merge two files' statistics by hand.
MeasurePython with DuckDBLineage coverage: static parse against runtime log
Lineage for 20 small jobs by parsing their SQL and by recording what DuckDB actually scanned: edges found by each.
MeasurePython with DuckDB, sqlglotThe ranking feedback loop
52 weeks of catalog search where most users take the top result: does the better table ever win, and what certification bonus does it take?
SimulatePython
Operations
The sensor deadlock
Twenty sensors hold the slots of a 10-slot worker pool while they wait for processing tasks that need a slot.
Break itPythonFind the cost of ordering
60 one-second intervals run sequentially, on 4 workers, and on 4 workers with depends_on_past; then interval 10 fails.
MeasurePythonIdempotent versus append retries
A daily load crashes halfway and is retried: append with and without a transaction, overwrite of the partition, and overwrite with non-deterministic values.
Break itPython
3 more labs
Check that your dead-man's switch really fires
A scheduler that hangs: a heartbeat checker outside it against the scheduler's own SLA alert.
Break itPythonFan-out as a batch-size decision
Launch overhead of a subprocess and of a container, the useful share of tasks from 1 s to 600 s, and 1,000 tiny tasks one by one against in 10 batches.
MeasurePython, DockerReplay a durable log
A 40-line durable-execution runtime: kill a workflow after two activities, replay it from its log, then branch on the clock and watch the replay diverge.
Break itPython
Counter vs. gauge under lost scrapes
A counter and a spiky gauge scraped every 15 s and 60 s with 30% of scrapes lost, over 200 simulated hours.
SimulatePythonMerge histograms vs. average p99
Ten servers with very different traffic: average of their p99s, the true p99, and the p99 from merged histogram buckets.
SimulatePython with NumPyCount your series
A prometheus_client endpoint: series per metric against label cardinalities, then one label with 10,000 values added.
MeasurePython with prometheus_client
3 more labs
Head vs. tail sampling
100,000 traces, 1% errors: error traces kept by 1% head sampling and by tail sampling, and the tail sampler's buffer at 1 s and 30 s traces.
SimulatePythonFind the false green in your own pipeline
An hourly job starts receiving empty files: job-status alert, freshness check and volume band, and how long each takes to notice.
SimulatePythonBase-rate simulation of alerting
A million checks with a 1-in-1,000 incident rate and a perfect-recall detector at four false-positive rates: pages and precision.
SimulatePython
Measure the cost of encryption on columnar scans
A 1% range query on 20 million Parquet rows: sorted ts, ts as a random permutation, and ts encrypted with AES and decrypted at query time.
MeasurePython with PyArrow, DuckDB and cryptographyEnvelope encryption and crypto-shredding in 40 lines
Encrypt 5 patients with their own data keys wrapped by a master key, time 100 MB of AES-GCM, shred one patient, then restore a backup.
MeasurePython with cryptographyShow that a masked column is still searchable
Recover the three hidden digits of a masked 6-digit ID with prefix count queries, then reject predicates on the masked column.
Break itPython (SQLite)
5 more labs
Crack a hashed phone number in seconds
Hash every 7-digit number with SHA-256, recover a hashed number by lookup, then try again with a per-record salt.
Break itPythonRe-identify a "5-anonymous" table and measure the uniqueness of quasi-identifiers
100,000 fake people: how many are unique on birth date, postcode and sex, and what k two 5-anonymous releases reach once joined.
Break itPython with pandasDifferencing attack and the Laplace mechanism
Recover one salary from two exact sums, then add Laplace noise to counts and measure the error at true counts 5, 100 and 1,000,000.
Break itPython with NumPyA privacy budget ledger
A noisy count that charges epsilon to a budget of 1.0: how many of 15 queries are answered, and what averaging repeats costs.
SimulatePython with NumPyA result cache that leaks, and the fix
A result cache keyed only by query text serves north rows to a south user; keying by identity and policy fixes it and removes the timing signal.
Break itPython
Special Shapes & Boundaries
Why `LIKE '%word%'` cannot use a B-tree, and what an inverted index does instead
A million Zipfian documents in PostgreSQL: LIKE '%word%' with a B-tree, then a GIN full-text index for a rare and for the most common word.
Real systemDocker, PythonBlock-max skipping on a toy engine
Postings for 50,000 documents and 5 Zipfian terms: exhaustive top-10 scoring against block-max skipping, postings read, and where skipping stops being safe.
SimulatePython with NumPyThe memory-bound arithmetic of brute-force vector search
400,000 random 768-dimensional vectors: one brute-force query as float32, float16 and int8, effective bandwidth, and float32 scaled to 10 million vectors.
MeasurePython with NumPy
5 more labs
Measure recall against brute-force truth, and see the filtered-search collapse
An HNSW index over 200,000 random vectors: recall@10 and queries per second for efSearch 10 to 200; then a 0.5% filter, post-filtered and pre-filtered.
MeasurePython with hnswlib, NumPyThree self-joins versus a frontier expansion
A power-law graph of a million vertices: 1- to 4-hop chained self-joins in PostgreSQL against a CSR breadth-first search, from a median vertex and from the top hub.
Real systemDocker, Python with NumPySupersteps and the straggler tax
10 PageRank supersteps over a power-law graph split across 8 workers by vertex and by edge, with and without a combiner; then PageRank iterations against connected components.
SimulatePython with NumPyFilter and refine, and the fill ratio
100,000 circles and buffered diagonal lines: bounding-box false positives by fill ratio, STRtree filter-and-refine against brute force, and the raw-degree distance trap.
MeasurePython with shapely, pyprojCardinality wall and Gorilla encoding
Series counts with and without a user_id label; delta-of-delta and XOR encoding of a million points, regular and noisy; minute rollups of an hour's p99.
MeasurePython with NumPy, datasketches
Splittability: CSV with quoted newlines versus NDJSON
Split a CSV with quoted newlines and an NDJSON file at random byte offsets and count the records each split breaks.
Break itPythonThe footer pays for column skipping
A 200-column Parquet file: read 3 columns against all 200, read the footer statistics, then append bytes after the footer.
MeasurePython with PyArrowThe six interop hazards
Write values one way and read them another: encoding, decimals as floats, null/empty/missing, zoneless times, int32 overflow, collation.
Break itPython with NumPy
3 more labs
Idempotency: a webhook that retries
A charging server that is sometimes slow and a client that retries on timeout: charges with and without an Idempotency-Key.
Real systemPythonOffset versus keyset pagination, under writes
Page through a million SQLite rows with OFFSET and with a keyset predicate: time at page 10,000, and duplicates after one insert.
MeasurePythonFile drop with manifest as commit
Publish file batches with stage, checksum, atomic rename and manifest last; break them three ways; then delete a batch with no schedule.
Break itPython
Method
Run your own config dump, seed sweep and scale ladder
One command: dump the configuration, generate data from a seed, sweep 1x to 30x, write raw results; random lookups into 8 to 240 MB, five seeds.
MeasurePython with NumPyClosed-loop vs. open-loop, and coordinated omission (45 minutes)
A server slows from 40 ms to 400 ms under a closed-loop and an open-loop client; then a one-second freeze measured with and without intended-time correction.
MeasurePythonKill a node and measure the recovery
Three instances behind a health-checked proxy; kill the primary under steady load, three times, against the hypothesis "below 1% errors within 10 s".
Break itPython
7 more labs
Mean, percentile averaging and fan-out in 20 lines (30 minutes)
One million latencies, 1% of them slow: the mean, the percentiles, an average of shard p99s, and a fan-out of 100 calls.
SimulatePython with NumPyWall time minus CPU time
10,000 small writes with an fsync after each: wall time against CPU time, then one flush per 100 writes; with fsync and with macOS F_FULLFSYNC.
MeasurePythonMeasure your noise floor and your winner's curse (45 minutes)
An A/A spread over 10 identical runs, a "tuning sweep" of 30 identical configurations, and a blocked against interleaved A/B under background load.
MeasurePython with NumPyA micro-benchmark that disappears, and a sampling blind spot
A billion x*x computed and discarded, then summed; and a 2 ms sleep once a second profiled at 99 Hz and 999 Hz.
MeasureC compiler, PythonDifferential profile of two builds
Profile a JSON-parse-and-insert loop before and after adding a lock, diff the profiles frame by frame, then repeat with one side under extra load.
MeasurePythonCatch a benchmark that did no work (30 minutes)
A C loop that "writes" a value 100 million times, with the result unused and then observable, against the ceiling for durable writes.
MeasureC compiler, PythonRandom split versus group split versus hash split
50,000 rows for 500 couriers with personal biases: a random split, GroupKFold by courier, and a hash split; then 10% more rows and how many test rows move.
MeasurePython with scikit-learn