Give the cross-process migration writer an explicit caller-owned transaction - #20
Closed
Parad0x-Labs wants to merge 1 commit into
Closed
Parad0x-Labs wants to merge 1 commit into
Parad0x-Labs wants to merge 1 commit into
Conversation
Measured on the runner (instrumented capture, run 35622837193, SQLite 3.45.1): the writer's bare autocommit CREATE TABLE raised OperationalError SQLITE_SCHEMA (17, "database schema has changed"), exactly once per round, in all four rounds; runs 35615031134 and 35626506389 leg A showed the same failing assertion at the pinned baseline. The interleaving was not traced directly: SQLITE_SCHEMA's documented meaning is that the schema changed after the statement was prepared, so "a concurrent first-use migration committed while the statement was in flight" is an inference from that documented semantics, not a captured timeline. storage.db's contract makes the caller owner of the transaction, so the writer now opens BEGIN IMMEDIATE before its schema-touching work: the iteration takes the database's write lock across CREATE and INSERT (documented SQLite locking; the production stores already serialize their write paths on it), so a migration commit lands entirely before or after the iteration. A failed iteration rolls back its own statements and closes its handle, recording rollback/close problems rather than swallowing them. A distinct regression case pins that a failed caller transaction rolls back its own statements while previously committed rows survive.
This was referenced Sep 21, 2026
Parad0x-Labs
deleted the
mission/migration-writer-transaction-repair
branch
September 23, 2026 03:50
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.
Give the cross-process migration writer an explicit caller-owned transaction
Base: main @
8153a9697f3c022a635bc95fa450ba7b5720e3c9· Head:mission/migration-writer-transaction-repair@50e790b2bf4a62145d73ec1e4f4f55bc3473acfbChanged:
tests/test_run_migrations_cross_process.pyonly (+69). No production code.Failure
test_concurrent_first_use_migrations_lose_no_committed_row_and_poison_no_processfails on Linux CI: the concurrent writer subprocess recordsOperationalError: database schema has changed(errors: 1) and the test asserts errors == 0. Observed at the pinned baseline in runs 35615031134, 35622837193 (leg 1), 35626506389 (leg A).Root cause — measured, then inferred (kept distinct)
Measured (instrumented capture, run 35622837193 leg 2, Python 3.12.13 / SQLite 3.45.1): the error is raised by the writer's bare autocommit
CREATE TABLE IF NOT EXISTScall —sqlite_errorcode17,SQLITE_SCHEMA, exactly once per round, all four rounds. Committed rows == durable rows andintegrity_checkok throughout: nothing is corrupted; the surfaced error itself is the failure.Inferred (not traced at prepare/step granularity): SQLITE_SCHEMA's documented meaning is that the schema changed after the statement was prepared — i.e. a concurrent first-use migration committed while the statement was in flight. That interleaving is an inference from documented semantics, not a captured timeline.
Repair
storage.db.get_connection's contract makes the caller owner of the transaction, and the production stores already serialize their write paths onBEGIN IMMEDIATE. The test writer now:BEGIN IMMEDIATEbefore its schema-touching work, holding the database's write lock across CREATE and INSERT (documented SQLite locking), so each migration commit lands entirely before or after the iteration;Adds one regression case: a failed caller transaction rolls back its own statements while previously committed rows survive and the store stays openable.
Evidence
OperationalError: database schema has changed; leg B (only this file applied, same node) PASSED; leg C cumulative cross-process + singleton suites 17 passed, pinned ruff clean.tests/test_legacy_migration_repair.pyare independent of this change (itsgit show 7ff50e8d…history pin is absent from the public repo; also failing on main CI run 35570948370) — needs its own bounded repair.Limitations
Identity note
Code is byte-identical to reviewed
f88e9723379a856735a722ac29649fdf1d9f72e8; the only delta is explanatory wording (comment + commit message) that separates the measured capture from the inferred mechanism. The published branchrepair/migration-writer-transaction(which carries two extra diagnostic-workflow commits) anddiagnostic/migration-writer-captureare untouched.