DEV Community

Discussion on: The Bug Lives at the Seam, Not in the Logic

Collapse
 
izgorodin profile image
Edward Izgorodin •

The fix for #573 makes the manifest honest about the build, and the restore still compares it with the build rather than with the database. SCHEMA_HEAD is now the head of the migrations folder shipped in the running build, read with ScriptDirectory.get_current_head(); create() stamps it into the manifest, and restore() checks the manifest against the same value. The restore endpoint passes no expected_head, so nothing on the backup path reads alembic_version from the database the rows go into. The issue asked for that half too: its reproduction has the guard accepting the archive "regardless of real DB revision", and it says "the restore guard should compare against the actual target schema head."

On a single build the two cannot drift, because startup upgrades to head, and an older build will not even start against a newer database: Alembic stops with a CommandError because the version row names a revision its scripts do not contain, which I checked on a scratch SQLite with the version row set one revision ahead. They drift during an upgrade. The chart sets no update strategy, so the Deployment rolls: the new pod migrates while it starts, and the old pod keeps serving until the new one passes /readyz. A restore that lands on the old pod in that window matches the old build's head and writes into a database one revision ahead, which is the 0034 against 0035 case from the post. Reading version_num inside restore(), the same query readiness.py already runs, and refusing when it differs from the manifest would close that window, and a test that sets the version row one past the build and expects BackupSchemaMismatchError would keep it closed.

Collapse
 
debashish_ghosal profile image
Debashish Ghosal •

You've read it exactly right. SCHEMA_HEAD is the head of the migrations folder shipped in the build (ScriptDirectory.get_current_head()), create() stamps it into the manifest, and restore() refuses when manifest.alembic_head != (expected_head or SCHEMA_HEAD) → BackupSchemaMismatchError. So the guard is build-vs-manifest, and the restore path never reads alembic_version from the target DB. Your rolling-upgrade window is the real gap: the old pod can restore into a DB one revision ahead. Reading version_num inside restore() (the same query readiness.py runs) and refusing on a mismatch would close it — noting that for the v0.2.0 write-up. Thank you for the precise repro.