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
76 lines
5.5 KiB
Markdown
76 lines
5.5 KiB
Markdown
# 1. The problem, and the smallest correct mental model
|
|
|
|
[Index](../README.md) · Next: [2. Anatomy of a Flyway migration run →](02-anatomy-of-a-migration-run.md)
|
|
|
|
Every app with a database eventually needs a second table added, or a column renamed, or a
|
|
default changed — and it needs that to happen the same way on the laptop where it was written,
|
|
on the CI database that only exists for four minutes, and on production, which nobody wants to
|
|
SSH into and run SQL by hand against. A migration tool's whole job is to make "the same way"
|
|
true without a human re-typing SQL in three places.
|
|
|
|
Both tools this module compares solve that with the same core idea: number or name every schema
|
|
change, keep a table *inside the database itself* that records which ones have already run, and
|
|
on startup, apply whatever is missing. That tracking table is the single most important thing to
|
|
understand before either tool's specific behaviour makes sense — it is the reason a second
|
|
`mvn spring-boot:run` doesn't re-run migration 1, and it is the reason two things you're about to
|
|
read about (checksum validation, and locking) exist at all.
|
|
|
|
<svg viewBox="0 0 720 230" xmlns="http://www.w3.org/2000/svg" font-family="monospace" font-size="13">
|
|
<rect x="10" y="10" width="220" height="60" rx="6" fill="#eef2ff" stroke="#4f46e5"/>
|
|
<text x="120" y="35" text-anchor="middle">migration files</text>
|
|
<text x="120" y="52" text-anchor="middle" font-size="11">V1__..., V2__...</text>
|
|
<path d="M230 40 L280 40" stroke="#334155" stroke-width="2" marker-end="url(#arrow)"/>
|
|
<rect x="280" y="10" width="220" height="60" rx="6" fill="#ecfeff" stroke="#0891b2"/>
|
|
<text x="390" y="35" text-anchor="middle">the tool at startup</text>
|
|
<text x="390" y="52" text-anchor="middle" font-size="11">diff files vs. history</text>
|
|
<path d="M500 40 L550 40" stroke="#334155" stroke-width="2" marker-end="url(#arrow)"/>
|
|
<rect x="550" y="10" width="160" height="60" rx="6" fill="#fef9c3" stroke="#ca8a04"/>
|
|
<text x="630" y="35" text-anchor="middle">your database</text>
|
|
<text x="630" y="52" text-anchor="middle" font-size="11">applies what's new</text>
|
|
|
|
<path d="M390 70 L390 110" stroke="#334155" stroke-width="2" marker-end="url(#arrow)"/>
|
|
<rect x="270" y="110" width="240" height="60" rx="6" fill="#fdf2f8" stroke="#be185d"/>
|
|
<text x="390" y="135" text-anchor="middle">tracking table, in that database</text>
|
|
<text x="390" y="152" text-anchor="middle" font-size="11">flyway_schema_history / DATABASECHANGELOG</text>
|
|
<path d="M270 140 L20 140 L20 40 L 10 40" stroke="#334155" stroke-width="1.5" fill="none" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
|
|
<text x="130" y="190" font-size="11" fill="#475569">read back on the NEXT startup — this is what "already applied" means</text>
|
|
<defs>
|
|
<marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 z" fill="#334155"/></marker>
|
|
</defs>
|
|
</svg>
|
|
|
|
The tracking table is not a cache or a convenience — it is the source of truth the tool consults
|
|
before touching your schema at all, and it lives in the same database the schema lives in, so it
|
|
survives redeploys, restarts, and different machines running the same migration set. Flyway's is
|
|
called `flyway_schema_history`; Liquibase's is called `DATABASECHANGELOG`, with a second table,
|
|
`DATABASECHANGELOGLOCK`, purely for coordinating concurrent runs (chapters
|
|
[8](08-concurrent-startup-and-locking.md) and [11](11-liquibase-locking.md)).
|
|
|
|
## What this module actually runs
|
|
|
|
Everything in this article is one small Spring Boot 4.1.1 module,
|
|
[`db-migrations-flyway-liquibase`](..), with both starters on the classpath:
|
|
[`spring-boot-starter-flyway`](../pom.xml) and [`spring-boot-starter-liquibase`](../pom.xml). Two
|
|
tiny migration sets live side by side — Flyway's under
|
|
[`src/main/resources/db/migration`](../src/main/resources/db/migration), Liquibase's as one YAML
|
|
changelog at
|
|
[`db.changelog-master.yaml`](../src/main/resources/db/changelog/db.changelog-master.yaml) — and
|
|
every scenario in the chapters that follow is a real JUnit test using Spring's
|
|
[`ApplicationContextRunner`](../src/test/java/com/ankurm/dbmigrations/flyway/FlywayHappyPathTest.java),
|
|
which boots a real `ApplicationContext` against a real H2 file database in milliseconds, with no
|
|
web server needed. A small [`Transcript`](../src/test/java/com/ankurm/dbmigrations/Transcript.java)
|
|
helper writes what each test asserts to a plain text file under
|
|
[`docs/output/`](output) — every table and error message quoted in this documentation, and in the
|
|
published article, was copied out of one of those committed files, not retyped from memory.
|
|
|
|
The one part of the module that *is* a running app rather than a test is
|
|
[`DbMigrationsApplication`](../src/main/java/com/ankurm/dbmigrations/DbMigrationsApplication.java),
|
|
whose only purpose is to expose `/diag/migrations` (chapter [15](15-production-checklist.md)) so
|
|
you can watch a real startup's bookkeeping instead of only reading about it.
|
|
|
|
## Going deeper
|
|
|
|
- [Flyway: how migrations work](https://documentation.red-gate.com/flyway/flyway-cli-and-api/concepts/migrations) — the vendor's own concept page (`rel="nofollow"`).
|
|
- [Liquibase: how it works](https://docs.liquibase.com/concepts/introduction-to-liquibase.html) — the vendor's own concept page (`rel="nofollow"`).
|
|
- [Spring Boot 4.1 release notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.1-Release-Notes) — for the package relocation both tools' autoconfiguration went through (chapter [2](02-anatomy-of-a-migration-run.md) touches this).
|