Repository navigation
fix(db): resolve 077 migration version collision blocking getDbInstance - #2966
Conversation
077_api_key_stream_default_mode.sql and 077_quota_pools.sql both claimed prefix 077, so getMigrationFiles() threw a version-collision error and getDbInstance() failed at every startup (app would not boot; all DB-touching unit tests were red on release/v3.8.8). Renumber the dependency-free, idempotent quota_pools migration 077 -> 085 (no other migration references quota_pools/quota_allocations), keep the non-idempotent api_key_stream_default_mode ALTER at 077, add a retroactive isSchemaAlreadyApplied guard (case 085) for DBs that already applied it under 077, and add a regression test enforcing unique migration prefixes.
There was a problem hiding this comment.
Code Review
This pull request resolves a migration version collision at prefix 077 by renumbering the idempotent quota_pools migration to 085, adding a retroactive schema guard, and introducing a regression test to enforce unique migration prefixes. Feedback highlights a critical bug where databases that already applied quota_pools under 077 will silently skip the new 077_api_key_stream_default_mode migration; to fix this, a compatibility entry should be added to rename the migration in the tracking table. Additionally, the new uniqueness test should be updated to filter out superseded duplicate migrations as documented in its comments.
| case "085": | ||
| // Retroactive guard for quota_pools migration renumbered from 077 → 085 | ||
| // (077 collided with 077_api_key_stream_default_mode). DBs that already | ||
| // applied quota_pools under the old 077 number should not re-run as 085. | ||
| return hasTable(db, "quota_pools") && hasTable(db, "quota_allocations"); |
There was a problem hiding this comment.
🚨 Critical Migration Skip Bug
While adding the retroactive isSchemaAlreadyApplied guard for "085" prevents 085_quota_pools.sql from re-running, it does not free up the "077" slot in the _omniroute_migrations table for databases that already applied quota_pools under the old "077" version.
The Problem:
If a database has already applied quota_pools as version "077", the _omniroute_migrations table contains a row with version = "077" and name = "quota_pools".
When the migration runner checks for pending migrations:
- It sees
"077"is already in the applied versions set. - It silently skips the new
077_api_key_stream_default_mode.sqlmigration because its version ("077") is considered already applied. - As a result, the database will be missing the schema changes from
077_api_key_stream_default_mode.sql(e.g., missing columns), leading to runtime crashes.
The Solution:
You must add an entry to RENAMED_MIGRATION_COMPATIBILITY to automatically rename the applied 077_quota_pools migration to 085_quota_pools in the tracking table. This will free up the "077" slot so 077_api_key_stream_default_mode can run.
Please add the following entry to RENAMED_MIGRATION_COMPATIBILITY (around line 173):
{
fromVersion: "077",
fromName: "quota_pools",
toVersion: "085",
toName: "quota_pools",
}| const byVersion = new Map<string, string[]>(); | ||
| for (const f of migrationFiles()) { | ||
| if (!byVersion.has(f.version)) byVersion.set(f.version, []); | ||
| byVersion.get(f.version)!.push(f.name); | ||
| } | ||
| const collisions = [...byVersion.entries()] | ||
| .filter(([, names]) => names.length > 1) | ||
| .map(([version, names]) => `${version} → [${names.join(", ")}]`); | ||
| assert.deepEqual( | ||
| collisions, | ||
| [], | ||
| `Migration version collision(s) detected: ${collisions.join("; ")}. ` + | ||
| `Each migration must have a unique numeric prefix (rename to the next free number).` | ||
| ); |
There was a problem hiding this comment.
⚠️ Test Does Not Filter Superseded Migrations
The test comment states that this test checks for collisions "minus any SUPERSEDED_DUPLICATE_MIGRATIONS allowlisted renamed pairs", but the implementation does not actually perform this filtering.
If any superseded duplicate migrations (such as 041_session_account_affinity) are present on the filesystem, this test will fail. To make the test robust and align with the documented intent, you should filter out the allowlisted superseded migrations.
Since SUPERSEDED_DUPLICATE_MIGRATIONS is not exported from migrationRunner.ts, you can define a local list of allowed duplicates in the test file and skip them during the collision check.
const SUPERSEDED_DUPLICATE_MIGRATIONS = [
{ version: "041", name: "session_account_affinity" }
];
const byVersion = new Map<string, string[]>();
for (const f of migrationFiles()) {
const isSuperseded = SUPERSEDED_DUPLICATE_MIGRATIONS.some(
(sup) => sup.version === f.version && sup.name === f.name
);
if (isSuperseded) continue;
if (!byVersion.has(f.version)) byVersion.set(f.version, []);
byVersion.get(f.version)!.push(f.name);
}
const collisions = [...byVersion.entries()]
.filter(([, names]) => names.length > 1)
.map(([version, names]) => `${version} → [${names.join(", ")}]`);
assert.deepEqual(
collisions,
[],
`Migration version collision(s) detected: ${collisions.join("; ")}. ` +
`Each migration must have a unique numeric prefix (rename to the next free number).`
);There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d5b163558d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| case "085": | ||
| // Retroactive guard for quota_pools migration renumbered from 077 → 085 | ||
| // (077 collided with 077_api_key_stream_default_mode). DBs that already | ||
| // applied quota_pools under the old 077 number should not re-run as 085. | ||
| return hasTable(db, "quota_pools") && hasTable(db, "quota_allocations"); |
There was a problem hiding this comment.
Rehome legacy 077 quota marker before skipping 085
For users upgrading from a build where 077_quota_pools was already recorded in _omniroute_migrations, this guard only prevents re-running the renamed quota migration; it does not free the old 077 version slot. Since runMigrations() builds applied as a set of versions only, the current 077_api_key_stream_default_mode.sql is then treated as already applied and its ALTER TABLE api_keys ADD COLUMN stream_default_mode never runs. Add a compatibility/rehome step for 077_quota_pools before computing applied versions so the new 077 migration can execute while 085 is marked/skipped safely.
Useful? React with 👍 / 👎.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge OverviewThis is a well-executed fix for a critical migration version collision. The changes:
Verification
Files Reviewed (4 files)
Reviewed by laguna-m.1-20260312:free · 1,126,989 tokens |
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: #3052 (heap-pressure auto-calibration), #3051/#3048 (proxy fail-closed + registry assignments, @terence71-glitch), #3049/#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (#2958 scope fix, #2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (#2966–#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration), cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch), cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration), cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch), cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
…add contributor hall Audited all 687 commits / 60 release/v3.8.8 PRs since v3.8.7 against the CHANGELOG: - Added 5 missing Fixed entries: cheaper-inference#3052 (heap-pressure auto-calibration), cheaper-inference#3051/cheaper-inference#3048 (proxy fail-closed + registry assignments, @terence71-glitch), cheaper-inference#3049/cheaper-inference#3046 (session-pool fingerprint rotation + claude-web cf_clearance, @oyi77). - Credited previously-uncredited contributors: @branben (cheaper-inference#2958 scope fix, cheaper-inference#2959 Notion context source) and @JxnLexn (per-API-key stream default mode). - Added an Added entry for the per-API-key stream default mode feature. - Added the "🏆 Contributors" hall (24 contributors), matching the v3.8.6 format. Maintainer fix-PRs (cheaper-inference#2966–cheaper-inference#3030) are intentionally referenced by their original issue numbers in the body rather than the fix-PR number; @diegosouzapw is in the hall.
Problem
release/v3.8.8shipped two migration files with the same numeric prefix:077_api_key_stream_default_mode.sql(commit99d673fe5)077_quota_pools.sql(entered via merge1b0d3c75e)getMigrationFiles()throws a hardMigration version collision detectederror on duplicate prefixes (unless allowlisted inSUPERSEDED_DUPLICATE_MIGRATIONS). This pair was not allowlisted, sogetDbInstance()threw at every startup — the app would not boot, and every DB-touching unit test was red onrelease/v3.8.8.Fix
quota_pools077 → 085. It is dependency-free (no other migration referencesquota_pools/quota_allocations) and fully idempotent (CREATE TABLE IF NOT EXISTS), so it can safely move to the next free number. The non-idempotentapi_key_stream_default_modeALTER TABLE ADD COLUMNstays at 077.isSchemaAlreadyAppliedguard (case085) so DBs that already applied quota_pools under the old 077 number do not re-run it (mirrors the existing 070/071 precedents for migrations renumbered out of the 068 collision).tests/unit/db-migration-version-uniqueness.test.tsenforcing unique migration prefixes.Verification
db-migration-version-uniqueness: 2/2 pass (new)db-quota-migrations-idempotency: 6/6 pass (was red before — confirmsgetDbInstance()unblocked)db-migration-runner22/22,db-core-migration3/3,usage-migrations6/6,request-log-migration2/2typecheck:core: no new errors in touched files (4 pre-existing baseline errors inmcp-server/server.ts+transformersLocal.tsare unrelated)🤖 Generated with Claude Code