Skip to content

fix(migrations): prevent migrations from opening their own connections - #1215

Merged
RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
Meet-hybrid:fix/issue-1211-migrations-single-transaction
Aug 21, 2026
Merged

fix(migrations): prevent migrations from opening their own connections #1215
RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
Meet-hybrid:fix/issue-1211-migrations-single-transaction

Conversation

@Meet-hybrid

Copy link
Copy Markdown
Contributor

Closes #1211

Summary

TypeORM's default migrationsTransactionMode is all — every migration in a run executes inside a single shared transaction. A migration that opens its own connection (via queryRunner.connection.createQueryRunner() or dataSource.createQueryRunner()) gets a separate pooled connection that:

  1. cannot see uncommitted work — tables/rows created by earlier migrations in the same run are not yet committed, so the fresh connection fails with relation "X" does not exist on any fresh database (this was exactly the fix-invoice-number-sequence failure resolved in fix(migrations): add baseline schema so migration:run works from scratch #1195);
  2. escapes the shared transaction — its writes are committed independently and are not rolled back when a later migration fails, leaving the database half-migrated.

This PR documents the constraint and enforces it in CI so the footgun cannot silently return.

Changes

  • src/migrations/README.md — rewritten to describe the actual TypeORM migration system and document the single-transaction rule with a Do / Don't example.
  • docs/migrations.md — new "Transaction behavior (important)" section and a best-practices row.
  • scripts/validate-migrations.js (new) — CI guard that scans every migration file for createQueryRunner() / direct .connection access and fails the build when found.
  • .github/workflows/ci.yml — new migrations:check step in the validate job (runs before migration:run).
  • package.json — adds migrations:check script and includes the guard in pnpm run verify.

Audit

All 29 migration files were audited: none open their own connection — every migration uses only the QueryRunner passed to up() / down().

Test evidence

node scripts/validate-migrations.js   # PASS: All 29 migration files use only the passed queryRunner
pnpm run typecheck                    # pass — tsc --noEmit
pnpm run lint:ci                      # pass — 0 errors
pnpm run format:check                 # pass — Prettier clean

The guard was also validated against a planted violation (queryRunner.connection.createQueryRunner() in a migration): it correctly fails with a line-level error message identifying the offending file and line.

The validate job also runs migration:run, migration:generate:check (drift), and migration:revert against Postgres 16 — these will run on this PR.

rinafcode#1211)

All migrations run inside one shared transaction (TypeORM default "all"
mode), so a migration that opens its own pooled connection cannot see
uncommitted tables/rows created by earlier migrations in the same run and
its changes escape atomic rollback. This was the root cause of the
fix-invoice-number-sequence failure from scratch (rinafcode#1195).

Document the constraint in the migrations README and the database
migration guide, and add a CI guard (scripts/validate-migrations.js) that
fails the build if any migration calls createQueryRunner() or touches
queryRunner.connection. Audited all 29 existing migrations: none open
their own connection.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
@Meet-hybrid Meet-hybrid changed the title fix(migrations): prevent migrations from opening their own connections (#1211) fix(migrations): prevent migrations from opening their own connections Aug 20, 2026
@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the project

@RUKAYAT-CODER
RUKAYAT-CODER merged commit dd9cb22 into rinafcode:main Aug 21, 2026
3 checks passed
@Meet-hybrid

Copy link
Copy Markdown
Contributor Author

Thank you for contributing to the project

Always.

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.

All migrations run in a single transaction — migrations must not open their own connections

2 participants