Migration Run Procedure
Authoritative step-by-step for applying a migration to scidex_v2.
v1 → v2 Migration Run Procedure
Script: scripts/migrate-from-v1.py
Spec: SPEC-006 §3 (Phase C Cutover)
Last updated: 2026-05-16
Prerequisites
| Requirement | Check |
|---|---|
| v1 Postgres reachable | psql $SCIDEX_V1_DSN -c '\l' |
| v2 Postgres reachable | psql $SCIDEX_DSN -c '\l' |
| v2 schema applied | psql $SCIDEX_DSN -c 'SELECT COUNT(*) FROM schema_registry' |
| Python env active | python -c 'import psycopg; import scidex_substrate' |
Set environment variables before running:
export SCIDEX_V1_DSN="postgresql://scidex_v1_ro@127.0.0.1:5432/scidex" # v1 source (read-only)
export SCIDEX_DSN="postgresql://scidex_v2_app@127.0.0.1:5432/scidex_v2" # v2 target
Or pass them directly via --v1-dsn / --v2-dsn flags.
Step 1 — Validate row counts (no writes)
python scripts/migrate-from-v1.py --check --json
This prints a delta table and emits a JSON summary:
{
"actors": {"v1_count": 1200, "v2_count": 1198, "match_pct": 99.83},
"papers": {"v1_count": 45000, "v2_count": 44200, "match_pct": 98.22},
...
}
Gate: match_pct ≥ 80 for hypotheses, papers, wiki_pages, actors.
Step 2 — Dry run (no writes committed)
python scripts/migrate-from-v1.py --dry-run
Runs all migration steps but rolls back all v2 writes before exit. Use this to confirm the script runs end-to-end without fatal errors. Exit code 0 = safe to proceed.
Step 3 — Full migration
python scripts/migrate-from-v1.py
Migration order (dependency-safe):
-
schema_registry— seed artifact type schemas -
actors— user accounts + identities -
artifacts— papers, hypotheses, wiki pages, knowledge edges, gaps -
signals— proposal votes, senate votes -
events— audit trail
Each step is idempotent: rerunning inserts missing rows and skips existing ones via ON CONFLICT DO NOTHING / upsert logic.
Step 4 — Verify after migration
python scripts/migrate-from-v1.py --check --json
Confirm match_pct ≥ 80 across all key tables. If any table is below threshold, see Troubleshooting below.
Incremental / partial re-runs
# Only migrate rows created after a date (incremental catch-up):
python scripts/migrate-from-v1.py --since 2026-05-01
# Only migrate specific types:
python scripts/migrate-from-v1.py --types actors,artifacts
# Combine:
python scripts/migrate-from-v1.py --types artifacts --since 2026-01-01
Exit codes
| Code | Meaning |
|---|---|
0 |
All steps completed; sanity checks passed |
1 |
Sanity check failed — v1 should stay up; do not flip traffic |
2 |
Fatal error (connection failure, SQL error); check logs |
Rollback
The script never deletes v1 data. If migration produces an unacceptable v2 state:
-
Keep v1 up (it was never written to).
-
Truncate affected v2 tables:
TRUNCATE actors, artifacts, … RESTART IDENTITY CASCADE; -
Re-run from Step 1.
For a selective rollback of one entity type:
python scripts/migrate-from-v1.py --types actors --since 2026-05-15
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
match_pct < 80 for papers |
Some v1 papers have null primary keys | Check scripts/backfill/cleanup_null_id_wiki_pages.py |
Exit code 2, FATAL: connection refused |
Wrong DSN | Verify SCIDEX_V1_DSN / SCIDEX_DSN |
SanityCheckError |
v2 is missing referential integrity | Run --skip-checks to inspect counts, fix root cause, re-run |
| Slow run (> 30 min) | Large row counts without batch tuning | Add --types to migrate one step at a time |
Related
-
SPEC-006 §3 — cutover runbook spec
-
docs/operations/v2-rc-checklist.md§7 — data migration gate -
src/scidex_substrate/migrations/from_v1/— individual migration modules -
tests/integration/test_migrate_v1_v2.py— full integration test suite