Add db-migrations-flyway-liquibase: Flyway vs Liquibase migrations, rollbacks and baselines on Spring Boot 4.1
Companion code for the Flyway vs Liquibase article: checksum validation, out-of-order and repeatable migrations, baselining an existing schema, Flyway Community's undo/diff/deploy stubs, concurrent-startup locking for both tools, Liquibase changeset identity and rollback (auto-generated vs explicit), a verified Liquibase 5.0.3 filename-caching defect, the new OSS license service, the FSL license change, and running both tools against one database. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Q6XdRjtsp4862EM44T7i9a
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# 5. Repeatable migrations
|
||||
|
||||
[← 4. Out-of-order migrations](04-out-of-order-migrations.md) · [Index](../README.md) · Next: [6. Baselining an existing database →](06-baselining-an-existing-database.md)
|
||||
|
||||
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`](../src/test/java/com/ankurm/dbmigrations/flyway/FlywayRepeatableTest.java)
|
||||
ships one versioned migration (`V1__init.sql`, creating and seeding an `invoice` table) alongside
|
||||
[`R__invoice_summary_view.sql`](../src/test/java/com/ankurm/dbmigrations/flyway/FlywayRepeatableTest.java),
|
||||
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`](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](03-checksum-validation.md): 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.
|
||||
|
||||
<svg viewBox="0 0 700 150" xmlns="http://www.w3.org/2000/svg" font-family="monospace" font-size="13">
|
||||
<rect x="20" y="20" width="200" height="50" rx="6" fill="#eef2ff" stroke="#4f46e5"/>
|
||||
<text x="120" y="42" text-anchor="middle">V1__init.sql</text>
|
||||
<text x="120" y="58" text-anchor="middle" font-size="11">checksum changes → FAILS</text>
|
||||
<rect x="260" y="20" width="240" height="50" rx="6" fill="#dcfce7" stroke="#16a34a"/>
|
||||
<text x="380" y="42" text-anchor="middle">R__invoice_summary_view.sql</text>
|
||||
<text x="380" y="58" text-anchor="middle" font-size="11">checksum changes → RERUNS</text>
|
||||
<text x="20" y="110" font-size="12" fill="#475569">Same comparison against flyway_schema_history's checksum column —</text>
|
||||
<text x="20" y="128" font-size="12" fill="#475569">the V/R prefix alone decides whether a mismatch is a failure or a re-run.</text>
|
||||
</svg>
|
||||
|
||||
## 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](https://documentation.red-gate.com/flyway/flyway-cli-and-api/concepts/migrations#repeatable-migrations) (`rel="nofollow"`) cover ordering and the `installedOn`/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`/`upsert` of reference data that should reflect whatever the file currently says, not whatever it said the first time it ran.
|
||||
Reference in New Issue
Block a user