Automate migration backups and add session rollback on failure

- Replace operator-driven backup requirement with automatic migration engine responsibility
- Full DB backup when new migrations are detected, before any step runs
- Per-table backup before each migration step affecting that table
- Session rollback (or per-table restore) on any migration failure
- Update local-first-recovery to reflect automatic backup requirement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-03-22 00:05:22 +03:00
parent 747c42499d
commit 52444350c1
2 changed files with 32 additions and 19 deletions

View File

@@ -1,6 +1,6 @@
# Contract: Database Patterns (Go / MySQL / MariaDB)
Version: 1.7
Version: 1.8
## MySQL Transaction Cursor Safety (CRITICAL)
@@ -104,32 +104,45 @@ items, _ := repo.GetItemsByPricelistIDs(ids) // 1 query with WHERE id IN (...)
// then group in Go
```
## Backup Before Any DB Change
## Automatic Backup During Migration
Any operation that changes persisted database state must have a fresh backup taken immediately before execution.
This applies to:
- Go migrations
- Manual SQL runbooks
- Data backfills and repair scripts
- Imports, bulk updates, and bulk deletes
- Admin tools or one-off operator commands
The migration engine is responsible for all backup steps. The operator must never be required to take a backup manually.
Backup naming, storage, archive format, retention, and restore-readiness must follow the `backup-management` contract.
### Full DB Backup on New Migrations
When the migration engine detects that new (unapplied) migrations exist, it must take a full database backup before applying any of them.
Rules:
- No schema change or data mutation is allowed on a non-ephemeral database without a current backup.
- "Small" or "safe" changes are not exceptions.
- The operator must know how to restore from that backup before applying the change.
- If a migration or script is intended for production/staging, the rollout instructions must state the backup step explicitly.
- The backup taken before a migration must be triggered by the application's own backup mechanism, not by assuming `mysql`, `mysqldump`, or other DB client tools exist on the user's machine.
- Before a migration starts, double-check that backup output resolves outside the git worktree and is not tracked or staged in git.
- The full backup must complete and be verified before the first migration step runs.
- The backup must be triggered by the application's own backup mechanism; do not assume `mysql`, `mysqldump`, `pg_dump`, or any other external DB client tool is present on the operator's machine.
- Before creating the backup, verify that the backup output path resolves outside the git worktree and is not tracked or staged in git.
### Per-Table Backup Before Each Table Migration
Before applying a migration step that affects a specific table, take a targeted backup of that table.
Rules:
- A per-table backup must be created immediately before the migration step that modifies that table.
- If a single migration step touches multiple tables, back up each affected table before the step runs.
- Per-table backups are in addition to the full DB backup; they are not a substitute for it.
### Session Rollback on Failure
If any migration step fails during a session, the engine must roll back all migrations applied in that session.
Rules:
- "Session" means all migration steps started in a single run of the migration engine.
- On failure, roll back every step applied in the current session in reverse order before surfacing the error.
- If rollback of a step is not possible (e.g., the operation is not reversible in MySQL without the per-table backup), restore from the per-table backup taken before that step.
- After rollback or restore, the database must be in the same state as before the session started.
- The engine must emit structured diagnostics that identify which step failed, which steps were rolled back, and the final database state.
## Migration Policy
- For local-first desktop applications, startup and migration recovery must follow the `local-first-recovery` contract.
- Migrations are numbered sequentially and never modified after merge.
- Trigger, take, and verify a fresh backup through the application-owned backup mechanism before applying migrations to any non-ephemeral database.
- Each migration must be reversible where possible (document rollback in a comment).
- Never rename a column in one migration step — add new, backfill, drop old across separate deploys.
- Auto-apply migrations on startup is acceptable for internal tools; document if used.

View File

@@ -1,6 +1,6 @@
# Contract: Local-First Recovery
Version: 1.1
Version: 1.2
## Purpose
@@ -53,7 +53,7 @@ For protected user data, destructive reset is forbidden.
Rules:
- Do not drop, truncate, or recreate protected tables as a recovery shortcut.
- Backup-before-change is mandatory and must follow the `backup-management` contract.
- Backup-before-change is mandatory, must be performed automatically by the migration engine (never by the operator), and must follow the `backup-management` and `go-database` contracts.
- Validate-before-migrate is mandatory.
- Migration logic must use fail-safe semantics: stop before applying a risky destructive step when invariants are broken or input is invalid.
- The application must emit explicit diagnostics that identify the blocked table, migration step, and reason.