Restore: onto a database that was never migrated it crashes before restoring anything #476
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#476
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, reading the restore code and restoring a backup onto an unmigrated SQLite file.Checked again on 2026-10-01 against
mainat0a5cad587: still present.restore_backupdecides whether the instance is empty by askingget_user_model().objects.exists(). On a database that has never been migrated there is noaccounts_usertable, so that query raisesOperationalError(SQLite) orProgrammingError(PostgreSQL). Neither is aBackupError, somanage.py restoreends in a traceback and restores nothing. A database with no tables at all is the emptiest instance there is, and it is what a rebuild after losing a host starts from.core/backup.py:503,restore_backup(the emptiness check);core/management/commands/restore.py:31, which catches onlyBackupError.docker compose run --rm -e POSTULO_SKIP_MIGRATE=1 postulo python manage.py restore /app/data/backups/....tar.gz.POSTULO_SKIP_MIGRATE=1stopsdocker/entrypoint.shcreating the tables, and the rest of the entry point (plugins sync,check --deploy) runs without complaint on the empty file. The command then stops atbackup.py:503withdjango.db.utils.OperationalError: no such table: accounts_user. Reproduced: a backup taken from a migrated SQLite file, restored withmanage.py restoreonto a new, unmigrated file.migratefirst, then restore) is not written anywhere, andrestoreruns the migrations itself afterwards precisely so that nobody has to.get_user_model()._meta.db_table in connection.introspection.table_names()before counting, and count only when it is there.load_databasealready copes with an empty target (SQLite's backup API replaces the file's contents;pg_restore --clean --if-existstolerates missing objects), and themigrateat the end brings the result forward.Done when
tests/test_backup.pyrestores an archive onto a file-backed SQLite database that has never been migrated (the existing restore tests all run on the test runner's already-migrated in-memory database) and finds the users and the manifest's counts; it fails today withOperationalError.Related to #234 (closed), which asked for a safe restore onto a fresh instance and documented the
POSTULO_SKIP_MIGRATE=1procedure this path fails on.