Skip to content

fix(migration): revert V29 to its released checksum, move SEP-31 owner backfill to V31 - #1979

Merged
amandagonsalves merged 3 commits into
developfrom
fix/v29-checksum-mismatch
Jul 17, 2026
Merged

amandagonsalves merged 3 commits into
developfrom
fix/v29-checksum-mismatch

Conversation

@amandagonsalves

@amandagonsalves amandagonsalves commented Jul 17, 2026 •

Copy link
Copy Markdown
Collaborator

Description

V29__sep31_customer_id_owner.sql was edited in place after ANCHOR-1248 to add a backfill INSERT, on the assumption it had never shipped in a tagged release. That held against the 4.5.0 tag, but not against every deployed environment: at least one already had the original, bare V29 (table creation only) applied before 4.6.0 shipped, so its recorded Flyway checksum no longer matched the file, and the app correctly refused to start.

This reverts V29 to its original, already-released form (bare CREATE TABLE), and moves the backfill INSERT (ANCHOR-1248) plus the muxed-account decode repair (V30) into a new migration, V31. V31 delegates to V30's existing decode logic instead of duplicating it.

Reverting V29 only fixes environments that still have the original (pre-4.6.0) checksum recorded. Any environment that already booted 4.6.0 successfully recorded the with-backfill checksum for V29, and would now fail validation in the opposite direction once V29 is reverted. Since neither history can be edited by hand (no DB access), this PR also adds FlywayChecksumCompatibilityCallback, a Flyway BEFORE_VALIDATE callback that normalizes the with-backfill checksum back to the original one, in-app, before Flyway validates. It's a no-op for every other case (fresh install, or a database that already has the original checksum), so it's safe to leave in place regardless of which of the two known histories a given environment has.

The callback is registered as a @Bean in DataBeans (component.share), since that's the one config package every Flyway-enabled server (platform, sep, observer, event-processor) actually component-scans - each server's @ComponentScan is otherwise scoped to its own controller/component packages and excludes platform.configurator, so a plain @Component there would never reach Spring Boot's FlywayAutoConfiguration.

The table-existence check that guards the normalization scopes its metadata lookup to connection.getSchema() rather than searching every schema. data.schema is configurable, and an unqualified lookup would treat a fresh target schema as already-initialized if any other schema in the same database happened to have a flyway_schema_history table - the check would pass, but the subsequent unqualified UPDATE (which resolves against the connection's actual current schema) would then fail because that schema's table doesn't exist.

Changes

  • V29__sep31_customer_id_owner.sql: reverted to its original content (byte-identical to the pre-ANCHOR-1248 commit).
  • V31__backfill_sep31_customer_id_owner.java (new): runs the same backfill INSERT that used to live in V29, then delegates to V30__backfill_muxed_sep31_customer_id_owner's existing migrate() for the muxed-account repair.
  • FlywayChecksumCompatibilityCallback.java (new): Flyway callback that rewrites V29's recorded checksum from the 4.6.0 with-backfill value back to the original value, only when the with-backfill value is actually present in the connection's current schema.
  • DataBeans.java: registers the callback as a @Bean so every Flyway-enabled server picks it up.

Context

Production incident: the event-processor service failed to start after the 4.6.0 release with FlywayValidateException: Migration checksum mismatch for migration version 29 - Applied to database: 1190516276 (the bare, pre-backfill V29) vs. Resolved locally: 1803170936 (the with-backfill V29 shipped in 4.6.0).

Testing

  • ./gradlew :core:test :platform:test - BUILD SUCCESSFUL.
  • Manual, against a live Postgres 16 instance:
    1. Fresh install: ran the actual Flyway engine through the full V1-V31 chain on an empty database with seeded sep31_transaction history (plain and muxed callers) - applies cleanly in order, sep31_customer_id_owner ends up fully and correctly backfilled. The new callback is a verified no-op here (flyway_schema_history doesn't exist yet at BEFORE_VALIDATE time; the callback detects that and returns early instead of erroring).
    2. Already-broken environment (reported incident): simulated the exact reported state (bare V29 applied, V30/V31 never run) and ran V31 directly - produces the identical, correct result.
    3. The environment this fix is actually for - one that already booted 4.6.0 and recorded the with-backfill checksum for V29: seeded flyway_schema_history with that exact checksum (1803170936) via a real migrate run against the old with-backfill V29 file, then ran Flyway again against the reverted V29 with the callback registered - validation succeeds and the recorded checksum is rewritten to 1190516276. Confirmed as a negative control that the same scenario without the callback throws the checksum mismatch (in the reverse direction from the original incident, as expected).
    4. Cross-schema false-positive: with public already holding an initialized flyway_schema_history, pointed a fresh, never-migrated fresh_target schema (via currentSchema=fresh_target) at the same database and confirmed it migrates cleanly - i.e. public having history doesn't fool the check into skipping/misinterpreting fresh_target's own state.
    5. Re-ran in all scenarios to confirm idempotency.

Documentation

N/A

Known limitations

This normalizes exactly the two checksums known from this incident (the original V29 and the 4.6.0 with-backfill V29). It does not attempt to handle a third, unknown checksum for V29 - that would still fail validation and require a manual flyway repair. Any environment currently crash-looping on this checksum mismatch, in either direction, can redeploy directly once this ships - no manual DB access needed.

…r backfill to V31

V29__sep31_customer_id_owner.sql was edited in place after ANCHOR-1248 to add
a backfill INSERT, on the assumption it had never shipped in a tagged release.
That assumption didn't hold everywhere: at least one already-deployed
environment had the original, bare V29 (table creation only) applied before
4.6.0 shipped, so its recorded checksum no longer matched the file, and Flyway
correctly refused to start ("Migration checksum mismatch for migration
version 29").

V29 is reverted to its original, already-released form. The backfill INSERT
(from ANCHOR-1248) and the muxed-account decode repair (V30) both now run
from a new migration, V31, which delegates to V30's existing logic instead of
duplicating it. Since V31 has never been recorded anywhere, Flyway applies it
cleanly regardless of which V29 checksum a given environment already has.

Verified against a live Postgres instance both ways: a fresh install running
the full V1-V31 chain, and a simulated already-broken environment (bare V29
pre-applied, V30/V31 never run) — both converge on the same fully backfilled,
correctly muxed-decoded result. Re-running V31 is a no-op (confirmed), since
the backfill INSERT is `ON CONFLICT (customer_id) DO NOTHING` and the muxed
repair only touches rows where `creator_memo IS NULL`.
Copilot AI review requested due to automatic review settings July 17, 2026 18:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Restores V29’s original checksum and relocates SEP-31 owner backfilling to V31.

Changes:

  • Reverts V29 to table creation only.
  • Adds V31 to backfill owners and reuse V30’s muxed-account repair.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
V29__sep31_customer_id_owner.sql Restores the original migration content.
V31__backfill_sep31_customer_id_owner.java Performs owner backfill and muxed-account repair.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…at already booted 4.6.0

Reverting V29 to its original form only fixes databases that still hold
the original checksum. A database that already booted 4.6.0 successfully
recorded the with-backfill checksum instead, and would now fail validation
in the opposite direction. Add a Flyway BEFORE_VALIDATE callback that
normalizes that one known checksum back to the original before Flyway
validates, so both histories are accepted with no manual DB access needed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

…d, scope table check to current schema

The callback was a plain @component under platform.configurator, a
package none of the four servers' @componentscan lists include - Spring
Boot's FlywayAutoConfiguration never saw it, so the checksum mismatch
this PR targets would still crash startup. Register it as a @bean in
DataBeans (component.share), which every Flyway-enabled server scans.

The table-existence check also used a null schema pattern, which matches
across every schema in the database. Since data.schema is configurable,
that let an already-initialized sibling schema mask a genuinely fresh
target schema, passing the check while the subsequent unqualified UPDATE
(scoped to the connection's actual current schema) had nothing to update
against and would throw. Scope the metadata lookup to
connection.getSchema() so the check and the update look at the same
table.
@amandagonsalves
amandagonsalves merged commit 40d3ef5 into develop Jul 17, 2026
14 of 16 checks passed
@amandagonsalves
amandagonsalves deleted the fix/v29-checksum-mismatch branch July 17, 2026 20:28
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.

3 participants