Skip to the document
Madhuopen lab

HanuDB, tuned while I slept · part 1 of 6

adopted14 September 2026cost: local CPU; a free-tier VM for the real runs

Writes: the log, group commit and the disk

What one fsync, one core and a few dozen IOPS allow

The question

Of 37 attempts in this theme, which ones survived two benchmark runs, the correctness suite and the gates?

adopted

With two durability watermarks, every "how far are we" number must follow the smaller one. After 0091 the current.log staleness floor (meta.next_txn) followed the index watermark, so after a SIGKILL the records the kv had not flushed yet were dropped as…

Attempts37
Kept13
Discarded or crashed13
Best kept score40,116.4

Every run in this theme

Every run in this theme
#scorewrites/sreads/sverdictwhat changed
00019,046.88,7479,357keepbaseline: fjall + tantivy(t,k,all) + WAL group commit, flush 1s, commit 500ms
00038,784.28,3479,244discardseal segment outside the wal lock (spawn_blocking zstd+fsync): writes -13% both runs (8784 / 8972); on 1 CPU the extra thread only competes
000612,273.325,4215,926keepHARNESS: bench client now runs inside the VM on the server loopback (cores 2-5); reference run of the 0005 code. Not comparable with earlier scores.
001213,3558,853crashINVALID (ran on the 0001 code by mistake: keep.sh had silently never updated .baseline, discard.sh reset the tree): doc store Compressor::None + 4 KB blocks; re-test…
002020,581.829,16214,526discarddoc store Compressor::None + 4 KB blocks (valid re-test): +1.1% (noise); LZ4 decode of the id block is not a bottleneck in the container
002621,998.216,27629,732discardfsync group-commit window 200 us: writes -44% (tokio sleep granularity is ~1 ms, so the window adds a full ms of latency; writes are latency-bound with 32 clients).…
002926,895.224,42329,617keepfjall docs memtable 64 -> 16 MB: peak RSS 368 -> 221 MB; +3.6% vs the baseline re-run under the same host load (calibration 664 vs 687; quiet-host baseline was 27031)
003026,773.423,97629,898keepget returns fjall's slice zero-copy (Bytes::from_owner): score unchanged (-0.5%, noise, calibration 675); kept as a simplification
0033b6,170.94,1889,093infoREAL e2-micro hanudb-1 (client on the VM, real GCS): writes fsync-bound on pd-standard (p50 6.9 ms), 41922 docs rebuilt from GCS in 2.6 s, 0 mismatches
003426,807.427,70625,938discardTCP_NODELAY on accepted sockets (ListenerExt::tap_io): no change (loopback client); not simpler
004033,163.429,62037,131discardhand-rolled hyper http1 loop (pipeline_flush, writev, nodelay): runs 33163 / 32515, mean 32.8k vs baseline mean 33.1k: no gain, more code
004231,886.726,41938,486discardbatched WAL writes (records buffered, one write syscall per group commit): SIGKILL durability OK but runs 31887 / 36001 (mean 33.9k) vs baseline 34938 / 35619 (mean…
004636,931.329,26346,609keepWAL file preallocated + zero-filled (positional writes never change the file size, fdatasync flushes data only; lenient log decode stops at zeros/torn/txn gap): writes…
0046b3,69910,677infoREAL e2-micro twin mode with the preallocated WAL: write p50 8.1 -> 5.4 ms (3.7k/s); write p99 85 ms = snapshot staging under the WAL lock (next experiment)
0047b5,5437,472infoREAL e2-micro with 0047: write p99 85 -> 21.6 ms, 5.5k writes/s (p50 4.6 ms)
005132,71829,95135,740discardcommit interval 500 -> 250 ms: runs 32718 / 30385 (mean 31.6k vs ~35k). 500 ms stays
0059binfoREAL e2-micro: aggregation (terms + stats) over 41922 docs in 64 ms on the VM
006236,969.829,09746,973discardskip JSON re-serialisation when the document carries _id: runs 36970 / 37107 (mean 37.0k = baseline); writes are fsync-bound, freed CPU changes nothing; not simpler
0061binfoREAL e2-micro rolled to 0061 (idempotency keys + TTL, aggregations, dump, compact); status OK
0065c4,3861,575infoREAL e2-micro after cooldown: writes back to 4.4k/s (burst credit recovered, steal negligible) but reads 1.6k/s p99 232 ms with 65% iowait: 30 GB pd-standard = a few…
006733,010.422,83947,711discardfallocate instead of zero-fill for the WAL: disk writes 4.54 -> 3.36 MB per 1k docs (-26%) but writes 28k -> 22.8k/s (p50 0.69 -> 0.95 ms): ext4 converts unwritten…
006836,484.528,37946,904keepWAL file reused from offset 0 after each seal (no truncate, no re-zeroing; recovery ignores records with txn below the sealed segments' next txn): disk writes 4.54 ->…
0068b37,001.629,16946,938infodisk attribution after a 584k-doc run: written 2229 MB; on disk after rebuild index=105 MB kv=315 MB seg=1 current.log=5 bucket=77 MB. fjall (journal+flush+compaction)…
006936,401.928,48246,524keepfjall Leveled compaction with l0_threshold 8 and 128 MB table target: disk writes 3.84 -> 3.35 MB per 1k docs (-12.5%), score neutral (33265 / 36402), get p50 unchanged.…
007036,62128,76746,619discardfjall journal LZ4 compression: disk writes 3.34/3.32 vs 3.35 MB per 1k docs = no change; score neutral
0070binfodisk-write attribution: no merges 3.11 MB/1k (merges ~0.24), commit 60 s 3.29 (tantivy flushes happen anyway); tantivy total ~0.4, WAL+local segment+bucket copy ~0.7,…
007135,789.328,92044,291keepfjall memtable 16 -> 64 MB (knob --kv-memtable-mb, default 64): disk writes 3.35 -> 2.65 MB per 1k docs (-21%, fewer flushes/compactions), score neutral (34526 / 35789),…
0071b4,4502,410infoREAL e2-micro (0071 build): 30 s at 4450 writes/s wrote 489 MB to disk = 3.66 MB per 1k docs = ~16 MB/s, above what a 30 GB pd-standard sustains -> bursts only, as…
0072keepHARNESS: bench client keeps every txn (8 B) and a 1-in-64 document sample instead of every document; ids regenerated as w{client}-{k}. Same requests, same gates; long…
007433,135.828,31438,779keepSAFETY: snapshot packing fdatasyncs every 32 MB so a multi-GB pack never holds more dirty pages than that in the cgroup. Bench unchanged; soak with growth 0.25 re-run…
0078b30,182.327,02433,710infovalid regression run of 0078 after killing the stray soak containers (calibration 564)
0087cinfoROOT CAUSE of every rebuild OOM: segments-only rebuild of 3.8M docs logged wb_mb=88 -> 317 -> 542 with sealed=1 (rebuild progress line every 50 segments). fjall rotates…
008836,585.233,52440,077keepSAFETY+SPEED: fjall worker_threads(2) (default was min(cores,4) = 1: a compaction blocked every memtable rotation and flush behind it, so the active memtable grew…
008935,240.733,72936,827discardCPU: hand the HTTP handler's parsed serde_json::Value to the indexer (Arc in the Op / IndexMsg) so the indexer does not re-parse the document. Writes 33.1k / 34.3k vs…
009140,116.434,82146,218keepARCH: the document store uses lsm_tree directly (src/kv.rs) instead of fjall: no second write-ahead journal (HanuDB's WAL already is one), our own flush + compaction…
009935,065.735,08635,045discardWRITES: a precise group-commit window (spin on the blocking thread, 150 / 300 us) before each fdatasync: writes 35.2k / 35.0k (unchanged), p99 6.0 ms (was 4.7).…
010735,469.435,27435,674keepDISK: --kv-compress warm ([None, Lz4]: only the youngest level uncompressed). On the bench it equals cold (kv 299-357 MB, disk writes 1436-1469 MB): with l0_threshold 8…
hanudbwritesautonomous loop