Soft-drop recovery

VOLTRO_SOFT_DROP=1 renames dropped columns instead of deleting them. voltro db restore-snapshot brings them back. (Distinct from Rollback — this is the one path that recovers DROP-COLUMN data.)

This page is the ONLY mechanism that recovers data from a DROP COLUMN. The sibling Rollback page covers reversing structural changes via forward-apply and file-based down bodies — neither of which brings back dropped data. If you dropped a column and want its data back, you needed VOLTRO_SOFT_DROP=1 set at apply time; that's what this page is about.

By default Voltro's applier issues ALTER TABLE ... DROP COLUMN for any lossy migration. Once that runs the data is gone — your only options are a full DB restore or replaying from a backup.

VOLTRO_SOFT_DROP=1 rewrites every drop-column op into a RENAME. The data stays in a sidecar column named <original>__dropped_<timestamp>. voltro db restore-snapshot <plan-id> walks the migration's operations + RENAMEs them back.

When to use

Set VOLTRO_SOFT_DROP=1 in your apply pipeline as a default safety net. The cost is one extra column per drop (data still takes disk space until GC); the benefit is a one-command recovery window.

Specifically helpful for:

  • Reversible production migrations — drop a column, realize 30 minutes later it broke a downstream report, restore it.
  • Pre-release schema churn — drop columns liberally during pre-release, recover when you change your mind.
  • High-stakes drops — set VOLTRO_SOFT_DROP=1 per-migration via shell env var for the specific apply.

Apply with soft-drop

VOLTRO_SOFT_DROP=1 voltro db apply

The applier looks at every op in the plan. For drop-column ops it emits a RENAME instead of a DROP:

-- Without VOLTRO_SOFT_DROP
ALTER TABLE "users" DROP COLUMN IF EXISTS "legacy_email";

-- With VOLTRO_SOFT_DROP=1
ALTER TABLE "users" RENAME COLUMN "legacy_email" TO "legacy_email__dropped_20260603145522";

The migration row still says "drop-column applied" in _voltro_migration_plans — the planner doesn't know the rename happened. The post-apply fingerprint is what matters for boot checks (and that's based on the declared schema, where the column genuinely doesn't exist anymore).

Restore

voltro db restore-snapshot plan_01j5xkqyz...
↩ restored users.legacy_email (from legacy_email__dropped_20260603145522)
✓ restored 1 column(s) from plan 'plan_01j5xkqyz...'

The command:

  1. Loads the migration plan by id.
  2. Walks every op in operations, finds the drop-column ones.
  3. For each (table, column), queries information_schema.columns for a <column>__dropped_* match.
  4. RENAMEs the most-recent match back to the original name.

If no snapshot matches (the plan was applied without VOLTRO_SOFT_DROP), the command logs a warning + skips that column but continues with the others.

Workflow

  1. Apply a risky migration with soft-drop enabled.
  2. The dropped column survives as <col>__dropped_<stamp>.
  3. Test the post-apply state.
  4. Either:
    • It's fine. Wait for GC (see below) to actually drop the column.
    • It broke something. Run voltro db restore-snapshot <plan-id> to bring the column back.
  5. Re-fix your schema in code (re-declare the column in the .entity.ts file) + run a normal voltro db apply to put the schema back in shape.

Step 5 is important — the framework's declared schema is the source of truth. Just restoring the snapshot brings the data back but the declared schema still says the column shouldn't exist; the next apply would re-drop it.

GC

Snapshot columns aren't automatically dropped. They survive until you:

  • Run a manual ALTER TABLE ... DROP COLUMN <col>__dropped_<stamp> (or a wrapping migration that handles it).
  • Drop the table entirely.

Reclaim the space once your "is the migration confirmed safe?" review window has passed with voltro db gc-snapshots --before <date> — it permanently drops every <name>__dropped_<ts> column AND table older than the date (the <ts> stamp drives the comparison). Add --dry-run to preview:

voltro db gc-snapshots --before 2026-01-01 --dry-run   # preview
voltro db gc-snapshots --before 2026-01-01             # drop them (NOT reversible)

Limitations

  • Restore needs VOLTRO_SOFT_DROP=1 at apply time. Without it the drop is a hard DROP COLUMN / DROP TABLE and the data is gone — restore-snapshot can only bring back what was soft-dropped (renamed to <name>__dropped_<ts>). It restores both columns AND tables.
  • Lossy at the row level. This recovers DROP COLUMN (everything from before the drop is in the snapshot column). It does NOT recover UPDATE/DELETE row-data — those are forever. For that, use database backups.
  • No effect on planner classification. Lossy ops are still classified as lossy by voltro db plan. VOLTRO_SOFT_DROP is about HOW the drop happens, not WHETHER it's classified safe. The lossy refusal still requires explicit dropped() annotation or VOLTRO_DESTRUCTIVE_OK=1.

See also