Pular para conteúdo

Database Rollback

Como reverter migrations do banco de dados.

Quando Fazer Rollback

  • Migration causou erro em production
  • Performance degradou significativamente
  • Dados corrompidos
  • Incompatibilidade descoberta

Processo de Rollback

1. Avaliar Situação

# Ver versão atual
alembic -c alembic.prod.ini current

# Ver histórico
alembic -c alembic.prod.ini history

# Identificar revision para voltar

2. Backup ANTES de Rollback

# CRÍTICO: Backup mesmo para rollback!
aws rds create-db-snapshot \
  --db-instance-identifier prod-db \
  --db-snapshot-identifier "pre-rollback-$(date +%Y%m%d-%H%M%S)"

# Aguardar backup completar
aws rds wait db-snapshot-completed \
  --db-snapshot-identifier pre-rollback-...

3. Executar Rollback

# Voltar 1 versão
alembic -c alembic.prod.ini downgrade -1

# Ou voltar para revisão específica
alembic -c alembic.prod.ini downgrade abc123

# Verificar
alembic -c alembic.prod.ini current

4. Verificar

# Rodar testes
python scripts/verify_schema.py

# Verificar dados
psql -h prod-db.rds.amazonaws.com -U app_user -d app_db -c "SELECT count(*) FROM users;"

# Testar aplicação
curl https://api.seuapp.com/health/db

5. Rollback Código

Se migration já foi deployada com novo código:

# Rollback application code também
git revert <commit-with-code-changes>
git push origin main
# Deploy automático

Rollback Complexo

Migration com Data Loss

Se migration deletou dados:

# RUIM: Migration sem backup de dados
def upgrade():
    op.drop_column('users', 'legacy_field')  # Data lost!

# BOM: Migration com backup
def upgrade():
    # Backup data first
    op.execute("""
        CREATE TABLE users_legacy_backup AS
        SELECT id, legacy_field FROM users
    """)

    op.drop_column('users', 'legacy_field')

def downgrade():
    # Restore from backup
    op.add_column('users', sa.Column('legacy_field', sa.String()))
    op.execute("""
        UPDATE users u
        SET legacy_field = b.legacy_field
        FROM users_legacy_backup b
        WHERE u.id = b.id
    """)
    op.execute("DROP TABLE users_legacy_backup")

Rollback de Emergency

Point-in-Time Recovery (PITR)

Para casos extremos:

# Restaurar para timestamp específico
aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier prod-db \
  --target-db-instance-identifier prod-db-restored \
  --restore-time 2026-01-20T14:00:00Z

# AVISO: Cria nova instância, não substitui atual
# Requer update de connection strings

Restore from Snapshot

# Listar snapshots
aws rds describe-db-snapshots \
  --db-instance-identifier prod-db

# Restore
aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier prod-db-restored \
  --db-snapshot-identifier pre-migration-20260120-140000

Rollback Sem Downgrade

Se downgrade não existe ou não funciona:

Opção 1: Forward Fix

# Criar nova migration que corrige
alembic revision -m "fix previous migration"

# Edit migration file with fix
def upgrade():
    # Fix the issue from previous migration
    ...

Opção 2: Manual Fix

-- Connect to database
psql -h prod-db... -U app_user -d app_db

-- Manual SQL to fix
BEGIN;
-- Your fix here
COMMIT;

Testing Rollback

Sempre Testar Downgrade

# Staging environment
alembic upgrade head
alembic downgrade -1
alembic upgrade head

# Verificar que tudo funciona
pytest tests/integration/

Comunicação

Durante Rollback

🔄 **Rolling back migration in production**

**Incident**: #123
**Reason**: Migration caused errors
**ETA**: 10 minutes
**Impact**: Brief connection issues expected

Updates every 5 minutes.

Após Rollback

✅ **Migration rollback completed**

**Status**: Resolved
**Duration**: 12 minutes
**Verification**: All checks passing

Root cause analysis and fix in progress.
Postmortem: [link]

Prevention

  • Sempre testar migrations em staging
  • Code review de migrations
  • Backup automático antes de production migrations
  • Downgrade sempre implementado
  • Dry-run quando possível

Referências