Skip to content

Serialize SQLite migrations with a single exclusive transaction#455

Merged
jalehman merged 4 commits into
mainfrom
copilot/fix-sqlite-migration-race-condition
Apr 18, 2026
Merged

Serialize SQLite migrations with a single exclusive transaction#455
jalehman merged 4 commits into
mainfrom
copilot/fix-sqlite-migration-race-condition

Conversation

Copilot AI commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

runLcmMigrations() was executing schema changes and backfill updates outside a top-level transaction, so concurrent startup processes could repeatedly contend on SQLite write locks and hit SQLITE_BUSY. This change serializes the migration run behind one exclusive transaction so later processes wait once, then observe an already-migrated database.

  • Migration transaction boundary

    • Wrap runLcmMigrations() in BEGIN EXCLUSIVE / COMMIT / ROLLBACK
    • Keep existing idempotent migration steps unchanged inside the new outer transaction
    • Ensure failures unwind the full migration, not just individual backfill savepoints
  • Nested backfill compatibility

    • Update backfillMessageIdentityHashes() to reuse an existing transaction instead of issuing its own BEGIN
    • Preserve its existing batching behavior when called outside a transaction
  • Regression coverage

    • Add a migration test that asserts the run opens exactly one BEGIN EXCLUSIVE
    • Tighten the failure-path test to verify a migration error rolls back all schema and migration-state changes, not only backfilled row data
  • Release note

    • Add a patch changeset for the migration-locking fix

Example of the new transaction shape:

export function runLcmMigrations(db: DatabaseSync, options?: { fts5Available?: boolean }): void {
  db.exec(`BEGIN EXCLUSIVE`);
  try {
    // schema creation, ALTER TABLEs, backfills, FTS setup
    db.exec(`COMMIT`);
  } catch (error) {
    db.exec(`ROLLBACK`);
    throw error;
  }
}

Copilot AI changed the title [WIP] Fix SQLite migration race condition with exclusive transaction Serialize SQLite migrations with a single exclusive transaction Apr 16, 2026
Copilot AI requested a review from jalehman April 16, 2026 18:55
@jalehman jalehman marked this pull request as ready for review April 18, 2026 21:44
@jalehman jalehman merged commit 370b91b into main Apr 18, 2026
2 checks passed
@jalehman jalehman deleted the copilot/fix-sqlite-migration-race-condition branch April 18, 2026 22:05
@github-actions github-actions Bot mentioned this pull request Apr 18, 2026
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.

SQLite migration race condition: runLcmMigrations() needs BEGIN EXCLUSIVE transaction

2 participants