- 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) |
||
|---|---|---|
| .. | ||
| main.wo | ||
| README.md | ||
| wo.toml | ||
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.