Migraciones Sin Tiempo de Inactividad
Resumen
Disciplina de cambios de esquema que mantiene un sistema en funcionamiento durante un despliegue continuo. Durante un despliegue, los pods antiguos y los nuevos comparten una misma base de datos — por lo que el esquema debe ser compatible con ambas versiones de la aplicación a la vez. Combinar una migración destructiva con el despliegue que la necesita es la clásica interrupción autoinfligida.
Reglas de Autoría (todos los proyectos)
- Dos tipos de migración, mantenidos distintos: el conjunto
current/idempotente (iteración en desarrollo — se vuelve a ejecutar cada ciclo,CREATE OR REPLACE/IF NOT EXISTS, objetos en forma final, sinALTERposterior a la creación) y las migraciones committed (inmutables, ordenadas, lo que realmente se despliega).ALTER DEFAULT PRIVILEGESes el únicoALTERen línea aceptado. - La propiedad proviene de cómo se ejecutan las migraciones, no de
ALTER ... OWNERdispersos. - Una única fuente de verdad de bootstrap compartida por la ruta de inicio local/Docker y la base de datos shadow/test — cada entorno se construye de forma idéntica.
Expand/Contract (Cambio Paralelo)
Se aplica cuando: la base de datos contiene datos que deben sobrevivir al despliegue (staging con datos, producción). Exento: proyectos locales/reiniciables — pero escribe las migraciones commit como si esto aplicara, para que la promoción nunca sea una reescritura.
Un cambio de esquema se divide en tres versiones, nunca en una:
- Expand — agrega la nueva forma de manera aditiva, compatible hacia atrás: nueva columna nullable, nueva tabla, nueva función. La versión N de la aplicación sigue funcionando sin cambios.
- Migrate — realiza backfill en lotes acotados (nunca un
UPDATEque bloquee toda la tabla); escritura dual desde la app N+1 si es necesario; cambia las lecturas a la nueva forma. Solo ahora agregaNOT NULL(añade la restricciónNOT VALID, luegoVALIDATEpor separado). - Contract — solo después de que la versión N de la app esté completamente retirada, elimina la columna/restricción antigua en una versión posterior.
Trampas de PostgreSQL
| Trampa | Enfoque seguro |
|---|---|
Columna NOT NULL con valor predeterminado volátil/grande | Agregar nullable → backfill en lotes → agregar restricción NOT VALID → VALIDATE |
| Agregar una restricción | Agregar NOT VALID primero, luego VALIDATE CONSTRAINT (bloqueo más débil) |
| Construir un índice | CREATE INDEX CONCURRENTLY — no se puede ejecutar dentro de una transacción; aíslalo del envoltorio de transacción de la herramienta de migración |
| Una migración bloqueada que congela producción | Establece lock_timeout / statement_timeout para que falle rápidamente |
| Renombrar una columna | Es expand/contract: agregar nueva → backfill → cambiar lecturas → eliminar antigua, a través de versiones |
Por qué "CREATE OR REPLACE en forma final" no es suficiente en producción
Esa regla es ergonomía para el desarrollador y es correcta en desarrollo. En producción, con usuarios en vivo y un despliegue continuo, ambas versiones de la aplicación comparten el esquema durante la duración del despliegue — por lo que expand/contract es lo que hace seguro el cambio. No confundas los dos conceptos.
Justificación completa: Artículo XVII de la constitución, incluido en engineering-constitution/references/engineering-constitution.md. Pipeline de despliegue: cloud-delivery-aks.