Patrón Expand/Contract
El patrón expand/contract (también llamado cambio paralelo) envía cambios de esquema aditivos mientras las versiones antiguas y nuevas de la aplicación se ejecutan, y luego elimina el esquema obsoleto solo después de que el tráfico se haya transferido por completo.
Receta
Fase 1 EXPAND - la migración añade una nueva columna/tabla (nullable o no utilizada)
Fase 2 DEPLOY - la app v2 escribe en ambas, antigua y nueva
Fase 3 BACKFILL - el trabajo llena la nueva columna para las filas históricas
Fase 4 DEPLOY - la app v3 lee solo la nueva
Fase 5 CONTRACT - la migración elimina la columna antigua
Cuándo usar esto: Renombrar columnas, cambiar tipos, dividir tablas o cualquier DDL que rompa la compatibilidad en tablas bajo tráfico activo.
Ejemplo de Trabajo
Renombrar full_name a display_name sin tiempo de inactividad:
-- Migración V10 EXPAND
ALTER TABLE app.users ADD COLUMN display_name text;-- App v2 escritura dual (código de aplicación)
INSERT INTO app.users (full_name, display_name, ...)
VALUES ($1, $1, ...);
UPDATE app.users SET display_name = full_name WHERE display_name IS NULL;-- Migración V11 BACKFILL (lote en aplicación o trabajo SQL)
UPDATE app.users
SET display_name = full_name
WHERE display_name IS NULL AND id BETWEEN $1 AND $2;-- Migración V12 CONTRACT (después de que se envíe la app v3)
ALTER TABLE app.users DROP COLUMN full_name;
ALTER TABLE app.users ALTER COLUMN display_name SET NOT NULL;Lo que esto demuestra:
- La columna antigua vive al menos dos ciclos de despliegue.
- El backfill se puede dividir por rangos de claves primarias.
- La fase de contracción es irreversible sin restauración; se controla mediante métricas.
Análisis Profundo
Técnicas de Expansión
| Tipo de cambio | Paso de expansión |
|---|---|
| Renombrar columna | Añadir nueva columna; escritura dual |
| Restricción NOT NULL | Añadir nullable; backfill; luego SET NOT NULL |
| Dividir tabla | Crear nueva tabla; trigger o escritura dual de la app |
| Cambiar tipo | Añadir amount_cents bigint; migrar desde amount numeric |
| Cambio de objetivo de FK | Añadir nueva columna FK nullable; backfill; cambiar |
Lista de Verificación de Seguridad de Contracción
- Ningún código de producción hace referencia al objeto antiguo (grep + grafo de despliegue)
- Ningún dashboard de BI consulta la columna antigua
- La replicación/suscriptores incluyen la nueva forma
- Copia de seguridad tomada antes de DROP
- Migración de contracción en ventana de bajo tráfico (DROP todavía bloquea brevemente)
Feature Flags
-- Opcional: rastrear la fase de migración en la tabla de configuración
INSERT INTO app.feature_flags (key, enabled) VALUES ('use_display_name', true);- Coordinar la fase del esquema con las feature flags de la aplicación.
- Revertir la flag de la aplicación sin migración de contracción si solo se ha expandido hasta ahora.
Posibles Problemas
- Contracción antes de que el backfill se complete - FALLA NOT NULL o la app lee NULLs. Solución: Puerta de enlace
SELECT count(*) WHERE new_col IS NULLen el pipeline de despliegue. - Escritura dual olvidada en una ruta de código - La herramienta de administración todavía escribe solo en la columna antigua. Solución: Búsqueda de código en todos los escritores; las pruebas de integración afirman ambas columnas.
- DROP en la misma versión que expand - Tiempo de inactividad o fallo en el orden de despliegue. Solución: Mínimo una versión entre expand y contract.
- Vista depende de la columna antigua - La migración de contracción falla o rompe informes. Solución: Actualizar vistas en la migración de expansión para usar un puente COALESCE.
- Caché de esquema ORM - Los pods antiguos fallan después de la contracción. Solución: Despliegue rodante asegura que no haya pods antiguos antes de que se ejecute el trabajo de contracción.
Alternativas
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
| DROP en ventana de mantenimiento | Aplicación interna pequeña | SLA 24/7 |
| Base de datos Blue/green | Cambio de forma masivo | El equipo carece de habilidad en replicación lógica |
| Vistas como capa de compatibilidad | Renombrar con muchos lectores | Rutas de escritura intensiva a través de vistas |
Preguntas Frecuentes
¿Cuántas versiones entre expand y contract?
Típicamente 2-3: expandir, desplegar escritura dual, desplegar corte de lectura, contraer. Ajustar a tu cadencia de versiones.
¿Triggers para la escritura dual?
Posible en SQL para aplicación uniforme; la escritura dual de la app es más fácil de probar. Los triggers añaden amplificación de escritura.
¿Renombrar vs añadir/eliminar?
RENAME COLUMN real es rápido pero rompe la app antigua instantáneamente. Expandir/contraer con un nuevo nombre es más seguro para cero tiempo de inactividad.
Relacionados
- Zero-Downtime DDL - índices concurrentes y NOT VALID
- Large Table Migrations - backfill por lotes
- Migrations Basics - disciplina SQL versionada
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, y PostGIS 3.5+.