writeonce/docs/examples/residency
shoney.arickathil 516bd8362d docs(db2-keys): a runnable example for per-table storage
- docs/examples/residency: one program, two tables filled by the same
  loop, differing only in the annotation. Run twice against one WO_DATA
  and orders replay while sessions do not
- the example checks its own claim (exits 1 if a durable:false table
  survives, or a durable:true one fails to replay) rather than narrating
  it in a print
- resident: keys is written out as a commented block with the loader's
  exact refusal, so the frontier is visible in the example rather than
  only in a story. It documents WHERE the refusal happens: woc compiles
  it and emits a .wob; wovm exits 2, because the annotation is a
  load-time property
- residency-accept gains two legs: the example runs and its restart
  claim holds, and the refusal message the README quotes is checked so
  doc and code cannot drift apart
- the gate writes the example's output to /tmp/residency.log,
  banner-separated, for tail -F
- README commands verified verbatim; they needed mkdir -p because wovm
  will not create WO_DATA

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit c9c7e03e62c3918cb65ef7994d1e33a0c5337b71)
2026-08-30 20:37:27 +02:00
..
main.wo docs(db2-keys): a runnable example for per-table storage 2026-08-30 20:37:27 +02:00
README.md docs(db2-keys): a runnable example for per-table storage 2026-08-30 20:37:27 +02:00
wo.toml docs(db2-keys): a runnable example for per-table storage 2026-08-30 20:37:27 +02:00

residency — per-table storage

What databasev2 2 added: durable and resident, declared per @table instead of one environment variable for the whole process.

Before it, WO_DATA was the only switch. Set, and every table is WAL-logged; unset, and none are. Applications are not uniform — a session table is disposable, an orders table is precious, and a 120 GB audit table does not fit in RAM at all. One global switch forces "everything is precious" or "nothing is", and you pay for whichever is wrong.

Run it

just residency

The gate writes the example's own output to /tmp/residency.log, banner- separated, so you can tail -F it while it runs.

Or by hand, which is the whole demonstration — the same program twice against one data directory:

compiler/_build/default/bin/woc --emit docs/examples/residency/main.wo -o /tmp/residency.wob
mkdir -p /tmp/residency-data          # WO_DATA must exist; wovm will not create it
WO_DATA=/tmp/residency-data runtime/wovm /tmp/residency.wob seed
WO_DATA=/tmp/residency-data runtime/wovm /tmp/residency.wob
seeded: orders=3 sessions=3
after restart: orders=3 sessions=0
ok: durable replayed, volatile did not

The second run inserts nothing. Three orders come back from the log; zero sessions do, because they were never written to it. Both tables were filled by the same loop — only the annotation differs, so the difference after the restart is the annotation's doing and nothing else's.

The three modes

Declaration Meaning State today
durable: true (default) WAL-logged, replayed at boot ✅ works
durable: false never written to the log; costs no disk and no fsync; empty after a restart ✅ works
resident: all (default) every row's payload lives in RAM ✅ works
resident: keys the id map stays resident, the payload lives in the WAL and is read back by offset ⛔ refused at load

Why resident: keys is refused

It is the mode the track exists for — a table larger than RAM. Storage, the read paths, deletes and checkpoint survival all work. Updating such a row does not: the row has no slab slot to mutate, so a write would land in a scratch buffer and be discarded silently. Doing it properly is read-modify-append.

So the loader refuses the annotation rather than honouring it in name only. Uncomment the AuditEntry block in main.wo and you get:

wovm: class 0 declares `resident: keys`, which is INCOMPLETE: rows are stored
and read keys-only, but UPDATING one is not implemented (it needs
read-modify-append). Remove it until databasev2 2 lands updates;
`resident: all` is what runs

Note where that comes from: woc compiles it happily and emits a .wob. The annotation is a load-time property, so the compiler is green and wovm exits 2.

Refusing at load rather than at the first update is deliberate. A developer who declared a 120 GB table keys-resident, saw it compile, and shipped would find the gap in production. That judgement earned its keep in a way nobody had written down: an audit before relaxing the refusal found that delete on such a table was reading a WAL byte offset as a slab index and freeing whatever it landed on — memory corruption, not a missing feature. It is fixed and pinned by a test that SEGVs against the old code, but the refusal is what stood in front of it.

What this example does NOT show

The mode-mismatch startup refusal, the zero-WAL-bytes measurement, and the two compile-time refusals are proven by scripts/residency-accept.sh against purpose-built snippets, because each needs a deliberately broken program or a byte-level assertion on the log file. This example is the readable half.