generated from john/python-template
3.6 KiB
3.6 KiB
Database Rebuild Migration Workflow
This project uses an explicit export/import rebuild workflow for schema migration.
Policy:
- Do not add runtime legacy-compatibility write paths.
- Rebuild a fresh target database from current models.
- Export current data/media, then import into the fresh target.
Commands
1) Export current DB + uploads into a bundle
uv run python tools/export_import_migration.py export --bundle-dir .migration-bundle
Optional source overrides:
--source-db <path-or-sqlalchemy-url>--source-upload-dir <path>
2) Import bundle into a fresh target (SQLite or PostgreSQL)
uv run python tools/export_import_migration.py import --bundle-dir .migration-bundle --target-db .\data\transcription-new.db --target-upload-dir .\data-new
PostgreSQL target example:
uv run python tools/export_import_migration.py import --bundle-dir .migration-bundle --target-db postgresql://transcription:change-me@localhost:5432/transcription --target-upload-dir .\data-new
3) Verify migration parity and integrity
uv run python tools/export_import_migration.py verify --source-db .\data\transcription.db --target-db postgresql://transcription:change-me@localhost:5432/transcription
The verify command checks:
- row-count parity across migration tables
- orphan-reference checks for
source,job,job_source, andexecution_attempt - duplicate
(job_id, source_id, attempt_number)inexecution_attempt
Exit code:
0when counts and integrity checks pass1when mismatches or integrity violations are detected
4) One-shot export+import
uv run python tools/export_import_migration.py migrate --bundle-dir .migration-bundle --target-db .\data\transcription-new.db --target-upload-dir .\data-new
What gets migrated
- Tables (in dependency order):
document_type,person_role,tag,document,person,photo,document_person,document_tag,person_tag,job,source,job_source,execution_attempt. - Media tree under
UPLOAD_DIR.
The bundle contains:
database.json(row export)uploads/(copied media files)
Path normalization during export/import:
source.file_pathis normalized todocuments/...(upload-root-relative POSIX).photo.pathis normalized tophotos/...(upload-root-relative POSIX).
Legacy V4.x portrait/homepage backfill in the export step:
- If the source DB has no
phototable, the exporter synthesizesphotorows from legacyperson.portrait_pathvalues and from legacy homepage image files underUPLOAD_DIR/homepage. - Legacy portrait and homepage image files are copied into the unified
UPLOAD_DIR/photos/{photo_id}{suffix}layout in the migration bundle. - Legacy homepage markdown is relocated from
UPLOAD_DIR/homepage/homepage.mdtoUPLOAD_DIR/homepage.md. - Legacy
person.full_namevalues are split intogiven_names+last_namefor V5.1 schema compatibility.
Cutover (SQLite -> PostgreSQL)
After importing to a fresh target:
- Stop app and worker services to freeze writes.
- Export a migration bundle from the last SQLite state.
- Import bundle to PostgreSQL target.
- Run
verifyagainst source and target before switching runtime. - Switch runtime config to PostgreSQL (
DATABASE__DRIVER=postgresand relatedDATABASE__*values). - Start app and worker services.
- Run smoke checks (
/healthz, create/upload/process one job).
Rollback
If verify or smoke checks fail:
- Stop app and worker services.
- Revert runtime config to SQLite.
- Start app and worker against pre-cutover SQLite database.
- Preserve failed migration bundle and logs for analysis.