Migration Runner
Versioned, Lock-Protected Database Migrations
What Morphium Offers
quarkus-morphium ships a lightweight, MongoDB-backed schema/data migration mechanism.
Annotate a class @MorphiumChangeUnit, mark one method @Execution,
and the extension discovers it at build time and (optionally) runs it automatically at
startup — recording success in a changelog collection and coordinating multiple
application instances via a distributed lock, so a rolling deployment never runs the same
migration twice concurrently.
The Challenge
Schema/data migrations for a document database still need the same guarantees relational migration tools provide: run exactly once, run in a defined order, survive a crash mid-run without corrupting state, and never run twice concurrently when multiple application instances start at the same time (a rolling Kubernetes deployment, for example).
Morphium Features Used
Prerequisites & Key Concepts
- Automatic discovery, explicit execution. The Quarkus extension's
build-time processor scans for
@MorphiumChangeUnitclasses via ClassGraph/Jandex and hands the list to the runner — no manual registration needed. Whether they actually run at startup is controlled separately byquarkus.morphium.migration.migrate-at-start(defaultfalse). - Idempotency is your responsibility. The runner guarantees a
migration's
@Executionmethod is not re-invoked once its changelog entry is recorded asEXECUTED— but the method body itself must be safe to run more than once in case of a crash between the side effect and that changelog write. - Lock TTL, not a permanent hold. The distributed lock has a
configurable TTL (
lock-ttl-seconds, default 60) and is renewed while a migration runs; if a process crashes while holding it, the lock simply expires rather than deadlocking every future deployment. - This demo bypasses
migrate-at-start. That property only triggers a run once, automatically, at application boot. To let you trigger a run on demand from this page, the demo constructs its ownMorphiumMigrationRunnerand callsexecute(...)directly — exactly what the extension's own startup code does internally.
The Migration
import de.caluga.morphium.quarkus.migration.Execution; import de.caluga.morphium.quarkus.migration.MorphiumChangeUnit; import de.caluga.morphium.quarkus.migration.RollbackExecution; @MorphiumChangeUnit(id = "001-add-archived-flag", order = "001", author = "showcase")1 public class AddArchivedFlagMigration { @Execution2 public void execute(Morphium morphium) { var query = morphium.createQueryFor(MigrationNote.class) .f(MigrationNote.Fields.archived).eq(null); query.set(MigrationNote.Fields.archived, false, false, false, null);3 } @RollbackExecution4 public void rollback(Morphium morphium) { /* ... */ } }
Morphium instance as its single parameter.execute() throws.Related Documentation
- Developer Guide — @MorphiumChangeUnit, @Execution, @RollbackExecution
- Configuration Reference — quarkus.morphium.migration.*