Skip to content

perf(#190): the pooled reader's scope, measured on all three rows — plus the save-path attribution and the stride1 scaling curve - #206

Merged
yifanxuaaa merged 16 commits into
mainfrom
codex/190-pooled-scope
Sep 20, 2026
Merged

yifanxuaaa merged 16 commits into
mainfrom
codex/190-pooled-scope

Conversation

@yifanxuaaa

@yifanxuaaa yifanxuaaa commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

This branch now carries two lanes: the #190/#205 pooled-reader work it was opened for, and the #209 commit-time RCA (ledger L54–L62). Its base was retargeted from codex/190-corpus-phase-declaration — whose content is already on main — so this PR shows the branch's whole 16-commit diff against main.

Closes #209.


#209 — what the multi-writer regression actually was

The three causes, in order of size

  1. The locator LIMIT (63fa15c49). Save-scoped publication means objects can hold up to SAVE_SLOTS rows per object_id, so the locator query gained ORDER BY o.object_id,o.save_id LIMIT ? to guard a duplicate case this corpus never has. The scoping itself is cheaper than the previous model's query (3.488 vs 4.559 µs); the LIMIT cost 15.5 µs per call × 380,380 calls. Removing it recovered 6.65 s. Fixed and kept.
  2. The cadence multiplies how often the same bytes are written. A pack grows on every append, so sqlite3BtreeInsert compiles to OP_Delete + OP_Insert and rewrites the whole overflow chain every time. Inside a ~40-append transaction 39 of every 40 rewrites are superseded in the page cache; with one commit per append every one is flushed. The old model's commit was 5.5× cheaper per transaction (204.5 vs 37.3 µs) and still 7.67× more expensive per append.
  3. The fixed per-transaction cost, paid 42× more oftenBEGIN IMMEDIATE 9.7 µs (≈3.8 µs of it the eager RESERVED file lock), the COMMIT statement's setup, and the per-step statement parses ≈ 25 µs × 48,446 ≈ 1.2 s.

The cadence is not a tuning choice. With busy_timeout = 0 by declared profile, any overlap refuses the second writer rather than delaying it: COMMIT_EVERY = 8, 64 and 100000 all fail in round 0 with CleanupFailed { OwnershipUnavailable }. Widening the step does not slow the second writer, it loses its save.

The commit path, read with SQLite's own instruments (73e0b961c)

  • What a step writes. 27.839 pages per commit on the product's own connection (SQLITE_DBSTATUS_CACHE_WRITE = 1,348,771 pages over 48,446 commits): 24.433 the pack body it grew, 4.74 the structural pages the growth forces (page 1's change counter, the objects primary-key btree, the objects_save index, the free-list trunk, the pack's own leaf — ≈245,000 predicted against 229,788 measured). Zero spills. COMMIT executes 3 VDBE opcodes and costs 56.1 µs, so its price is the page writes and nothing else.
  • No statement does more than its rows and indexes require. FULLSCAN_STEP, SORT, AUTOINDEX and REPREPARE read 0 in all 21 buckets; every plan is a seek; the EXISTS subquery is a coroutine evaluated once for the one matched row. The 4 MiB pack cache is not thrashing either: 97.33 % hit rate over 227 distinct packs.
  • BEGIN IMMEDIATE is 9.7 µs of every step and 0.47 s of the run for 5 VDBE opcodes; a deferred-BEGIN arm prices ≈3.8 µs of it as the eager RESERVED lock. Not removable — the watermark read that opens the step must happen under the write lock.
  • Kept: the per-step policy write removal (704580673) — 48,191 of 48,446 watermark statements and 45,794 ceiling statements gone, Store byte-identical, commit_ns −5.9 % / −5.0 % in two windows, with tests/pack_watermark.rs pinning it from outside the crate (both cases fail on a structurally deferred watermark).
  • Withdrawn: the per-step statement cache (341926632) — the bucket moved, the wall clock did not.

The format: measured at −52.8 % on commit_ns, then reverted by owner decision (f876242b2)

Reserving each lane's pack directory area at a fixed offset, recording the assembled length in the header and pre-allocating the row in reservation steps makes an append keep the payload the same size, so SQLite takes btreeOverwriteCell and its content comparison and dirties only the pages whose bytes changed: 3.353 pages/commit against 34.158 in a four-arm design probe. Matched pair, one sample per arm, one window: operation 26.021 → 24.720 s (−1.300 s, −5.0 %), commit_ns 2.938 → 1.388 s (−1.550 s, −52.8 %), per append 64.16 → 30.30 µs, every other bucket inside ±0.11 s. The prediction was beaten on direction and missed on level (1.388 s against ≤ 1.0 s); the residual is recorded as unattributed.

It is not in this PR. It was reverted by owner direction and retained whole at docs/roadmap/0.1/0.1.7/evidence/stage-6-history-209-format-20260920T222118Z/scratch/format.diff (21 files, +258 −82), so it can be re-applied without re-deriving it. It moves the Store hash and needs a format-profile and schema bump, which is why it was always an owner decision.

A measurement-protocol finding that outlives the issue

The machine's level moves by 55 % and the protocol's quiet preflight does not predict it. The archived, unchanged profile-1 binary read 16.739 s in L57's window, 17.550 s in the confirmation window and 26.021 s in the last window — same executable, same 45,794 appends, same 48,446 commits. All six rows compared passed the same gate (no named cargo/rustc/fs-bench competitor, ≥ 70 % idle) and the fastest window carried the highest load (8.38 against 5.42). Every future round must re-derive its absolute bar in-window.

What is not done

resolve_ns — the locator is 2.5 s of engine time over 297,082 save-path calls (8.48 µs/call against 3.488 µs isolated) and pack_bytes a further 1.1 s over 19,771 whole-body reads at 56.1 µs each; the cache hypotheses are dead and the gap is not explained. filesystem (6.7 s) has its own round. #190, #205 and #208 remain open.

Production LOC, #209 commits

commit production LOC
63fa15c49 locator fix 25391 → 25388 (delta −3)
704580673 the step's policy write 25391 → 25403 (delta +12)
every other #209 commit (evidence, ledger, handoffs, pre-registrations) unchanged, delta 0

Same counter (tools/production_loc.py) and scope over each commit's exact first-parent and committed crates + core/crates snapshots. The branch's product content against main is 5 files, +159 −11: the locator fix, the step's policy write, and tests/pack_watermark.rs.



The #190 / #205 half of the branch (unchanged)

One production change, retained and measured; the rest is the evidence, the attribution and a correction of an earlier attribution.

1. The product change (one commit, d57d6f9aa)

Resolver::resolve_charged built a PoolReader once per resolved inode leaf, so its pack cache (4 MiB) and decoded value cache (512 KiB) died with every leaf. The reader's lifetime is now its calling operation's: ReadSession owns one for the whole read operation (as it already owned the operation-scoped decoded-group cache), the save's own pool_reader — already shared by the pooled lane and the depth walk, already released on every pack write — serves read_batch, resolve_location and the selection input, and Store::read_batch keeps a reader for its one wave. No bound moves, no retained-bytes growth, no instrumentation, no other cache change, single construction worker untouched. PoolReadCounters::since keeps each chain's reported work its own.

The mechanism was confirmed before any product line changed, from the retained campaign's own counters: 99.7 % of stride10's 79,784 pack fetches re-read a pack the same state had already read, 7.99 GB copied for 45.3 MB of pack space (176×), and the operation-scoped decoded-group cache already served 95.1 % of record calls. Pre-registered in PREREGISTRATION.md before the first sample.

2. Measured, matched arms, one sample each

pre-fix post-fix delta
stride10 operation 19.500 s 16.746 s −2.754 s (−14.1 %)
stride3 operation 48.813 s 37.066 s −11.747 s (−24.1 %)
stride1 operation 146.178 s 105.726 s −40.45 s (−27.7 %)
ns per changed MB, 17 / 53 / 157 versions 52.3 / 68.6 / 85.0 M 44.8 / 51.6 / 61.5 M slope 14.71 → 7.49 M per e-fold (−49 %)

The whole saving is the read/update path (filesystem −2,909 / −11,712 ms); storage.accept_loop is unchanged (+0.030 / −0.085 s). Counters moved exactly as pre-registered: pack_fetches 79,784 → 250 and 427,384 → 2,104; pack_bytes 7.99 GB → 16.6 MB and 25.11 GB → 80 MB — while physical_record_calls, physical_group_decodes, physical_group_cache_hits, chain_edges and leaf_requests are bit-for-bit unchanged. Retained against the pre-registered ≥1 s stride10 bar with no stride3 regression.

Equivalence on all three rows: every state root and content counter equal between arms, and both Stores hash to a recorded constant — stride10 4af37932…, stride3 f5c7ff5a…, and stride1 1635cf7b…, the last recomputed from the retained campaign's own stride1 Store. Sampled identity-matched verification per row, inside the 60 s budget: 1,083 / 3,377 / 9,996 path-states, 0 mismatches, 0 missing, 0 unexpected on every arm.

3. The attribution rounds (harness-only and evidence)

  • fe17ac75a publishes the save's own counters per state from the SaveOutcome the product already returns (29 figures), so storage.accept_loop can be read as the work that happened.
  • L50 sizes the write pattern the Store's shape implied: 44,141 whole-file payloads in groups of exactly one, 45,791 pack BLOB rewrites = 4.43 GB for 45.3 MB stored (97.8×), forced commits every ~40 appends — bounded by a synthetic replay at 1.03 s. Decision: no write-path treatment, because the best-sized candidate was around, not clearly above, the one-second bar.
  • L51 measures stride1 and finds the residual depth term: at matched changed volume the ratio barely moves (1.54 / 1.84 / 1.54 / 1.32×), while per-resolution chain work and resolution counts grow.
  • L52 corrects L50/L51: save.chain.* is MutationOwner::chain_total, which accumulates selection acquisitions and reuse resolutions (membership.rs::stored_canonicalowner.resolve_location). Per object written early → late at stride1: reuse resolutions 0.112 → 1.032 (9.21×), trials 0.677 → 0.745 (1.10×), chain objects per event 2.59 → 3.56.

4. Production LOC

commit production LOC
fcfacdce3 mechanism + pre-registration (docs) 85725 → 85725 (delta 0)
d57d6f9aa the scope treatment (product) 85725 → 85778 (delta +53); reference 65417 unchanged, core 20308 → 20361
43f06aef7, eae3d839c, 67163427c, faec3a83b evidence (docs) 85778 → 85778 (delta 0)
fe17ac75a save counters (harness only) 85778 → 85778 (delta 0)

Same counter (tools/production_loc.py) and scope over each commit's exact first-parent and committed crates + core/crates snapshots, runtime SQL included, tests/harness/docs/tools/generated excluded.

5. Checks

On the treatment commit: core cargo test --locked 494 passed / 0 failed (72 binaries), clippy --all-targets clean, fmt --all --check clean, check_product_boundary.py PASS (122 files) with self-tests OK (6 ran). Harness: cargo test --locked 117 passed / 0 failed, release build PASS with the inherited unused_mut warning recorded, not fixed. Harness format is not clean and not claimed: ops/history.rs carries 20 pre-existing rustfmt hunks plus 9 in the added code — reformatting moves the built binary (embedded panic! locations), which would leave the measured receipts describing a source no longer in the tree, so the measured source is kept and the deviation stated. No CI, no tools/preflight.sh.

6. What this does not claim

Every history.* row is a diagnostic: admission INELIGIBLE, O3 INCOMPLETE, complete commands 35.8 / 61.9 / 158.5 s against the 15 s and 25 s classes, so all budget classes are NOT_RUN. stride1 runs the ordinary recording (the driver refuses per-state phase nodes above 53 states), so its spans sit inside the state total — the pre/post points on each row are computed the same way, which is what keeps the curve comparison valid. The v0.1.6 time comparison is untouched. Two stride10 rows reconcile INCOMPLETE with a measured out-of-clock cause (first execution of a freshly built executable, 0.7–2.0 s) plus one further unidentified out-of-clock cost, both outside the child's clock and neither affecting any operation figure. No pin was written, no budget class changed, no cap promoted, no selection shrunk, no cold claim invented, no case re-run for a better number.

7. Follow-up

The remaining work is the save path, which is now 50–55 % of the operation and has no time attribution at all — no span for selection, resolution, compression, placement or commit, so compression/chunking/CAS have no cost counter. That is filed as #205 with the instrument to build and the three candidate levers. #190 stays open for its owner-blocked qualification gaps (D1–D5) and the v0.1.6 reconciliation.

… regression is mostly that

The #209 handoff ordered root cause analysis before any fix, because the measured
buckets disagreed: `commit_ns` +2.696 s but `resolve_ns` +6.108 s, while the
`eb319aaa9` message blamed the commit cadence. The RCA separates them with two
one-difference arms and one engine counter.

`resolve_ns` is a symptom. An engine attribution of the locator lookups
(`sqlite/lookup.rs::candidates`) puts 10.771 s of the 10.411 s bucket into 380,380
queries - 28.32 us each. Disabling every step commit (48,446 -> 34) lowers that to
21.78 us and recovers 7.593 s: real, but the second effect. Isolated timing of the
query texts against the run's own Store, 30,000 calls each:

  previous model text                                     4.559 us/call
  new model text, publication scoping, no ORDER/LIMIT     3.488 us/call
  the same new-model text with ORDER BY ... LIMIT ?      15.543 us/call
  ORDER BY alone 3.485 us      LIMIT alone 13.840 us      plan: unchanged

So the publication scoping is 1.07x *faster* than the previous query and the LIMIT
is 4.5x the whole query. The publication-scoped catalogue query is the multi-writer
model's contract and stays; the ORDER BY and the LIMIT were guarding a
duplicate-locator case that this workload never produces (380,380 queries, 388,897
rows, against a bound of SAVE_SLOTS), and r.location/every caller already takes the
first eligible locator per identity deterministically.

Matched clean arms, one sample each, fresh --output, one binary per arm with its
sha256 recorded, both global flocks held: operation 33.116 -> 26.467 s (-6.649 s,
-20.1 %), resolve_ns 10.411 -> 6.848 s, commit_ns 3.015 -> 3.154 s and commits
unchanged at 48,446 - the multi-writer cadence is untouched. The strictly matched
instrumented pair (same counters, same harness) reads 33.116 -> 25.630 s with
resolve_ns 10.411 -> 6.691 s and per-locator 28.32 -> 10.00 us. All 31 workload
counters are identical in all four arms and the saved Store is byte-identical
(7ea2fe6ccf13bc5a...); only the arm that changed the model has different bytes.

The step boundary was measured, not assumed, and it cannot be widened: a
measurement-only default-1 lever committed once per N steps, and N=8, 64 and 100000
all fail in round 0 with CleanupFailed { original: OwnershipUnavailable, cleanup:
OwnershipUnavailable }, because busy_timeout is zero by declared profile. The second
writer's wait on the shipped step is p50 0.4 us / p99 ~11 ms / worst 37.6 ms, and
both writers complete in every round.

Checks: fmt --check exit 0; test --locked --workspace 535 passed / 0 failed
(multi_writer 5/5, memory_bounds 10/10, visibility 9/9, persistence_failure 8/8);
clippy --all-targets -D warnings exit 0; check_product_boundary.py PASS over 175
files and its self-tests 6/6 OK; harness test 117 passed / 0 failed and a release
build exit 0. No CI, no tools/preflight.sh. Not run: verification mode - the rows
are diagnostics, admission INELIGIBLE, every budget class NOT_RUN.

Production LOC: 25394 -> 25391 (delta -3). Scope: first-party product
implementation under core/crates/*/src and <crate>/sql, plus crates/*/src; tests,
examples, docs, tools and manifests excluded. Method: tools/production_loc.py,
first parent a02168a against the final staged tree; core 25394 -> 25391 (-3,
layerfs-storage only), reference 65417 unchanged, combined 90811 -> 90808. The whole
delta is sqlite/lookup.rs: the LIMIT clause and its bound parameter.

Receipts: docs/roadmap/0.1/0.1.7/evidence/stage-6-history-209-rca-20260920T191016Z/
(README.md, PREREGISTRATION.md, results.txt, multi-writer-latency.txt, runs/).
…asured wait

Records the #209 root cause round: the pre-registration that named the treatment
before it was written, the four measured arms, the isolated SQL separation, the
second-writer latency, and ledger entry L54.

  instr0       shipped eb319aa + the RCA counters   33.116 s
  c1nocommit   every step commit disabled             25.523 s
  c2locator    locator ORDER BY/LIMIT removed         25.630 s
  treatment2   the shipped fix alone                  26.467 s

Both writers were measured, not assumed: the shipped step's lockable interval is p50
0.4 us / p99 ~11 ms / worst 37.6 ms with zero OwnershipUnavailable in eight concurrent
rounds, and the arm that widened the step failed in round 0 rather than waiting. What
remains unexplained is stated with its arithmetic: ~4.8 s of accept growth charged to
no bucket, commit_ns +2.72 s, filesystem +3.35 s out of scope, plus the full_ns/sql_ns
movement that is between-sample variance and is not attributed.

Production LOC: 25391 -> 25391 (delta 0). Documentation and diagnostic evidence only;
the product line is unchanged from the preceding commit. Method:
tools/production_loc.py, first parent 63fa15c against the final staged tree.
…nd bound the residual at 1.59 s

L54 named the per-append pack write as the largest uncharged accept-path candidate and
priced the unexplained residual at ~4.8 s. Both were measured and both are withdrawn.

`write_pack` instrumented directly, two matched arms, probe declared in
extra_environment: 1.440 s over 46,049 calls writing 4,487,746,188 bytes (4.18 GiB) of
pack body for a 49 MB Store - evict 0.003 s, body 1.255 s, ceiling 0.182 s. 1.22 s of
that was already inside sql_ns, so the genuinely uncharged part is 0.22 s. The 4.18 GiB
is real and cheap, which is why arithmetic over counters was not allowed to stand as a
result.

The ~4.8 s was an arithmetic error: it subtracted the retained #205 instrumented arm's
bucket sum from the new binary's accept span, two different instruments across two
different binaries. Read within one arm the remainder is 3.499 s in the shipped-cadence
arm - of which 1.440 s is the now-measured write_pack - and 0.850 s with step commits
disabled, so it is cadence-driven rather than stable.

With both effects removed the same source reads operation 17.950 s against the held
16.360 s (1.10x), accept_loop 8.998 s, resolve_ns 4.364 s, commit_ns 0.060 s:
-15.166 s of the -16.756 s regression. The two effects therefore account for 15.17 s of
16.76 s and the residual is 1.59 s. That arm is an experiment, never a candidate - it
has no multi-writer capability, which is the point of the model - and the shipped arm
stays at 26.467 s because the cadence is a contract.

The treatment, the byte-identical Store, the 31 identical workload counters, the
retained capability and the un-widen-able step are all unchanged. Only the size of the
open problem changes. Listed separately: filesystem +3.35 s, still out of scope.

Production LOC: 25391 -> 25391 (delta 0). Diagnostic evidence only; the measurement-only
probe and the step-commit lever were both removed, so the product tree is unchanged from
63fa15c. Method: tools/production_loc.py, first parent c2197be.
… off the commit path

Closes the arithmetic the last two rounds left open and scopes the next one.

Against the held previous-model row (16.360 s) the shipped arm's +10.107 s decomposes as
accept span +7.926 s and filesystem +3.349 s, less the accept path's own unnamed growth
+0.126 s, less -1.042 s from the other spans outside accept_loop: 11.149 - 1.042 =
10.107. The seven bucket deltas sum to +5.871 s (commit_ns +2.717, resolve_ns +1.991,
sql_ns +0.908, full_ns +0.121, place_ns +0.071, delta_ns +0.051, group_ns +0.007) and
7.926 - 5.871 = 2.055 s is exactly the unnamed growth inside the accept span plus the two
spans outside it (0.126 + 1.929). Every term reconciles.

The mechanism, in one sentence: the model kept every byte of work and removed the
amortisation. One transaction used to cover ~40 appends, so a page dirtied 40 times was
written once. Priced per unit of work the commit regression is 6.8x - 9.5 us of commit
per append before, 65.1 us now - even though each individual COMMIT got cheaper.

The handoff scopes the next round to commit_ns (3.154 s, 11.9 % of the operation, pure
overhead) with an acceptance bar of stride10 strictly below 26.467 s and commit_ns
strictly below 3.154 s while the second writer still streams. Its first hypothesis is
untested and cheap: the declared profile never sets cache_size, mmap_size or cache_spill,
and the run's own Store shows cache_size at the engine default (2 MiB) with cache_spill
enabled against a 49 MB Store and 4.18 GiB of pack body written - and COMMIT cost tracks
the dirty page set. filesystem (+3.349 s) is explicitly not that round.

Production LOC: 25391 -> 25391 (delta 0). Documentation only; the measured product tree
is unchanged from 63fa15c. Method: tools/production_loc.py, first parent 09bfbd2.
… landed it

Production LOC: 25391 -> 25391 (delta 0). Documentation only; the measured
product tree is unchanged from 63fa15c. Method: tools/production_loc.py,
first parent 6e0d526.
…is L57, not L56

L56 landed in the round that wrote the prompt, so the prompt was pointing the
next agent at an entry that already exists.

Production LOC: 25391 -> 25391 (delta 0). Documentation only; the measured
product tree is unchanged from 63fa15c. Method: tools/production_loc.py,
first parent bbc121b.
… state it re-asserted

#209's commit_ns round. The declared-profile hypothesis is refuted by measurement and
the per-step policy write is removed; the Store stays byte-identical and the second
writer still streams.

The hypothesis first. A replay of the step's statement shape on a copy of the run's own
Store, interleaved round-robin in one process (6 rounds x 2,000 commits per arm),
writes exactly 15.30 pages per commit in every arm - declared profile, cache_size
64 MiB, cache_spill 0, both, mmap_size 256 MiB, and a contract-breaking journal_mode
OFF diagnostic - at 1.86-1.92 us per page, an 1.8 % spread, zero spills. A bare 4 KiB
pwrite on the same volume is 1.723 us, so the per-page cost is the write syscall.
sqlite3BtreeInsert overwrites a row in place only when the new payload is the same size
as the old, and a pack grows on every append, so every append reallocates the overflow
chain: pages written == ceil(pack body / 4096), i.e. 4.18 GiB = 1,095,640 pages, and at
~2.5 us a page that is the 2.809 s COMMIT. The payload's pages are the stored format's
price, not the profile's and not the cadence's.

The one treatment, pre-registered. advance_pack (0.345 s over 48,446 calls,
instrumented last round) writes back the value already in store_policy on 48,191 of
them, because next_pack_id moves only when a pack is allocated (255 times);
write_pack's saves.pack_ceiling UPDATE does the same on 45,794 of 46,049. So a step now
commits the policy state it changed: the watermark is advanced only when it moved, and
the ceiling is written only by the append that creates a pack. The watermark is NOT
deferred to publication - that is the change that lets a second writer collide - and
two new cases in tests/pack_watermark.rs pin it from outside the crate, both failing on
a structurally deferred watermark (red run retained).

Measured on a matched pair sampled from ONE binary (sha256 106f181b...), the arm
selected by a measurement-only lever declared in extra_environment and removed here.
Gate pair: operation 17.105 -> 16.499 s, commit_ns 1.933 -> 1.768 s (39.9 -> 36.5 us
per append). A declared A B B A diagnostic reads 16.634/16.451/16.381/16.524 s and
1.847/1.746/1.749/1.872 s: drift-cancelled -0.163 s operation and -0.112 s commit_ns.
Pooled: operation -0.237 s (-1.41 %), commit_ns -0.111 s (-5.90 %), with resolve_ns,
sql_ns and filesystem unmoved. commit_ns separates without overlap in all seven rows
(control 1.847-1.933 s, treatment 1.746-1.829 s); the operation does not once the
shipped row is included.

The held absolute bar is not reproducible today, and that is measured rather than
assumed: the archived previous-round binary, unchanged, read 26.467 s in its own
session and 19.908 s in this one, with identical counters and an identical Store, and
two binaries with the same product behaviour read 17.105 s and 19.908 s fifteen minutes
apart. The bar (operation < 26.467 s, commit_ns < 3.154 s) is therefore met by the
treatment arm AND by the control, does not discriminate, and is not claimed as this
round's evidence; the matched contrast is.

Equivalence and capability: the saved Store is byte-identical in all seven rows
(7ea2fe6ccf13bc5a..., 51,867,648 bytes) and all 582 workload counters are identical.
The second writer still streams on the shipped step in both arms - both writers
finished in every round, zero OwnershipUnavailable, pair p99 6.7-7.1 ms treatment
against 6.4-7.1 ms control, worst observed 9.8 ms against 36.5 ms; multi_writer 5/5.
Four preflight deferrals were written and retained; two long-running greps at ~99 % CPU
are declared interference shared by every row.

Checks: core fmt --check, test --locked --workspace 537 passed / 0 failed, clippy
-D warnings, boundary guard PASS over 175 files and self-tests 6/6; harness 117 passed
/ 0 failed and a release build, harness source unchanged. No CI, no tools/preflight.sh.
Every row is a diagnostic: admission INELIGIBLE, every budget class NOT_RUN.

Production LOC: 25391 -> 25403 (delta +12). Scope: first-party product implementation
under core/crates/*/src and crates/*/src plus shipped SQL; tests, docs, benchmark
harnesses and tools excluded. Method: tools/production_loc.py, first parent 10df6ef
against the final staged tree. Core 25391 -> 25403 (layerfs-storage 7585 -> 7597:
cas/lifecycle.rs +9, cas/placement.rs +2, cas/owner.rs +1), reference 65417 unchanged,
combined 90808 -> 90820. Ledger L57.
…egression is window-dependent

Correction round, no product line changed. The previous model was rebuilt from the
commit its retained receipt names and measured back to back against this one, because
the figure this round compared against came from a different session.

The previous-model binary was never archived, so it was rebuilt from f039bca
(3d818895f70b..., archived under binary-archive/previous-model-rebuild) and validated
as the same operation by saving the byte-identical retained previous-model Store
constant 4af37932aa3391b1... in both of its rows, with the retained counters (1,149
commits, 45,791 appends, chain.objects 107,628). The harness source seal is
04bcfab5... on both sides: the harness is identical and only the product crates
differ. Four rows back to back, one sample per arm per order, balanced
prev -> shipped -> shipped -> prev so the corpus-residency asymmetry (0 resident pages
for whichever arm runs first, 5,141 for the second) is shared rather than charged:
prev-model 11.033 s / 0.242 s, shipped-b 16.481 s / 1.794 s, shipped-c 16.714 s /
1.817 s, prev-model-b 10.921 s / 0.228 s.

Corrected: the per-append commit multiple is 7.67x on the balanced means (7.40x and
7.96x by order) and 7.17x on the 08:10 same-session pair, so the ratio is
window-stable while the absolute prices are not - the previous model reads 9.54 us
there and 5.14 us here, the multi-writer model 68.4 us and 39.43 us. The ~4x this
round's summary quoted divided one window's numerator by another window's
denominator. gap-attribution.md section 3's 6.8x has a second defect: it divided
commit_ns per commit by a per-append figure; per append it is 7.2x.

Also recorded: the operation-level regression is 1.51x in this window against the
2.02x the #205 pair measured in its own (16.360 -> 33.123 s, same session, and that
row stands). Both models are faster here by different factors, so the commit multiple
held and the operation multiple did not. The gap decomposes as commit_ns +1.570 s
(27.9 %), resolve_ns +1.528 s (27.2 %), filesystem +0.980 s (17.4 %), uncharged in the
accept span +0.780 s, sql_ns +0.501 s, the rest +0.262 s - commit_ns and resolve_ns
within 3 % of each other, which is why the next rounds are resolve_ns and filesystem.

Nothing in the treatment's equivalence or capability evidence changes: within the
round's own binary pair the Store is byte-identical and all workload counters
identical. One deferral was written and retained during the addendum.

Production LOC: 25403 -> 25403 (delta 0). Diagnostic evidence only; the rebuilt
previous-model binary is an instrument, not a product line. Method:
tools/production_loc.py, first parent 7045806 against the final staged tree. Ledger
L58.
Production LOC: 25403 -> 25403 (delta 0). Documentation only; no product line changed.
Method: tools/production_loc.py, first parent 1dd1cb5.
…he commit-time RCA

No product line is kept. The treatment was pre-registered, measured and withdrawn under
its own falsifier; the tree is byte-identical to f3e84c0 over core/crates and crates.

The treatment. Every SQL text the per-step path issues was parsed again on every call -
UPDATE object_packs SET data ... 4,723 ns against 102 ns cached over 46,049 calls, SELECT
next_pack_id ... 1,106 ns against 83 ns, BEGIN IMMEDIATE and COMMIT likewise. Six texts went
through the connection's prepared-statement cache, predicting operation <= 16.35 s, sql_ns
<= 0.96 s, commit_ns <= 1.79 s and resolve_ns not above 4.45 s, with withdrawal if the
operation or sql_ns failed to fall.

Measured, both arms from one binary (sha256 85605a522738e6...), the control behind a
declared measurement-only lever, A B B A balanced, the saved Store byte-identical in all
four rows with commits 48,446 in every row: treatment 22.256 / 22.770 s operation with
commit_ns 2.485 / 2.494 s and sql_ns 1.279 / 1.343 s; control 22.536 / 22.537 s with
commit_ns 2.377 / 2.429 s and sql_ns 1.590 / 1.581 s. Delta: operation -0.024 s, commit_ns
+0.087 s, sql_ns -0.275 s, resolve_ns +0.109 s.

Withdrawn. sql_ns fell by 0.275 s against a predicted 0.22 s - the parse confirming itself
- and the operation did not fall (-0.024 s, inside the window's own -0.514 s drift). The
saving is redistributed: sql_ns -0.275 s against commit_ns +0.087 s and uncharged per-step
work +0.055 s. A B B A makes commit_ns a U in time and sql_ns a hump, so two samples per arm
cannot separate an arm effect from a window effect there. Why removing a parse from the pack
UPDATE adds time to the commit that follows it is not established, and nothing here claims
caching makes stride10 slower - only that it does not make it faster.

Two diagnostics, two hypotheses killed. The statement cache is a 16-entry LRU and one width
over capacity turns a 108 ns hit into a 10,677 ns miss, but a measurement-only counter over
a real run counts 380,444 locator calls at 126 distinct widths with 99.0 % one identifier
wide and 44,334 object-insert calls at 68 widths with 99.6 % one wide, so the hot entries
stay resident: text construction and cache thrash are both refuted as explanations of the
~6.5 us per locator call between the in-run cost (10.00 us) and the isolated one (3.488 us).
That ~2.5 s remains unexplained and is resolve_ns's. The pack UPDATE replayed with the
product's parameter shape and a realistic body cycle makes a cached statement 4,575 ns
cheaper than a fresh one and prices growth at +17.8 us per append. The D5 probe's source was
deleted with the tree before it was copied to the campaign directory; the slip is recorded
in the report rather than papered over, and its raw measurements are retained.

The successor prompt, docs/roadmap/0.1/0.1.7/issue-commit-time-rca-handoff.md, asks for what
this round could not deliver: the commit-time defect diagnosed with SQLite's own instruments
rather than timed around - EXPLAIN QUERY PLAN and the opcode listing for every statement one
step issues against the run's own Store, sqlite3_stmt_status (FULLSCAN_STEP, SORT, AUTOINDEX,
VM_STEP, REPREPARE) after a real run, sqlite3_db_status (CACHE_WRITE, CACHE_SPILL,
CACHE_HIT/MISS, CACHE_USED) on the product's own connection, which has never been read,
page_count/freelist_count across a run, and a per-statement charge split inside the step.

Checks on the reverted tree: fmt --check exit 0, test --locked --workspace 537 passed / 0
failed, clippy -D warnings exit 0, check_product_boundary.py PASS over 175 files and
self-tests 6/6 OK. Harness unchanged. No CI, no tools/preflight.sh. Every row is a
diagnostic: admission INELIGIBLE, every budget class NOT_RUN.

Production LOC: 25403 -> 25403 (delta 0). The product tree is byte-identical to f3e84c0
over core/crates and crates, so the delta is zero by construction rather than by
coincidence; core 25403 unchanged, reference 65417 unchanged, combined 90820. Method:
tools/production_loc.py, first parent f3e84c0 against the final staged tree. Ledger L59.
…mmit_ns

No product line changed: the tree stays byte-identical to 7045806 over core/crates and
crates. The kept treatment was measured in one window, and the machine's level then moved by
more than the effect (commit_ns 1.81 -> 2.49 s, stride10 16.5 -> 22.5 s for identical work),
so it was re-run with no source edited: the kept round's archived lever binary (106f181b...)
as the matched same-binary instrument, plus the shipped binary (3a6c1c20...), which the
current tree rebuilds byte for byte. collect.py and with_locks.py are byte-identical to the
retained ones and the harness seal is 04bcfab5..., so no harness change can invalidate the
pair.

A B B A on the lever binary, then the shipped row. Control (lever unset) 17.778 / 17.706 s
operation with commit_ns 2.058 / 2.037 s (44.94 / 44.48 us per append); treatment (=0)
17.794 / 17.772 s with 1.952 / 1.951 s (42.62 / 42.61 us); shipped 17.550 s with 1.932 s
(42.18 us). Pooled: operation 17.742 -> 17.706 s (-0.036 s, -0.20 %), commit_ns 2.047 ->
1.945 s (-0.103 s, -5.01 %), resolve_ns +0.057 s, sql_ns +0.004 s, filesystem +0.081 s. The
arms do not overlap in commit_ns (2.037-2.058 against 1.951-1.952), the shipped row is below
both treatment rows, and the saved Store is byte-identical in all five rows
(7ea2fe6ccf13bc5a...) with commits 48,446 everywhere.

The bucket effect reproduces in sign and size: -5.90 % in the keeping window against
-5.01 % here, in a window whose level is 8 % higher on the operation and 5 % higher on
commit_ns - about 2.2 us off a 44.7 us per-append commit price, which is what the treatment
claimed. The operation-level effect does not resolve: -0.237 s there against -0.036 s here,
both smaller than this window's own -0.228 s first-row-to-last drift. The change is kept for
what it provably removes - 48,191 of 48,446 watermark statements and 45,794 ceiling
statements, with a byte-identical Store - not for a wall-clock claim, and that is now the
recorded basis.

One custody note, recorded rather than dropped: LAYERFS_STORAGE_POLICY_REASSERT=0 IS the
treatment and unset is the control, the opposite of the statement-cache round's lever where
=0 restored the old behaviour. This window's first pass read it backwards and was caught
because the shipped row landed inside the control band; every row's arm is now checked
against its receipt's extra_environment. The successor prompt's next-free ledger pointer
moves from L60 to L61.

Checks: no product change, so L57's checks stand, and the identity to 7045806 is verified
by git diff --stat (0 lines). One sample per row, fresh --output, both global flocks, quiet
preflight (idle 74.9 % / 78.4 %), no deferrals. Every row is a diagnostic: admission
INELIGIBLE, every budget class NOT_RUN.

Production LOC: 25403 -> 25403 (delta 0). Documentation and evidence only; the instrument
binaries are not product lines. Method: tools/production_loc.py, first parent 3419266
against the final staged tree. Ledger L60.
…n window

The prompt was written before the withdrawal commit and before the confirmation window, so
two of its lines were already stale: it named f3e84c0 as the starting commit, and its
"where it stands today" paragraph quoted only the first two windows.

- starting commit b65d09a, with the check that proves it (git diff --stat 7045806 HEAD
  over core/crates and crates must print nothing);
- the confirmation window added to the read-first list and to the ledger line (L57-L60);
- three windows quoted instead of two, plus what is stable across the two that measured the
  kept change: -5.9 % and -5.0 % on commit_ns with non-overlapping arms, against an
  operation contrast that is inside the window drift in both.

Production LOC: 25403 -> 25403 (delta 0). Documentation only; no product line changed.
Method: tools/production_loc.py, first parent b65d09a.
… — the commit path is closed

Round of 2026-09-21 from b65d09a, ledger L61. No product line is kept and no
treatment is pre-registered: the tree is byte-identical to 7045806 over
core/crates and crates.

Q1: a step's transaction writes 27.839 pages per commit on the product's own
connection (SQLITE_DBSTATUS_CACHE_WRITE = 1,348,771 pages over 48,446 commits),
24.433 of them the pack body it grew and 4.74 the structural pages the growth
forces — page 1's change counter, the objects primary-key btree, the objects_save
index, the free-list trunk and the pack's own leaf, ≈245,000 predicted against
229,788 measured. Zero spills. COMMIT executes 3 VDBE opcodes and costs 56.124 µs,
so its price is the page writes: the commit path is closed for good.

Q2: no. FULLSCAN_STEP, SORT, AUTOINDEX and REPREPARE read 0 in all 21 buckets,
every plan is a seek, the EXISTS subquery is a coroutine evaluated once for the
one matched row, and no plan needs packs_save or objects_save. The 4 MiB pack
cache is not thrashing either: 97.33 % hit rate over 227 distinct packs.

Q4: BEGIN IMMEDIATE is 9.738 µs of every step and 0.473 s of the run, 5 VDBE
opcodes, and a deferred-BEGIN arm prices ≈3.8 µs of it as the eager RESERVED file
lock. Not removable: the watermark read that opens the step must happen under the
write lock.

Q3: the second A B B A was not run (owner direction: keep the round cheap); it is
retained marked, consumes no sample and is recorded NOT_RUN. The answer is offered
as mechanism — six full-step micro-probe arms all write 46.14 pages per commit,
commit_ns is 99.8 % the COMMIT statement, and three arms of identical code span
3.5 µs by position alone while the parse arms sit 10.5–11.0 µs above them. Reading:
a time effect inside L59's window, not an arm effect.

The only removable term left in the step is re-parsing and finalizing the pack
UPDATE per append (≈0.49 s) — L59's mechanism, whose saving this round places in
sql_ns and the uncharged remainder and never in commit_ns — plus the transient body
copy at bind (≈0.17 s), unavailable through rusqlite without patching a dependency.
The format is priced for the owner at ≈3.07 s rather than the ≈1.5 s in
circulation, and is not shipped.

Two diagnostic rows, one binary each, Store byte-identical in both (7ea2fe6c…)
with engine accounting identical to the page at a 1.47× difference in wall clock.
multi_writer.rs 5/5, pack_watermark.rs 2/2, boundary PASS, fmt --check exit 0.

Production LOC: 25403 -> 25403 (delta 0); core 25403, reference 65417, combined
90820. Method tools/production_loc.py, first parent 6221cad against the final
staged tree; no product line changed.
…ctory, pre-allocate the row, write appends incrementally

Owner-authorized format change (moves the Store hash). The design was measured
before it was chosen: on a copy of the run's own Store, a same-size row with the
front directory still costs 29.067 pages/commit against 34.158, while a reserved
directory with an append-only tail costs 3.353 — a 10.2x fall — and the
incremental-blob write path is the cheaper way to spend those pages (36.1 us
against 49.2 us per step).

Prediction and falsifier are in the pre-registration; the falsifier is a
withdrawal if pages/commit does not fall below 10 or the operation does not fall
by 1.5 s in-window.

Production LOC: 25403 -> 25403 (delta 0). Documentation and diagnostic sources
only; no product line changed by this commit.
…d reverted by owner decision

Round of 2026-09-21, ledger L62. No product line is kept: the change was
implemented, measured and reverted by owner direction, and the tree is
byte-identical to 7045806 over core/crates and crates. The change itself is
retained whole at the campaign's scratch/format.diff (21 files, +258 -82).

The design was measured before it was chosen. On a copy of the run's own Store,
reading SQLITE_DBSTATUS_CACHE_WRITE around every COMMIT: today's whole-row UPDATE
with a growing row costs 34.158 pages/commit; the same payload in a same-size row
costs 29.067, so pre-allocating the row is not enough -- the group directory sits
at the front, one new 16-byte entry moves every body byte, and SQLite's memcmp
guard in btreeOverwriteContent never fires. A reserved directory with an
append-only tail costs 3.353, 10.2x fewer pages. The layout was the blocker, not
the payload size.

Measured on a matched pair, one sample per arm, one window, against the archived
profile-1 executable: operation 26.021 -> 24.720 s (-1.300 s, -5.0 %),
commit_ns 2.938 -> 1.388 s (-1.550 s, -52.8 %), per append 64.16 -> 30.30 us.
Every other bucket is inside +/-0.11 s. The Store moved 7ea2fe6c... -> 0c54dbf2...
The prediction was beaten on direction and missed on level (1.388 s against a
predicted <= 1.0 s; operation -1.300 s against -2.5 to -3.2 s) because sql_ns
fell only 0.110 s -- the residual is recorded as unattributed, not explained.

Reverting does not restore L57's 16 s. The archived, unchanged profile-1 binary
read 16.739 s in L57's window, 17.550 s in the confirmation window and 26.021 s
in this round's window -- same executable, same 45,794 appends, same 48,446
commits. The 16 s figure is a property of the machine, not of the code. And the
protocol's own quiet preflight does not predict it: all six rows passed it while
differing by 55 %, and the fastest window carried the highest load.

Four tamper helpers had to be corrected: they corrupted the last byte of the row,
which under a pre-allocated row is reservation padding, so each had silently
stopped testing anything.

Checks on the change before it was reverted: fmt exit 0, workspace test 537
passed / 0 failed, clippy exit 0, boundary PASS over 175 files, harness 117
passed / 0 failed.

Production LOC: 25403 -> 25403 (delta 0); core 25403, reference 65417, combined
90820. Reverted before commit, so the total is unchanged by construction.
Owner decision on 2026-09-21: close #209 after the format round. The comment text
is retained beside the round it summarises.

Production LOC: 25403 -> 25403 (delta 0); core 25403, reference 65417, combined
90820. Documentation only.
@yifanxuaaa
yifanxuaaa changed the base branch from codex/190-corpus-phase-declaration to main September 20, 2026 22:52
@yifanxuaaa
yifanxuaaa merged commit 2f07f1f into main Sep 20, 2026
yifanxuaaa added a commit that referenced this pull request Sep 20, 2026
…aveat

One stride1 sample on the tree PR #206 merged to main (merge commit 2f07f1f):
operation 97.730 s over 157 states, commit_ns 4.104 s, resolve_ns 36.690 s,
pack_appends 84,493, Store d45975f25daee9d0..., binary 3a6c1c20397663c4....

The row is not comparable to the retained stride1 rows: those carry pack_appends
84,473 and Store 1635cf7b..., which is consistent with the multi-writer model
change (the same change that moved stride10's constant from 4af37932... to
7ea2fe6c...) but has not been verified for stride1. It also carries no in-window
control, so it must not be read against 105.726 s or 146.178 s from earlier
windows.

Production LOC: 25403 -> 25403 (delta 0); core 25403, reference 65417, combined
90820. Documentation and one diagnostic row only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.1.7: the multi-writer transaction cadence costs the single-writer save path 2.06x on stride10 (42x more commits)

1 participant