Transactional Migration Rollback Boundaries with ManagedDataSource
0 reputation · 11 May 2022, 04:06 UTC
0 reputation · 11 May 2022, 04:06 UTC
Define a reliable rollback strategy for Flyway migrations executed within a Dropwizard application using ManagedDataSource so that a failed schema change leaves no partial database state.
Dropwizard 2.x configures the data source with setAutoCommit(false) for explicit transaction control, yet DDL statements issued by Flyway often auto-commit unless the underlying database supports transactional DDL and the migration tool is instructed to run inside a transaction. The framework does not automatically roll back a failed migration, and behavior differs between Dropwizard 0.9.x and 2.x. Additionally, lazy-loaded JPA entities can become inconsistent if the persistence context is not refreshed after a schema alteration.
ManagedDataSource configuration properties guarantee that a Flyway migration runs inside a single transaction that can be rolled back on failure?No ManagedDataSource property guarantees a single-transaction migration. Rollback safety comes from two things: whether your database supports transactional DDL, and Flyway's own transaction handling — not the pool's autoCommit setting. On PostgreSQL, a failed migration rolls back cleanly with essentially no extra configuration. On MySQL/MariaDB, it cannot, and your strategy must be containment rather than rollback.
Dropwizard 2.x sets autoCommit(false) on the pooled connections for explicit application transaction control. But Flyway checks out and manages its own connections, and on databases where DDL causes an implicit commit (MySQL, MariaDB, Oracle), the autoCommit flag is irrelevant — each DDL statement commits as it executes. Conversely, on PostgreSQL, DDL is transactional regardless of that flag. The pool configuration and the migration's transactional boundary are separate concerns.
On PostgreSQL, Flyway runs each migration's statements inside a transaction by default because the database supports it. Two settings are worth confirming for your Flyway version (property names vary across Flyway 5–8+, which Dropwizard 2.x commonly bundles — verify against the version on your classpath):
flyway.group=true — wraps all pending migrations in a single transaction, so a failure in migration 12 also rolls back migrations 10 and 11 from the same run. Without it, each migration is its own transaction boundary.mixed=false (the default) so transactional and non-transactional statements aren't combined in one migration.Callbacks (beforeMigrate/afterMigrate or Java Callback implementations registered on the FluentConfiguration) are useful for logging and validation, but they do not change transactional semantics. Don't rely on a callback to enforce rollback.
Where rollback is impossible, minimize the blast radius:
flyway_schema_history and blocks further runs. Inspect the actual schema state, clean up manually, then use flyway repair — don't delete history rows blindly.The reliable fix is ordering, not refreshing. Run Flyway at application startup before the HibernateBundle builds its SessionFactory — for example, in your Application.run() before the bundle's session factory is used, or via a Managed object whose start() precedes Hibernate initialization. Then no persistence context ever exists against the pre-migration schema, so there is nothing to refresh.
If migrations must run while a SessionFactory is live, existing sessions may hold cached entity metadata and loaded instances that no longer match the schema. There is no safe per-session refresh for structural changes; recreate the SessionFactory or restart the application instead.
These behaviors are well-established but version-sensitive. Confirm with a deliberate-failure test on staging:
mvn dependency:tree | grep flyway # confirm the actual Flyway versionAdd a failing statement at the end of a test migration, run it against PostgreSQL, and confirm via \d and flyway_schema_history that no partial objects remain. Repeat against your production engine — especially if it's MySQL — to validate your repair procedure. Add a log line in a Flyway callback and in the bundle's run path to prove migrations execute before the SessionFactory is built.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.