Repository navigation
test(objectql): bulk-write-per-row-hooks runs under the 10 s budget (#23182 row 5) - #23779
Merged
objectstack-fleet[bot] merged 1 commit intoOct 16, 2026
Merged
Conversation
…23182 row 5) The five ceiling cases (section 4 and D6) pushed 10 001 rows through engine.insert each, about 40 us a row and 2.75 s of the file's 3.6 s of test wall. The ceiling is judged on what the driver's find returns, so how the rows got into the store is not what those cases pin: the stub driver gains a synchronous seed() that lays down the very rows create() stores (the engine adds nothing to a row on insert), and the five cases seed through it. Every assertion, hook, row count and the 60 s case timeout are unchanged; small seeds still go through engine.insert. Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX Co-authored-by: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
objectstack-fleet
Bot
deleted the
claude/issue-23182-bulk-write-hooks-budget
branch
October 16, 2026 02:31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #23182 (row 5)
Row 5 of the test-budget card:
packages/objectql/src/bulk-write-per-row-hooks.test.ts, projectlocal, 16.1 s in the hourly battery's reading (run 38325245250). This PR is that file's fix and nothing else; the card stays open for its other rows. Test-only: one file, +37 / -15, no production file, no changeset (skip-changesetapplied).Where the time went (measured, not guessed)
The file's test bodies cost 3.1 to 4.0 s on a quiet local box, and the module line is the number the budget reads. Instrumenting
seedTasksinside the real file gave, in file order, 453, 392, 636, 585 and 687 ms for the five calls that push 10 001 (or 10 000) rows throughengine.insert: 2.75 s of 3.69 s, about 74 percent. Those five calls belong to the ceiling cases (section 4 and D6). The last three are slower than the first two as the heap climbs from 481 to 604 MB across them. The remaining cost is the case that admits a batch exactly at the ceiling (about 0.5 to 0.7 s: 10 000 rows, so 20 000 real per-row hook dispatches) and about 0.25 s for the other 58 cases. There is no sleep, no timer, no wait and no per-test kernel boot in this file; the cost is row volume throughengine.insert.What changed
The ceiling is judged on what the driver's
findreturns, so how those rows got into the stub store is not what the five cases pin. The stub driver gains a synchronousseed(object, rows)that stores the very rowscreate()stores (createandseednow share oneput), and a helperseedCeilingRows(driver, count, status)lays down the matched set. The five ceiling cases seed through it instead ofengine.insert. Every other seed in the file (2 to 4 rows) still goes throughengine.insert.Why the rows are the same ones: the engine adds nothing to a row on insert (a two-row probe gave
{title, status, id}fromengine.insertand from the driver alike). One-off check, not committed: for 10 001todorows, 10 000todorows and 10 001stalerows,driver.findafterengine.insertand afterseedCeilingRowsreturned deep-equal arrays, andengine.countread the same total.Before / after, wall of the file (module line, default reporter, file alone,
--maxWorkers=2, shared box, three runs each)localproject, 429 files,--maxWorkers=8on 4 cores, the base file copied under a temporary name into the same runSame 59 tests, same titles, same order. Slowest case after: "ADMITS a batch exactly AT the ceiling, in both phases", 682 ms (its 20 000 dispatches are the behaviour it pins, so it cannot shrink without weakening the pin); the next slowest is 50 ms. The card's 16.1 s was the slowest of 20 CI runs, about 4.4 times this box's 3.6 s reading; the same ratio puts this branch near 4 s in CI. That is a projection, not a measurement: NOT MEASURED until the next refresh PR's list is read. The single-file run's own
Durationline (12 to 17 s) is dominated by the cold import and transform phase (about 11 s), which is on no module line, the same shape rows 1 and 4 noted.Pins preserved
No test deleted, no
.skip/.todo, no timeout touched (the60_000on the at-ceiling case is unchanged), no project change, every assertion and every row count unchanged (10 001 and 10 000 still come fromMAX_BULK_PER_ROW_HOOK_ROWS). Per-row hooks on bulk writes are still proven per row: the before and after hook counts at the ceiling are still asserted at 10 000 each.Ablation (on-disk proof via
scripts/ablation-replace.mjs, wrap mode, restore proven: blob equals HEAD andgit diff HEADempty)Each leg ran the branch file and a temporary copy of the base file in one vitest invocation under the same mutation, and compared red sets by full test name.
engine.tsceiling check never refuses (if (verdict.kind) return;)engine.tsceiling check also refuses at the ceiling (a guard that refuses oncematchedreaches the limit)engine.tspredicate-update before-hooks never dispatched (rows.length > 99999999)count - 1)The subject (
engine.ts) is imported by relative source path, so there is no dist leg; the mutation reached disk (anchor 1 to 0, blob changed) and every restore was verified.Verification (head eb84ae2)
pnpm --filter @objectstack/objectql typecheck: exit 0, includingcheck:test-typecheck(39 files / 233 errors / 64 pinned signatures, unchanged; 0 errors in this file; the file is intsconfig.test.json's program, which is the one that covers it, and not in the defaulttsconfig.jsonprogram). Re-taken after the final edit.--no-inline-configon the changed file: 0 errors, 0 warnings (1 file).localproject of the package, before the restart, on this change: 429 files / 8222 tests passed (that count includes the temporary base copy of this file).node scripts/pm/dispatch-gates.mjs --commands: 53 derived, 53 run, all exit 0;--ranreconciliation: 53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN. Beyond that, the 11 declared wide-population families and the 52 further artifact-roster families (one of the 53 roster entries is also in the commands list): 63 commands run, 59 exit 0. Four roster gates need a pull-request or workflow-event context and exited non-zero with their own "NOT MEASURED / NOT WIRED, no PR context" text, so they judged nothing in that run:check-closing-target-claim,check-partof-closing-keyword,check-single-claim-paths,pm/check-governed-queue-guard.check-partof-closing-keywordwas then run on this body's own text (PR_BODYset): exit 0. The other three stay NOT MEASURED locally; CI supplies their context.check:dual-build-cjs-loadsandcheck:lean-entry-closurefirst exited 3 (no dist); after a full workspace build (75 of 75 tasks) both exited 0.Acceptance notes
60_000timeout on the at-ceiling case could now be lowered; it is left alone on purpose (the card forbids touching timeouts, and a lowered one is a separate decision).engine.insertcosts about 40 microseconds a row on a batch of 10 000 (about 0.4 s), growing as the heap fills. That is production cost, an optimisation and not a defect, so no card.Readings by provenance
A container restart killed the first run mid-flow (the worktree and commit survived). Re-taken after it: the before/after module-line runs in the first table row, typecheck, eslint, the full workspace build and the whole gate run on head eb84ae2. From before the restart and still valid because the head did not move: the first three-run set, the in-file seed instrumentation, the CI-shaped package run, the equivalence check and all ablation legs (their JSON and restore proofs are on disk).
Generated by Claude Code