Several documentation.red-gate.com and liquibase.com URLs added in the previous commit had since moved (Redgate restructured its docs under /flyway/reference/, Liquibase Pro pricing moved to /pricing); this repoints them at the current, verified-200 pages. Also captures docs/output/16-liquibase-lock-defaults-javap.txt, the trimmed javap output backing chapter 11's claim about Liquibase's default lock-poll and lock-wait settings, which chapter 11 previously asserted without a committed artifact to back it. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Q6XdRjtsp4862EM44T7i9a
4.3 KiB
5. Repeatable migrations
← 4. Out-of-order migrations · Index · Next: 6. Baselining an existing database →
Versioned migrations (V1__...) are one-shot: apply once, never again, ever, on this database.
Views, stored procedures and seed-reference-data scripts don't fit that model — you want them to
re-apply whenever their content changes, regardless of what version number anything else is at.
That's what a repeatable migration, prefixed R__ instead of V<n>__, is for.
FlywayRepeatableTest
ships one versioned migration (V1__init.sql, creating and seeding an invoice table) alongside
R__invoice_summary_view.sql,
a view definition. On the first startup it runs once, like anything else:
installed_rank | version | description | type | checksum
---------------+---------+-------------------------------------------+-------+------------
-1 | NULL | << Flyway Schema History table created >> | TABLE | NULL
1 | 1 | init | SQL | -192856793
2 | NULL | invoice summary view | SQL | -2025099931
Notice the version column is NULL — that's how the history table distinguishes a repeatable
migration from a versioned one; there is no version number to have. The test then widens the view
to also sum amount_cents, without touching a filename or any version number, and starts a
second context against the same database:
installed_rank | version | description | type | checksum
---------------+---------+-------------------------------------------+-------+------------
-1 | NULL | << Flyway Schema History table created >> | TABLE | NULL
1 | 1 | init | SQL | -192856793
2 | NULL | invoice summary view | SQL | -2025099931
3 | NULL | invoice summary view | SQL | 750256380
(both tables from
docs/output/04-flyway-repeatable.txt)
A brand new row — same description, new checksum — and the view really was redefined: querying
invoice_summary afterward returns the new total_cents column. Compare this against chapter
3: for a versioned migration, a changed checksum is a hard failure.
For a repeatable one, it's the trigger to rerun. Same mechanism (a checksum comparison against
the history table), opposite consequence, and the only thing that decides which applies is the
filename prefix.
Going deeper
- Repeatable migrations run last, after every pending versioned one, in the order they appear on the classpath (alphabetically, by default) — not interleaved by when they were last changed.
- Flyway's repeatable migration docs (
rel="nofollow") cover ordering and theinstalledOn/checksum comparison in full. - A view is the textbook use case, but the same mechanism works for anything idempotent — a stored procedure body, or a
MERGE/upsertof reference data that should reflect whatever the file currently says, not whatever it said the first time it ran.