code wiki / _hdl_build / nx_apistack_idempotency.nx
nx_apistack_idempotency.nx
buildroot/runtime/_hdl_build/nx_apistack_idempotency.nx
about
Retained idempotency evidence. Sequential replay is not an exactly-once transaction:
mutation and journal commit remain separate. Missing or malformed evidence is not NEW.
THE READ LOCK IS SHARED (2026-09-16, debt 1789503583). A reader used to take LOCK_EX|LOCK_NB, so a concurrent
reader, or the recorder inside its one append+fsync, made every keyed tools/call fail closed as EVIDENCE_UNAVAILABLE
and the tools API could not be re-shipped from this tree. Readers now take LOCK_SH and wait: a recorder holds LOCK_EX
for exactly one append and one fsync, and flock releases on death, so the wait is bounded by one write and is never
held by another reader. Blocking is chosen over a retry budget on purpose: a budget is a magic number that fails
closed under exactly the load that makes the wait long, and the old immediate refusal WAS the defect.
MALFORMED ROWS ARE QUARANTINED, NOT POISONOUS (same day). One row with a CR, a NUL, two TABs, no TAB, or a torn tail
used to refuse the whole ledger for every key. A malformed row is now skipped and COUNTED (r.quarantined, with the
first bad line in r.first_bad_line, both handed out by id_lookup_q) -- EXCEPT when its key field is the key being
looked up: evidence about the sought key that cannot be read is not NEW, so that one lookup still fails closed
(EBADMSG) exactly as before. A torn last row (no trailing newline) is a torn append and is judged by the same rule.
dependencies 2 imports · 13 importers
diagram shows first 10 each side; +0 more imports, +3 more importers in the complete lists below.
imports: nx_syscalls.nxnx_fio.nx
imported by: nx_apistack_idempotency_epoch_candidate_t293.nxnx_apistack_idempotency_gate.nxnx_fsops_write.nxnx_fsops_write_combined_t150.nxnx_fsops_write_dynamic_t145.nxnx_id_concurrent_canonical_gate_t190.nxnx_id_evidence_canonical_gate_t190.nxnx_id_ledger_audit_t190.nxnx_id_ledger_claim_audit_t219.nxnx_id_ledger_existing_keys_audit_t219.nxnx_id_legacy_reconcile_gate_t219.nxnx_toolcall_idem_lib.nxnx_writer_bound_canonical_gate_t190.nx
structs
| 28 | struct NxIdEvidence |
| 200 | struct NxIdLegacyRow |
consts
| 17 | const ID_NOT_FOUND:i64=0-1 |
| 18 | const ID_EVIDENCE_ERROR:i64=0-2 |
| 19 | const ID_MISSING:i64=0-3 |
| 20 | const ID_CAPACITY:i64=0-4 |
| 21 | const ID_RESOURCE:i64=0-5 |
| 22 | const ID_SIGNED_MAX:i64=9223372036854775807 |
| 23 | const ID_O_RDONLY:i64=0 |
| 24 | const ID_O_RDWR_APPEND_CREATE:i64=1090 |
| 25 | const ID_O_NONBLOCK:i64=2048 |
| 26 | const ID_O_NOFOLLOW:i64=131072 |
| 27 | const ID_O_CLOEXEC:i64=524288 |
| 193 | const ID_ROW_EMPTY:i64=1 |
| 194 | const ID_ROW_COMMENT:i64=2 |
| 195 | const ID_ROW_NO_TAB:i64=4 |
| 196 | const ID_ROW_EXTRA_TAB:i64=8 |
| 197 | const ID_ROW_EMPTY_KEY:i64=16 |
| 198 | const ID_ROW_CONTROL:i64=32 |
| 199 | const ID_ROW_UNTERMINATED:i64=64 |
functions
| 31 | func id_evidence_init(r:*NxIdEvidence)->i64 called by 1: id_read_evidence |
| 35 | func id_evidence_close(r:*NxIdEvidence)->i64 called by 8: id_read_evidenceid_lookup_qid_seenfsw_evidence_decidemainmain+2 calls 1: sys_munmap_direct |
| 39 | func id_field_valid(p:*u8,n:i64,key:i64)->i64 |
| 46 | func id_read_evidence(path:*u8,key:*u8,key_n:i64,r:*NxIdEvidence)->i64 |
| 120 | func id_lookup_q(path:*u8,key:*u8,key_n:i64,out:*u8,cap:i64,q:*i64)->i64 |
| 134 | func id_lookup(path:*u8,key:*u8,key_n:i64,out:*u8,cap:i64)->i64 |
| 137 | func id_seen(path:*u8,key:*u8,key_n:i64)->i64 |
| 146 | func id_record(path:*u8,key:*u8,key_n:i64,result:*u8,result_n:i64)->i64 |
| 206 | func id_legacy_row_next(source:*u8,bytes:i64,start:i64,row:*NxIdLegacyRow)->i64 |