162 lines
6.5 KiB
Markdown
162 lines
6.5 KiB
Markdown
# PostgreSQL Point-in-Time Recovery (PITR)
|
|
|
|
**Estado:** PENDIENTE — no implementado. Plan capturado para ejecución posterior. Es una tarea de infraestructura, no de código.
|
|
|
|
## Problema
|
|
|
|
Hoy el backup es un dump diario a las 01:00 AM (`scripts/backup.sh`). Si el disco o la BD fallan a las 23:59, **se pierden hasta 24 horas** de datos:
|
|
|
|
- CFDIs sincronizados del SAT
|
|
- CFDIs emitidos vía Facturapi (críticos — ya se cobraron)
|
|
- Payments registrados
|
|
- Cambios de suscripción
|
|
- Alertas resueltas
|
|
- Cualquier configuración modificada ese día
|
|
|
|
Para datos fiscales esto es **legalmente problemático**. El SAT tiene plazos de declaración; perder un día completo puede significar multas o rechazos.
|
|
|
|
PITR permite recovery a cualquier momento en el pasado (granularidad minutos/segundos) combinando:
|
|
|
|
- **Base backup** periódico (ej: diario)
|
|
- **WAL (Write-Ahead Log) archiving** continuo
|
|
|
|
## Propuesta
|
|
|
|
Migrar de `pg_dump` diario a **pgBackRest** o **wal-g** con archivos WAL subidos a almacenamiento externo.
|
|
|
|
### Stack recomendado
|
|
|
|
**Opción A: pgBackRest** (más robusto, más maduro)
|
|
- Pros: backups incrementales, encryption, verificación de integridad, restoration testing
|
|
- Cons: más complejo de setup, más moving parts
|
|
- Docs: https://pgbackrest.org/
|
|
|
|
**Opción B: wal-g** (más simple, cloud-native)
|
|
- Pros: setup más rápido, buen fit para S3/GCS
|
|
- Cons: menos features que pgBackRest
|
|
- Docs: https://github.com/wal-g/wal-g
|
|
|
|
**Recomendación:** wal-g si ya usamos/planeamos usar S3; pgBackRest si queremos más control y el almacenamiento es NFS/disco dedicado.
|
|
|
|
### Almacenamiento de WAL archives
|
|
|
|
Opciones, de mejor a peor:
|
|
|
|
1. **S3 bucket** (AWS o compatible: Backblaze B2, Wasabi, DigitalOcean Spaces) — off-site, durable, ~$5/mes para este volumen
|
|
2. **Servidor remoto dedicado** via SSH (rsync/SFTP) — más lento, más control
|
|
3. **Disco adicional local** — NO recomendado (falla del server = pierde backup)
|
|
|
|
### Configuración Postgres
|
|
|
|
```ini
|
|
# postgresql.conf
|
|
wal_level = replica
|
|
archive_mode = on
|
|
archive_command = 'wal-g wal-push %p' # O equivalente pgBackRest
|
|
archive_timeout = 300 # Force a WAL segment at least every 5 min
|
|
```
|
|
|
|
### Frequency
|
|
|
|
- **Base backup:** 1x / semana (domingos 02:00 AM)
|
|
- **WAL archive:** continuo (cada WAL de 16MB o cada 5 min, lo que ocurra primero)
|
|
|
|
Recuperación: base del último domingo + WAL segments hasta el momento T → reconstruye exactamente el estado en T.
|
|
|
|
### RPO / RTO target
|
|
|
|
- **RPO (Recovery Point Objective):** 5 minutos de pérdida máxima (por el `archive_timeout`)
|
|
- **RTO (Recovery Time Objective):** 30-60 min (download base backup + replay WAL + switch)
|
|
|
|
vs. hoy: RPO 24h, RTO ~1h con el dump.
|
|
|
|
## Pasos para implementar
|
|
|
|
1. **Elegir destino de WAL** (recomiendo Backblaze B2 o Wasabi por precio / durabilidad)
|
|
2. **Instalar wal-g** en el server de Postgres
|
|
3. **Configurar credenciales** (env vars para S3, o archivo de config)
|
|
4. **Modificar `postgresql.conf`** con `archive_mode`, `archive_command`, etc. + reiniciar Postgres
|
|
5. **Tomar primer base backup** manual con `wal-g backup-push`
|
|
6. **Crontab:** base backup semanal, monitoreo
|
|
7. **Script de restore** documentado y probado (¡sin probar no es backup!)
|
|
8. **Monitoreo:** alerta si no hay WAL push en >10 min (indica que archive falla)
|
|
9. **Runbook de disaster recovery** — pasos exactos para restaurar
|
|
|
|
## Testing de restore
|
|
|
|
Componente crítico — sin haberlo probado, no hay backup real.
|
|
|
|
Ritual cada trimestre:
|
|
1. Elegir timestamp arbitrario en el pasado (ej: hace 3 días a las 14:00)
|
|
2. Spawn Postgres nuevo en máquina de staging
|
|
3. `wal-g backup-fetch` del backup base
|
|
4. `wal-g wal-fetch` del WAL hasta el timestamp
|
|
5. Verificar que la BD está consistente + data esperada está presente
|
|
6. Documentar tiempo real de restore (ajusta RTO estimado)
|
|
|
|
## Consideraciones extra
|
|
|
|
### Encriptación
|
|
|
|
wal-g soporta encryption de WAL + backups con AWS KMS o pgp. **Recomendado para datos fiscales** — incluso si alguien compromete el bucket S3, no puede leer los archivos.
|
|
|
|
### Retention
|
|
|
|
- Backups/WAL de últimos 30 días: retain
|
|
- 31-90 días: 1 backup base semanal
|
|
- 90+ días: 1 backup base mensual (para compliance SAT de 5 años)
|
|
|
|
Configurable en wal-g via `WALG_DELTA_MAX_STEPS` y policy manual.
|
|
|
|
### Costo estimado
|
|
|
|
Volumen actual (estimado):
|
|
- Base backup: ~1-5 GB comprimido (depende de CFDIs acumulados)
|
|
- WAL diario: ~100-500 MB/día
|
|
|
|
S3 cost (Backblaze B2 ~$6/TB/mes):
|
|
- 30 días hot retention ≈ 30 GB ≈ $0.20/mes
|
|
- 5 años retention (archivos mensuales) ≈ 60 GB ≈ $0.40/mes
|
|
|
|
**Total estimado: ~$1-5/mes** dependiendo del vendor. Peanuts comparado con lo que protege.
|
|
|
|
## Alcance
|
|
|
|
| Tarea | Estimación |
|
|
|-------|-----------|
|
|
| Elegir vendor + crear bucket | 30 min |
|
|
| Instalar + configurar wal-g | 2 h |
|
|
| Configurar Postgres + reiniciar | 1 h |
|
|
| Primer base backup + verificar | 1 h |
|
|
| Script de restore + primer drill | 2 h |
|
|
| Crontab + monitoreo básico | 1 h |
|
|
| Runbook de DR escrito | 2 h |
|
|
| **Total** | **1-2 días** |
|
|
|
|
## Riesgos
|
|
|
|
1. **Archive command backup pressure:** si S3 está lento o hay ratelimit, `archive_command` se enchola y Postgres se satura. Mitigación: usar `archive_library` en Postgres 15+ con async archiving.
|
|
2. **Reinicio de Postgres requerido:** cambiar `wal_level` o `archive_mode` requiere restart. Coordinar con deploy window.
|
|
3. **Credenciales de S3 en server:** si server se compromete, las keys permiten borrar backups. Mitigación: usar IAM policy con "write only" (no delete) al bucket.
|
|
4. **Billing de S3:** vigilar primeros meses por si el volumen es mayor al estimado.
|
|
5. **Testing real importante:** sin drill de restore, es teatro de seguridad.
|
|
|
|
## Out of scope
|
|
|
|
- Replicación streaming (hot standby) — mucho más caro, diferente problem (HA vs DR)
|
|
- Multi-region — hasta que haya tráfico significativo fuera de CDMX
|
|
- Backup de BDs tenant individualmente — las BDs tenant viven en el mismo cluster Postgres, backup a nivel cluster las cubre todas
|
|
|
|
## Archivos a tocar
|
|
|
|
- `scripts/backup.sh` — deprecar o convertir en wrapper de wal-g
|
|
- `deploy/wal-g.conf` (nuevo) — config de wal-g
|
|
- `deploy/postgres/postgresql.conf` snippet (nuevo) — cambios a aplicar
|
|
- `docs/architecture/disaster-recovery.md` (nuevo) — runbook
|
|
- `docs/architecture/deployment.md` — actualizar sección de backups
|
|
|
|
## Relación con otros planes
|
|
|
|
- **`2026-04-14-audit-log.md`:** si un evento se perdió en la ventana de PITR, el audit log en S3 puede complementar la reconstrucción.
|
|
- **Infra futura (HA / multi-region):** PITR es precursor lógico — una vez que tenemos WAL archiving, agregar una standby que consuma esos WAL es incremental.
|