Write down what SP2 found, and what it measured

Trust the statement count, not the stopwatch: this machine's suite timings
drift about 20% between runs, so the branch-read regression is recorded as
201 SELECTs -> 2 rather than as seconds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Dvvqn9ZDR4ixeFPHNbww7
This commit is contained in:
parththakkar106
2026-08-18 19:14:07 +05:30
committed by Parth
co-authored by Claude Opus 5
parent 05a2a77e4c
commit c7b6a46a8a
2 changed files with 11 additions and 7 deletions
+6 -4
View File
@@ -312,10 +312,12 @@ was the pass condition. Six things worth not rediscovering:
transaction, three statements, once per adventure ever.
- **The identity map holds weak references, and it cost 25 % of the suite.** Resolving
the head branch per node re-read the `branches` row for every node in the flush, because
nothing held a strong reference between two calls: 201 SELECTs to write 200 actions,
and 36 s → 45 s on the same 297 tests. The head is now resolved once per adventure per
flush (2 SELECTs), which put the suite back at 38.8 s *with* 20 more tests. Pinned by a
test, because the symptom is only ever a stopwatch.
nothing held a strong reference between two calls: **201 SELECTs to write 200 actions**,
and 36 s → 45 s on the same 297 tests measured back to back. The head is now resolved
once per adventure per flush — **2 SELECTs**, and the same 297 tests then time within
noise of SP1 (44.2 s each; this machine's load drifts by ~20 % between runs, so trust
the statement count, not the stopwatch). Pinned by a test that counts reads of
`branches`, because the stopwatch is all the symptom there ever was.
- **The index screen is the one read scoped by head branch rather than by lineage.**
`_latest_narration` picks one row per adventure for a hundred adventures at once, and a
lineage clause each would put hundreds of OR-terms on that query. The two answers differ
+5 -3
View File
@@ -130,9 +130,11 @@ Three things to carry forward:
better invariant than the one SP1 shipped, and it was the contract that forced it.
- **The SQLAlchemy identity map is weak, and that is a performance cliff.** Resolving the
head branch once per node re-read the row from the database for every node in a flush —
201 SELECTs to write 200 actions, and a 25 % slower suite (36 s → 45 s). Nothing about
the results changed; only a stopwatch could see it. Hoist the lookup out of the loop and
hold the reference for the length of the call. Now pinned by a test.
201 SELECTs to write 200 actions, and a 25 % slower suite (36 s → 45 s, back to back).
Nothing about the results changed; only a stopwatch could see it. Hoist the lookup out
of the loop and hold the reference for the length of the call: 2 SELECTs, and the suite
back within noise of SP1. Pinned by a test that counts the reads rather than the
seconds — this machine's timings drift ~20 % between runs.
- **Clause count is bounded by the window, and it is now measured.** A story forked 20
times reads its newest 32 actions naming *one* branch, for 1.07× what an unforked story
of the same length costs. Reading the tail widens the lineage only when a deleted action