docs(db2-7): contract + CODE-LOGIC — the WO_DATA file form
- docs/plan/oop-vm/04-db-binding.md, WAL section, "Where the log lives": WO_DATA is always a path; directory form (existing dir or trailing `/` → `<dir>/shard-0.wal`, pre-7 bytes incl. the `//`), file form (the path IS the log, created only under an existing parent), the two refusal lines verbatim, too-long refused not truncated, one file at any core count, compaction/migration temps + parent fsync derived from the log path never from WO_DATA, the two pinning tests named. - database/src/CODE-LOGIC.md, `wal.c — durability`: the resolver's three codes and main.c's wording, why no mkdir -p, trailing slash on a missing dir kept as the pre-7 `cannot open` on purpose, `parent_dir_of` shared by the boot check and the post-rename fsync. - Both paragraphs sit in regions untouched by the uncommitted 6a/12 doc work in the same files; no other docs touched (story/board are pm's). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit f1985bae5d110ed773159393bd82c32b73bfb672)
This commit is contained in:
parent
587991124d
commit
ea66761c4e
2 changed files with 53 additions and 0 deletions
|
|
@ -58,6 +58,28 @@ tear). The crash battery in `runtime/test/test_wal.c` is the module's
|
||||||
meaning proven: acked-over-a-pipe after commit, SIGKILL mid-stream, replay,
|
meaning proven: acked-over-a-pipe after commit, SIGKILL mid-stream, replay,
|
||||||
zero acked-but-missing.
|
zero acked-but-missing.
|
||||||
|
|
||||||
|
**Where the log lives (databasev2 7, 2026-09-10).** `wo_wal_resolve_data_path`
|
||||||
|
turns `WO_DATA` into the log path before main.c opens anything: an existing
|
||||||
|
directory or a trailing `/` → `<dir>/shard-0.wal` byte for byte (the pre-7
|
||||||
|
form, `//` after a trailing slash included); anything else IS the log —
|
||||||
|
opened if a regular file, created by `wo_wal_open` if absent. Two refusals,
|
||||||
|
exit 2, one stderr line each, worded in main.c from the resolver's codes:
|
||||||
|
`WO_WAL_PATH_NO_PARENT` (the parent comes back in `out`, so the line names
|
||||||
|
the path AND the parent; no `mkdir -p` — a typo must not plant a store
|
||||||
|
somewhere unexpected, the operator creates directories, the runtime never
|
||||||
|
does) and `WO_WAL_PATH_NOT_A_FILE` (fifo, socket, device).
|
||||||
|
`WO_WAL_PATH_TOO_LONG` refuses what the old 512-byte `snprintf` silently
|
||||||
|
truncated. A trailing slash on a MISSING directory is still the directory
|
||||||
|
form and still fails at `wo_wal_open` (`cannot open`), unchanged on purpose.
|
||||||
|
Nothing below main.c knows which form was used: compaction and migration
|
||||||
|
build `<log path>.compact` and fsync `parent_dir_of(log path)` — the same
|
||||||
|
static helper the resolver's parent check uses, so the directory checked at
|
||||||
|
boot is the directory synced after every rename. Tests:
|
||||||
|
`test_resolve_data_path` (every arm of the rule, fifo via `mkfifo`) and
|
||||||
|
`test_file_form_temps_beside_log` (a directory planted at `<file>.compact`
|
||||||
|
makes compaction and migration refuse with the log untouched; removed, both
|
||||||
|
succeed and the file is the only artifact beside a decoy sibling directory).
|
||||||
|
|
||||||
## db.c — statement executors (iteration 9, Task 3)
|
## db.c — statement executors (iteration 9, Task 3)
|
||||||
|
|
||||||
One dispatcher, the builtin contract (0 ok, else WO_T_* + msg). The engine
|
One dispatcher, the builtin contract (0 ok, else WO_T_* + msg). The engine
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,37 @@ intact record count and prefix end — the crash battery's verifier
|
||||||
(`runtime/test/test_wal.c`: five rounds of insert/commit/ack-over-pipe with
|
(`runtime/test/test_wal.c`: five rounds of insert/commit/ack-over-pipe with
|
||||||
SIGKILL mid-stream; every acked row present and exact after replay).
|
SIGKILL mid-stream; every acked row present and exact after replay).
|
||||||
|
|
||||||
|
**Where the log lives (databasev2 7, 2026-09-10).** `WO_DATA` is always a
|
||||||
|
path, never a sentinel (ephemerality is `WO_EPHEMERAL=1`, databasev2 2 task
|
||||||
|
6a), and it names the store in one of two forms, resolved by
|
||||||
|
`wo_wal_resolve_data_path` (`wal.{c,h}`) before anything is opened:
|
||||||
|
|
||||||
|
- **Directory form** — an existing directory, or any path ending in `/`: the
|
||||||
|
log is `<dir>/shard-0.wal`, byte for byte what every deployment and gate
|
||||||
|
before this iteration used (a trailing slash still yields the `//` the
|
||||||
|
pre-7 driver produced, and a trailing slash on a missing directory still
|
||||||
|
fails at open: `wovm: cannot open <dir>//shard-0.wal`, exit 2).
|
||||||
|
- **File form** — anything else: the path IS the log (`app.db`, `store.wo.db`
|
||||||
|
— the name is the operator's). An existing regular file is opened; an
|
||||||
|
absent path is created by `wo_wal_open`, but only when its parent directory
|
||||||
|
already exists. Two refusals, each exit 2 and ONE stderr line: a parent
|
||||||
|
that is not an existing directory — `wovm: WO_DATA=<path> — its parent
|
||||||
|
<parent> is not an existing directory; create it first (wovm never runs
|
||||||
|
mkdir -p).` — because a typo must not plant a store somewhere unexpected;
|
||||||
|
and a path that exists but is neither a regular file nor a directory
|
||||||
|
(fifo, socket, device). A result longer than the driver's path buffer is
|
||||||
|
refused as well, never truncated.
|
||||||
|
|
||||||
|
One file is the whole store at any core count (shard 0 is the only WAL
|
||||||
|
writer since arc stage 3). Compaction (databasev2 3) and schema migration
|
||||||
|
(databasev2 12) rewrite through `<log path>.compact` beside the log and
|
||||||
|
fsync the log's parent after the `rename` — both derive that from the log
|
||||||
|
path, never from `WO_DATA`, so the file form inherits their crash safety
|
||||||
|
unchanged; the boot-time parent check and the post-rename fsync share one
|
||||||
|
derivation (`parent_dir_of`). `WO_EPHEMERAL` set together with either form
|
||||||
|
refuses exactly as 6a says. Pinned by `runtime/test/test_wal.c`
|
||||||
|
`test_resolve_data_path` and `test_file_form_temps_beside_log`.
|
||||||
|
|
||||||
## Insert (Task 3) — builtin 61, `database/src/db.c`
|
## Insert (Task 3) — builtin 61, `database/src/db.c`
|
||||||
|
|
||||||
`insert Class { field: expr, … }` is a typed expression (statement position
|
`insert Class { field: expr, … }` is a typed expression (statement position
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue