Skip to content

fix(sqlitedb): verify the whole migration set at startup - #139

Merged
prorochestvo merged 1 commit into
alphafrom
fix/134-verify-migration-completeness
Aug 23, 2026
Merged

fix(sqlitedb): verify the whole migration set at startup#139
prorochestvo merged 1 commit into
alphafrom
fix/134-verify-migration-completeness

Conversation

@prorochestvo

Copy link
Copy Markdown
Owner

RequireMigratedSchema asserted only that __schema_migrations was non-empty — which is
true of every partially migrated database.

release.yml flips the bin/release symlink before it starts the beacon-migrate
one-shot, so for a few seconds the cron binaries resolve to a build newer than the schema.
They started fine and then died mid-query on the first new column, which is a confusing way
to learn the migration has not run yet. #131 was the first column addition in a while:
anything touching weather_user_cities in that window would have failed on no such column: notify_state. An interrupted migrator or a hand-rolled deploy reaches the same state with no
window at all.

What changed

The gate now compares the recorded filenames against the migrations the build embeds, and
refuses to start when any is missing — naming the first five and counting the rest, because a
fresh database is behind by all 33 and a startup line listing them buries the sentence that
says what to run. Callers pass migrations.MigrationsFS; sqlitedb takes an fs.FS and does
not import the migrations package, so the layering holds.

Failing closed with "schema is behind this build: run cmd/migrator..." replaces failing open
and reporting a missing column.

One-directional on purpose. A database carrying migrations this build does not know about
is accepted: that is exactly what a rollback to the previous artifact looks like, and refusing
would turn a rollback into an outage. An empty migration file is not counted as missing
either — it applies nothing, so it is never recorded, and that is Migrator.Verify's
complaint to make rather than a reason to keep a service down.

What was rejected

Reordering the deploy so the migration runs before the symlink flip. It closes the same
window, but it runs the old binary against the new schema for the duration, which is
only safe while every migration stays additive — a weaker invariant than the one this change
relies on, and it protects nothing outside the deploy. The code change covers the interrupted
migrator and the hand-rolled deploy as well. Reversing this means moving the two lines in
release.yml; nothing here depends on the current order.

Tests

Every migration recorded → nil. Unmigrated → schema not initialised. Build ahead of the
database → schema is behind this build, naming the file (verified to fail with the
comparison removed). Database ahead of the build → accepted. Empty migration file → not
counted as missing. Plus the message trimming, including that it does not scribble on the
caller's slice.

go vet, scripts/lint-checks.sh, golangci-lint clean; 41 packages green.

Refs #134

RequireMigratedSchema asserted only that __schema_migrations was
non-empty, which is true of every partially migrated database. The
release flips the bin/release symlink before it runs the migrator, so
for a few seconds the cron binaries resolve to a build newer than the
schema: they started, then died mid-query on the first new column. An
interrupted migrator or a hand-rolled deploy reaches the same state with
no window at all.

Compare the recorded filenames against the migrations the build embeds
and refuse to start when any is missing, naming the first few. The
service now fails closed with a sentence saying what to run, instead of
failing open and reporting a missing column.

The check is one-directional on purpose: a database carrying migrations
this build does not know about is what a rollback to the previous
artifact looks like, and refusing there would turn a rollback into an
outage. An empty migration file is not counted as missing either — it
applies nothing, so it is never recorded, and that is Migrator.Verify's
complaint to make.

The deploy ordering is left alone. Migrating before the flip would run
the old binary against the new schema for the duration, which is only
safe while every migration stays additive.

Refs: #134

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U6jNuzK5PZrhDWBxB3t2gY
@prorochestvo
prorochestvo merged commit 790ee70 into alpha Aug 23, 2026
1 check passed
@prorochestvo
prorochestvo deleted the fix/134-verify-migration-completeness branch August 30, 2026 16:36
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.

1 participant