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 clearCheckSums clears every stored checksum, and the next update/validate recomputes and re-stores them from the current changelog content, accepting it as the new baseline. liquibase validate checks 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.