library · how it got fast

one sync for many writers.

How group commit took durable writes from 718 to 5,379 per second.

4 min read · 927 words · commit fd1b95c · filed as “Group commit for durable shared writes”

Design: SharedKiban::put_durable, delete_durable and write_durable queue behind a leader (src/group_commit.rs). The leader appends every queued WAL record, issues one fdatasync, publishes the writes to the memtables in queue order under one mirror guard, then wakes the followers.

Nothing is visible or acknowledged before the sync succeeds. A failed append or sync poisons the engine and fails the whole group. A lone writer leads its own group immediately, with no added delay. The &mut Kiban API is unchanged.

16 writerseach asks for durabilityone queuea leader takes the groupone fdatasync480 syncs, not 4,000then visiblenothing shows before the sync
fig. 01one sync, many writersSixteen writers queue behind a leader, which appends every record and issues one fdatasync. 4,000 durable writes took 480 syncs.

Method

tools/agentbench/readpath/src/bin/group-commit-bench.rs. T writer threads each insert fresh JSON memories (the agent-memory model, seed 20261019), one acknowledged-durable write at a time, into a 10k-record base database. Each run does 4,000 writes in total (4000/T per thread, at least 250). Every run closes and reopens the database and verifies every record byte for byte (all 100 runs verified).

  • Kiban sync: put followed by sync(), which is today's durable path on SharedKiban. Kiban group: put_durable.
  • SQLite: bundled SQLite, WAL, synchronous=FULL, one connection behind a mutex.
  • RocksDB: 11.8.1, WriteOptions::sync=true, with its own write groups.
  • LMDB: not in the matrix. LMDBLMDBA widely used embedded database built on a B+tree and memory-mapped files, known for very fast reads. allows one write transaction at a time, so it cannot scale with writers. Its measured single-writer durable write was 1,212 µs (about 825 writes/s) in #16/#17. The harness's LMDB backend could not reopen its environment for the verification pass and was dropped rather than reported unverified.
  • fdatasync counts come from an LD_PRELOAD probe (group_commit_probe.c) counting fdatasync, fsync, msync and sync_file_range; the table sums fdatasync + fsync.
  • Timed processes pinned taskset -c 0,1,2,5. Five alternating trials, order flipped per trial and thread count, database copied fresh for each run. Medians below. Raw results are in results/, and the driver is run_matrix.py.

Results (median of five trials)

writes per second, every one synced

1 writer1 of 5
kiban1,655p99 2.0 ms · 4,000 syncs
RocksDB1,788p99 1.6 ms · 4,000 syncs
SQLite1,399p99 2.1 ms · 4,044 syncs
kiban, ungrouped1,754p99 1.5 ms · 4,000 syncs
WritersEngineWrites/sp50 µsp99 µsSyncs per 4,000 writes
1Kiban sync6711,3016,7564,000
1Kiban group7271,2993,1304,000
1SQLite6461,4904,3024,044
1RocksDB6781,4033,5094,000
2Kiban sync7891,72918,7994,000
2Kiban group1,0821,6874,1502,759
2RocksDB1,0421,6914,8992,508
4Kiban sync7781,71255,0194,000
4Kiban group1,7302,0515,1941,641
4RocksDB1,9281,8544,6501,391
8Kiban sync7591,797112,2564,000
8Kiban group3,1032,1115,531911
8RocksDB3,0702,5124,508879
16Kiban sync7181,860200,0984,000
16Kiban group5,3792,28412,897477
16SQLite6271,537621,9334,032
16RocksDB5,5952,7675,312479

(SQLite is flat at ~640 writes/s from 1 to 16 writers, with its p99p9999% of operations were faster than this. It shows how bad the slow cases get. growing to 622 ms as writers queue on one connection. Its 2/4/8-writer rows are in results/.)

What the numbers say

  • Throughput scales with writers. Kiban group goes 727 → 1,082 → 1,730 → 3,103 → 5,379 writes/s. Per-write p50p50The median: half of all operations were faster than this. grows only from 1.3 to 2.3 ms, and the fsyncfsyncForces a file and its metadata onto the disk. count falls from 4,000 to 477 (about 8.4 writes per sync at 16 writers).
  • The tail collapses. Without grouping, p99 at 16 writers is 200 ms, because each writer queues for its own sync. With grouping it is 12.9 ms.
  • Parity with RocksDB, not a win. RocksDB is 11% ahead at 4 writers and 4% ahead at 16; Kiban is 4% ahead at 2 writers and 1% ahead at 8. All of these are within trial variation. RocksDB's p99 is lower at 16 writers (5.3 ms vs 12.9 ms). Kiban group matches but does not beat RocksDB's write groups.
  • Single-writer latency is unchanged, as designed.

Correctness evidence

Tests (src/group_commit_tests.rs, about 760 lines) cover grouping and wakeup, panic safety, failed-sync sweeps, batch atomicity, and seeded concurrent crashes at every group stage (before append, after append before sync, after sync before acknowledgement, during memtablememtableThe in-memory table that takes new writes before they are flushed to an SST. publication). A loom model covers the queue handoff. Gate results are in gates/ (see below). A mutation check that removed the sync-before-publish ordering was caught by the tests and reverted.

Limits

  • This is a host with one NVMe behind LUKS, where an fdatasyncfdatasyncThe system call that forces a file's data onto the disk. Slow, but it is what makes a write survive power loss. costs about 1.3 ms. Faster devices change the absolute numbers and the group sizes.
  • Single process, JSON values of ~250 bytes, 10k-record base.
  • Only the shared API is grouped. The single-threaded &mut Kiban path is unchanged.
  • Writers are saturating (no think time). Open-loop arrival rates were not measured.