Skip to content

Exp 269: bound the read, don't predict it (moonshot, rejected) - #304

Merged
danReynolds merged 2 commits into
mainfrom
exp-269-enforced-inline-reads
Aug 12, 2026
Merged

Exp 269: bound the read, don't predict it (moonshot, rejected)#304
danReynolds merged 2 commits into
mainfrom
exp-269-enforced-inline-reads

Conversation

@danReynolds

@danReynolds danReynolds commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Outcome

Rejected after adversarial review. The small-read latency mechanism is
real, but the candidate's defining safety claim is false. No runtime,
build-hook, diagnostic, or test-only API change remains in this PR; the final
diff is publication-only.

The exact prototype is preserved at archive/exp-269
(dd252db49a538777f0c23135f2e466be5c12e2a7) for inspection.

What the experiment tried

Exp 265 measured that worker-isolate dispatch is most of a hot point read, then
rejected caller-isolate routing because row history could not bound bytes, work
before the first row, or a non-yielding chain of reads.

Exp 269 tried to enforce those bounds during execution instead:

  • 64 decoded rows;
  • 64 KiB of TEXT/BLOB result payload;
  • 10,000 SQLite VM steps through a progress handler;
  • a nominal 1 ms inline budget per event-loop turn.

After two small observations, a statement could run synchronously on a
dedicated caller-isolate connection. Crossing a cap reset the statement and
replayed it on a worker.

What held up

The performance mechanism reproduced on the exact final candidate (collection
3, two order-flipped pairs):

lane final-candidate delta
point read -60.0%
21-column point read -64.9%
20-row page -38.2%
64-row page -24.0%
point read behind four busy readers -89.5%
eight point reads -69.8%

The release sweep against the exact parent recorded 1 win, 0 regressions, and
168 neutral metrics. Its sole win was synthetic point-query throughput;
representative chat/feed lanes remained within the declared noise floor.

Collections 1 and 2 used a pre-sentinel decoder implementation. They support
the mechanism but are not extra repetitions of the final runtime.

Why it is rejected

The decisive probe arms this SQL with two one-byte reads, then requests 16 MiB:

SELECT length(randomblob(?)) AS n

It returns one INTEGER, copies no result payload, and remains below the VM-step
limit because the expensive byte generation occurs inside one SQLite function
opcode.

arm elapsed (three samples) 1 ms timer fired before select returned?
parent 96e6730 27,629 / 25,975 / 26,032 us yes / yes / yes
candidate dd252db 27,790 / 26,105 / 26,203 us no / no / no

The database work costs the same. Current main parks the caller on a worker,
so the timer runs; the candidate blocks the calling isolate for 26-28 ms. A
progress handler can request cancellation between selected VM operations; it
cannot preempt a slow function, collation, virtual table, VFS/page fault, or
busy wait inside one operation. Parameter packing and cold preparation also
occur before handler installation, and native TEXT classification can scan a
complete cell before the Dart byte cap.

The audit found additional correctness blockers:

  • blanket fallback can replay extension UDF or virtual-table side effects;
  • a process-global schema cache keyed only by SQL can collide across databases;
  • zero-valued kill switches do not consistently disable inline routing;
  • the byte-cap/transfer-threshold invariant is assert-only in release;
  • the abort latch is evictable and can re-arm under SQL churn;
  • direct ReaderPool.spawn callers were not all migrated to the extra connection;
  • the inline connection overwrites an extension-installed progress handler.

These are reinforcing defects, but the single-opcode probe alone refutes the
architecture's wall-time premise.

Final package

Kept:

  • focused routing harness additions and original measurement receipts;
  • select_inline_opaque_work.dart plus raw candidate/parent/corrected-branch output;
  • corrected experiment, index, signal, direction synthesis, and JOURNAL lesson.

Reverted:

  • every lib/, native/, hook/, and inline-routing test change. The final
    branch is byte-identical to origin/main in those paths.

Validation

  • dart run benchmark/experiments/select_inline_opaque_work.dart --bytes=16777216 --samples=3
    on the corrected branch: timer fired before return in all three samples;
  • dart analyze --fatal-infos;
  • dart test -j 1 — 390 tests passed;
  • dart run benchmark/finalize_experiment.dart --experiment=experiments/269-enforced-inline-reads.md;
  • experiment signal/disposition checks, generated-data checks, knowledge-link/page checks;
  • git diff --check origin/main;
  • explicit zero diff versus origin/main under hook/, lib/, native/, and test/.

Transferable conclusion

Keep arbitrary-SQL select() worker-first. A counter or stopwatch around opaque
synchronous work is not preemption. Reopen caller-isolate execution only for an
explicitly restricted/synchronous API or a mechanism that can genuinely yield
inside one SQLite operation.

@danReynolds danReynolds added approved Experiment succeeded: a kept win or a passing guard type: moonshot Frontier experiment challenging an architecture assumption labels Aug 11, 2026
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Belief impact

Learned

  • 269.1 · Bound the read, don't predict it
    Removing worker dispatch has a large, reproducible effect on hot small reads. The exact final candidate measured point reads at -60.0% to -64.9%, pag…
  • 269.2 · Bound the read, don't predict it
    Rows, result payload bytes, VDBE-opcode count and a stopwatch checked before a synchronous call do not bound caller-isolate wall time. After two one-…
  • 269.3 · Bound the read, don't predict it
    A SQLite progress handler is a cancellation opportunity, not wall-time preemption. SQLite invokes it at selected VM loop boundaries; one function, co…
  • 269.4 · Bound the read, don't predict it
    The 1 ms per-turn budget measures and improves yielding across a chain of cheap reads, not the duration of one read. In the focused final-candidate l…
  • 269.5 · Bound the read, don't predict it
    Release-suite auto-comparison can silently choose a stale or non-release artifact. Exp 269's first sweep compared against a two-experiment-old anchor…

What this changed

Exp 269 strengthens rather than supersedes exp 265's rejection. Row, payload-byte and VM-opcode caps still do not bound caller-isolate wall time: arbitrary work can occur inside one SQLite opcode, before the progress handler is installed, or while SQLite is waiting below the VM. The missing primitive is preemption, not a better predictor or another counter.

The small-read mechanism remains real. On the exact final candidate, two order-flipped pairs measured point reads at -60.0% to -64.9%, 20- and 64-row pages at -38.2% and -24.0%, a point read behind four busy workers at -89.5%, and eight point reads at -69.8%. Collections 1 and 2 used an earlier decode-loop implementation, so they support the mechanism but are not extra repetitions of the final runtime.

A per-event-loop-turn stopwatch can make a chain of already-cheap reads yield, but checking it before entering synchronous native work cannot limit one read. The decisive query returned one INTEGER and copied no payload while spending 26-28 ms in one built-in function opcode; current main let a 1 ms timer fire during worker execution, while the candidate blocked the timer until the read returned.

Abort-and-replay is not a generally safe fallback for arbitrary read-only SQL. READONLY prevents writes to the database file; it does not make extension functions or virtual-table callbacks free of external or connection-local effects. Routing needs a typed, non-replaying failure contract before application callbacks can participate.

The release sweep remains useful only as a collateral-damage receipt. Its one win was synthetic point-query throughput; representative chat and feed lanes stayed within the declared noise floor, so it did not satisfy exp 265's request for production incidence evidence.

Ships the read routing exp 265 measured and then rejected, by replacing its
admission test with enforcement. Three caps checked against what the query is
producing rather than against its history — 64 rows, 64 KB of TEXT/BLOB payload
read from the cell length before the copy, and 10,000 VDBE opcodes via a SQLite
progress handler — each abort mid-flight into an ordinary worker dispatch, so
each holds whether or not the pool's expectation was right. A 1 ms
per-event-loop-turn budget answers the microtask coalescing exp 265 had no
answer for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@danReynolds
danReynolds force-pushed the exp-269-enforced-inline-reads branch from 546855f to dd252db Compare August 11, 2026 12:13
@danReynolds danReynolds changed the title Exp 269: bound the read, don't predict it (moonshot, accepted) Exp 269: bound the read, don't predict it (moonshot, rejected) Aug 12, 2026
@danReynolds danReynolds added rejected Experiment failed: below the decision bar, regressed, or abandoned codex codex-automation and removed approved Experiment succeeded: a kept win or a passing guard labels Aug 12, 2026
@danReynolds
danReynolds merged commit 86876fa into main Aug 12, 2026
7 checks passed
@danReynolds
danReynolds deleted the exp-269-enforced-inline-reads branch August 12, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex codex-automation rejected Experiment failed: below the decision bar, regressed, or abandoned type: moonshot Frontier experiment challenging an architecture assumption

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant