feat(db): the inline path takes the fatal rule, asymmetry documented — T3
databasev2 4 part A, task 3. Looks like a no-op; it is not — without it the two write paths would disagree about what a failure means, which is the unevenness the spec exists to remove. - inline path (a statement already on shard 0) keeps its own barrier, batch size 1. It cannot hold a reply: it returns into its OWN fiber rather than unparking a requester, so batching it would need that fiber parked on the barrier — part B's machinery, deliberately out of part A - the comment says so, and says why not to "fix" it, because the next reader will otherwise see an inconsistency and delete the commit - the ordering assumption is written down: committing here is safe only because the drain commits unconditionally whenever anything is staged, so the buffer is empty when this runs. If that stops holding, this commit would make another statement's record durable early and ack it to the wrong writer - staging and commit failures are fatal here too. The update arm's old comment admitted what it did — "RAM ahead of disk: trap, do not ack" — and that is now gone WO_T_IO no longer appears anywhere in db.c: the write path cannot be caught. Language-visible, and task 6 records it in the error catalogue. Verified: - just wovm-test: 36 suites 0 fail, cli_smoke OK - WO_SHARDS=1 db-bench-quick: 85 checks 0 failures, crash.s1.0 800 acked rows present after kill -9 — the configuration that takes this path exclusively - default shards: 85 checks 0 failures Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
76d80cc027
commit
9fc439dd47
1 changed files with 25 additions and 17 deletions
|
|
@ -25,15 +25,25 @@ int wo_builtin_db(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) {
|
||||||
: WO_T_DB;
|
: WO_T_DB;
|
||||||
wo_wal *w = (wo_wal *)vm->rt.wal;
|
wo_wal *w = (wo_wal *)vm->rt.wal;
|
||||||
if (w) {
|
if (w) {
|
||||||
/* RAM applied, record staged, ONE commit before the ack (the
|
/* THE INLINE PATH KEEPS ITS OWN BARRIER, AND THAT ASYMMETRY IS
|
||||||
* builtin's return). A failed commit is a failed write: the
|
* DELIBERATE (databasev2 4 part A). The request path batches:
|
||||||
* row is removed again so RAM never claims what disk never
|
* wo_vm_adopt holds each reply and commits once per drain. This
|
||||||
* acknowledged, and the statement traps. */
|
* path cannot, because it has no reply to hold — it returns into
|
||||||
if (wo_wal_append_insert(w, db, cid, id) != 0 || wo_wal_commit(w) != 0) {
|
* its OWN fiber rather than unparking a requester. Batching here
|
||||||
wo_row_remove(db, cid, id);
|
* would mean parking that fiber on the barrier, which is part B's
|
||||||
*msg = "wal commit failed";
|
* machinery and deliberately out of part A. Do not "fix" this by
|
||||||
return WO_T_IO;
|
* dropping the commit: without it an inline statement would never
|
||||||
}
|
* be durable at all.
|
||||||
|
*
|
||||||
|
* Committing here is safe because the drain commits
|
||||||
|
* unconditionally whenever anything is staged, so the buffer is
|
||||||
|
* empty when this runs. If that ever stops holding, this commit
|
||||||
|
* would make another statement's record durable early and ack it
|
||||||
|
* to the wrong writer.
|
||||||
|
*
|
||||||
|
* Failure is fatal, not a trap: the row is already in RAM. */
|
||||||
|
if (wo_wal_append_insert(w, db, cid, id) != 0) wo_wal_stage_fatal(w);
|
||||||
|
wo_wal_commit_fatal(w, 1);
|
||||||
}
|
}
|
||||||
R[A] = id;
|
R[A] = id;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -47,10 +57,10 @@ int wo_builtin_db(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) {
|
||||||
return ek == DB_ERR_UNIQUE ? WO_T_UNIQUE : ek == DB_ERR_OOM ? WO_T_OOM : WO_T_DB;
|
return ek == DB_ERR_UNIQUE ? WO_T_UNIQUE : ek == DB_ERR_OOM ? WO_T_OOM : WO_T_DB;
|
||||||
wo_wal *w = (wo_wal *)vm->rt.wal;
|
wo_wal *w = (wo_wal *)vm->rt.wal;
|
||||||
if (w) {
|
if (w) {
|
||||||
if (wo_wal_append_update(w, db, cid, id) != 0 || wo_wal_commit(w) != 0) {
|
/* was: trap and leave RAM ahead of disk, which the old comment
|
||||||
*msg = "wal commit failed"; /* RAM ahead of disk: trap, do not ack */
|
* admitted. Now fatal — see the insert arm. */
|
||||||
return WO_T_IO;
|
if (wo_wal_append_update(w, db, cid, id) != 0) wo_wal_stage_fatal(w);
|
||||||
}
|
wo_wal_commit_fatal(w, 1);
|
||||||
}
|
}
|
||||||
R[A] = 0;
|
R[A] = 0;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -70,10 +80,8 @@ int wo_builtin_db(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) {
|
||||||
}
|
}
|
||||||
wo_wal *w = (wo_wal *)vm->rt.wal;
|
wo_wal *w = (wo_wal *)vm->rt.wal;
|
||||||
if (w) {
|
if (w) {
|
||||||
if (wo_wal_append_remove(w, cid, id) != 0 || wo_wal_commit(w) != 0) {
|
if (wo_wal_append_remove(w, cid, id) != 0) wo_wal_stage_fatal(w);
|
||||||
*msg = "wal commit failed";
|
wo_wal_commit_fatal(w, 1);
|
||||||
return WO_T_IO;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
R[A] = 0;
|
R[A] = 0;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue