Liquibase: Tracking & Locking
|
This section documents schema/database evolution using version-controlled migration tools — Liquibase (relational/SQL), Mongock (MongoDB, Java-native) and Flamingock (Mongock’s successor, generalized to any external system) — as described by each tool’s own official documentation: liquibase.org and docs.liquibase.com for Liquibase, docs.mongock.io for Mongock, and docs.flamingock.io for Flamingock — which are the references these pages are written and verified against. This content was generated with the assistance of AI and should be verified against those official docs before being relied on in production. Mongock is now maintained under the Flamingock GitHub organization (github.com/flamingock/mongock), and Flamingock is positioned as its successor — see Coming from Mongock. Readers starting a new project should default to Flamingock unless they have a specific reason to stay on Mongock. This section’s bibliography lists the reference material consulted while preparing these pages. |
DATABASECHANGELOG
Liquibase records every applied changeset as a row in the DATABASECHANGELOG table, created automatically in the
target database on first use. Each row carries the changeset’s id, author and source filename (its identity,
per Liquibase: Changelogs & Changesets),
the timestamp it ran, an execution order number, and a checksum of its content — per
What is a Changelog?. This table is the audit trail:
it’s what liquibase status reads to compute which changesets are still pending, and what
Core Concepts' audit/tracking table concept maps onto for
Liquibase specifically.
DATABASECHANGELOGLOCK
A second table, DATABASECHANGELOGLOCK, holds a single row used as a mutex: before update runs, Liquibase sets
a LOCKED = true flag (with the locking host and timestamp) in this row, and clears it when the run finishes.
This is what stops two instances of an application — or a developer and a CI job — from applying changesets to
the same database concurrently and racing each other.
Checksum Mismatches
If a changeset already recorded in DATABASECHANGELOG is edited afterward — even a whitespace-only change to
some formats — the checksum Liquibase recomputes on the next run no longer matches the one stored from the first
run. By default this is a validation failure: Liquibase refuses to proceed, on the theory that it cannot know
whether the edited version was ever actually applied to this database (see
Core Concepts' idempotency and checksum sections). Two ways to
resolve it:
-
Preferred: revert the edit, and add a new changeset for the additional change instead — never edit an applied changeset (Best Practices).
-
When the edit is confirmed harmless (e.g. a comment-only change, or the edit was already applied by hand and you are certain the two are equivalent):
liquibase clearCheckSumsclears every stored checksum, and the nextupdate/validaterecomputes and re-stores them from the current changelog content, accepting it as the new baseline.liquibase validatechecks changelog integrity (including checksums) without applying anything, so it’s the safe way to confirm the mismatch before deciding.
Releasing a Stuck Lock
If a Liquibase run is interrupted (the process is killed, the container is stopped) while it holds the lock,
DATABASECHANGELOGLOCK can be left with LOCKED = true even though nothing is actually running anymore. The
next update then hangs or fails immediately with a lock-already-held error. liquibase releaseLocks forces the
lock row back to unlocked:
liquibase releaseLocks
Run this only once you’ve confirmed no other Liquibase process is genuinely still running against that database — forcing the lock while a real run is in progress reintroduces exactly the concurrent-write race the lock exists to prevent.