After a failed Composer upgrade, should a Symfony app roll back database migrations or just restore composer.lock and clear the cache?
0 reputation · 22 Oct 2024, 20:52 UTC
0 reputation · 22 Oct 2024, 20:52 UTC
When a composer update on a Symfony application fails partway through, the composer.lock file can be left in a partially updated state, and the compiled container in var/cache may still reference classes that were removed or changed. Restoring the lock file, reinstalling dependencies, and clearing the cache are the documented recovery steps, since Symfony has no built-in automated rollback for a failed upgrade.
The unclear part is what to do when the failed upgrade already executed Doctrine migrations before breaking. Clearing the cache and restoring the lock file do not touch the database, so the schema may no longer match the restored code. Whether to run doctrine:migrations:rollback, restore a database snapshot, or leave the migrated schema in place seems to depend entirely on each project's migration strategy.
Assuming a Symfony 6/7 application with Doctrine Migrations and a production deployment:
composer.lock and clearing var/cache?doctrine:migrations:rollback cannot cleanly undo?Restoring composer.lock and clearing var/cache is always required, but it is never sufficient on its own once migrations have executed. Those steps restore code and the compiled container; they do nothing to the database. Whether you then roll back migrations or restore a database snapshot depends on one thing: whether the migrations that ran are cleanly reversible.
There is no single documented rule, but the practical decision point is the reversibility of the executed migrations:
down() method, contains no irreversible operations (raw SQL data changes, dropped columns with data, destructive updates), and the migration run completed transactionally rather than dying mid-statement.Before choosing, inspect the version table to confirm what actually ran:
php bin/console doctrine:migrations:status
# or query directly:
SELECT version, executed_at FROM doctrine_migration_versions ORDER BY executed_at DESC;Compare the newest recorded version against the migrations present in the restored codebase. If the table lists versions whose migration classes no longer exist after the lock file restore, doctrine:migrations:rollback may not even be able to locate the down() code it needs — another argument for a snapshot restore.
Order matters because rollback executes PHP code from the migration classes:
composer.lock from a known-good copy (version control or backup — do not trust the file left behind by the failed update), then run composer install.php bin/console cache:clear (or remove var/cache for the target environment).php bin/console doctrine:migrations:migrate 'prev' or target the specific pre-upgrade version — tested first against a staging copy of the production database.The subtlety: rollback runs against the restored vendor tree. If the failed upgrade changed Doctrine Migrations itself, the rollback behavior comes from the old version, which is normally what you want — but it means you cannot roll back before restoring dependencies, because the new (possibly broken) code would execute the rollback.
Three common cases:
down() is empty, throws IrreversibleMigration, or was auto-generated incompletely.up(): schema rollback does not restore deleted or transformed rows.In all three, restore from the pre-upgrade backup and replay nothing. This is why the standard production posture is: take a database snapshot immediately before composer update, and treat migration rollback as a convenience for development, not a production recovery mechanism.
This assumes Symfony 6/7 with Doctrine Migrations 3.x; exact table and command names differ in older versions. Verify the migration bundle version and test the full recovery sequence on staging before relying on it. If the failed upgrade also deployed new application code that already served traffic against the migrated schema, check for writes made in the interim — a snapshot restore discards them.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.