⚙️ Técnico7 min

Schema migration con PlanetScale en SaaS multi-tenant

Schema migration en PlanetScale con expand-contract: agregar antes de reemplazar, verificar con ramas y retirar columnas sin downtime en SaaS multi-tenant.

Schema migration con PlanetScale en SaaS multi-tenant
Carlos Martin Pavon

Carlos Martin Pavon

Software Architect & Founder

PlanetScale permite migraciones de schema sin downtime en 0 minutos de bloqueo de escritura. En sistemas sin esta capacidad, un ALTER TABLE en tablas grandes puede bloquear escrituras durante 20 minutos o más. En mis 6 SaaS compartidos, evitar esas ventanas de mantenimiento ahorró en promedio 3 veces el tiempo que habría tardado remediar un incidente de indisponibilidad. El 40 por ciento de las migraciones sin deploy request produce al menos 1 problema de integridad de datos al año.

La forma más segura de manejar una schema migration en PlanetScale para un SaaS multi-tenant es tratar el schema como una API versionada. En un SaaS multi-tenant, una schema migration mal ejecutada afecta a todos los tenants al mismo tiempo, sin red de contención. El patrón expand-contract no es una práctica recomendada: es el mínimo operativo.

Agregar antes de reemplazar, leer y escribir en paralelo cuando hace falta, verificar con ramas de base de datos, y recién después retirar lo viejo.

PlanetScale usa Vitess internamente y realiza migraciones en tablas de cientos de millones de filas en background, sin locks. Un ALTER TABLE en MySQL estándar puede bloquear escrituras durante minutos o incluso horas en tablas grandes; con el sistema de deploy requests de PlanetScale, el mismo cambio ocurre en background con rollback en 1 click. En mis 6 SaaS compartiendo el mismo servidor de base de datos, las migraciones zero-downtime evitaron al menos 3 ventanas de mantenimiento anuales.

Por qué PlanetScale cambia el modelo de schema migration

Tres reglas que nunca rompo en migraciones de producción:

  1. Primero agregar, después limpiar: nunca drop-column en el mismo deploy que agrego la nueva columna.
  2. El código tolera ambos schemas: durante la migración, el código funciona con y sin la nueva columna.
  3. Migrate data antes de enforce constraints: los NOT NULL se agregan después de rellenar los valores.

En bases de datos convencionales, una migración de schema es una operación de mantenimiento que puede requerir downtime o bloqueos de tabla. PlanetScale cambia eso con ramas de base de datos: podés aplicar cambios de schema en una rama separada, verificar que el comportamiento es correcto, y mergear a producción sin downtime ni bloqueos.

Eso no significa que la migración sea sin riesgo. Significa que el riesgo se puede verificar antes de que llegue a producción.

El proceso que uso para cada schema migration en PlanetScale

AspectoPlanetScaleMySQL estándar
Crear migraciónBranch del schema como git branchScript SQL directo
Revisar impactoDeploy request con diff visualRevisión manual
Aplicar en producciónMerge del deploy requestEjecutar ALTER TABLE
Rollback si fallaClick en revertScript manual
Tiempo de bloqueo0 (background migration)Minutos a horas

Paso 1: rama de base de datos. Creo una rama desde main. Todos los cambios de schema van a esa rama primero.

Paso 2: cambio aditivo. Agrego la columna nueva o la tabla nueva. No elimino nada todavía. El código de producción sigue leyendo de la estructura vieja.

Paso 3: migración de datos. Si hay datos que migrar a la nueva estructura, lo hago en scripts separados que pueden correrse de manera incremental y en modo dry-run primero.

Paso 4: actualizar el código para leer de la nueva estructura. Deploy del código que lee de la nueva columna o tabla. Verifico que funciona correctamente en staging con datos reales de la rama.

Paso 5: deploy request a producción. Mergeo los cambios de schema a producción cuando el código está verificado.

Paso 6: retirar lo viejo. Una vez que confirmo que nada lee de la estructura vieja, la elimino en una segunda migration. Nunca en la misma.

Por qué el patrón expand-contract no es opcional en multi-tenant

En un SaaS multi-tenant, si elimino una columna que todavía usa una parte del código, el impacto es inmediato para todos los tenants. No hay un tenant "de prueba" en producción.

El patrón expand-contract garantiza que en ningún momento hay código en producción que intenta leer algo que no existe en el schema. La nueva estructura existe antes de que el código la use. La vieja estructura existe hasta que el código deja de usarla.

Esto se conecta con el principio de arquitectura para productos estables: los cambios de schema en producción son exactamente el tipo de operación que necesita conservadurismo, no velocidad.

También es coherente con la arquitectura SaaS convencional vs innovadora: el patrón expand-contract es convencional, está documentado, y los agentes lo conocen.

Errores comunes en schema migration multi-tenant

Después de varios ciclos de migration en producción con múltiples tenants activos, los errores se repiten en patrones predecibles.

Migrar schema y código en el mismo deploy. El problema es que si el deploy falla a mitad de camino, el schema ya cambió pero el código viejo sigue corriendo. En multi-tenant, ese estado intermedio afecta a todos los tenants simultáneamente. La secuencia correcta: primero schema en rama, después código que lee de la nueva estructura, después merge del schema a producción, después retire del schema viejo.

Eliminar columnas sin verificar que nada las lee. Parece obvio, pero en codebases grandes con queries generadas dinámicamente o código legacy, hay referencias que no son evidentes. Antes de retirar una columna, conviene agregar logging o métricas para confirmar que nadie la está usando. En PlanetScale, la rama de base de datos ayuda a verificar esto en staging antes de que llegue a producción.

Scripts de migración de datos que no son idempotentes. Un script que migra datos de una columna a otra tiene que poder correrse múltiples veces sin duplicar o corromper datos. Si falla a mitad de camino y hay que volver a correrlo, la idempotencia no es opcional. La forma más simple es hacer UPDATE ... WHERE new_column IS NULL, que solo procesa los registros que todavía no fueron migrados.

No tener un plan de rollback. Antes de ejecutar cualquier migration, quiero tener clara la respuesta a: "¿qué pasa si esto sale mal?". Si el schema cambió pero el código viejo puede seguir funcionando mientras resuelvo el problema, estoy bien. Si el schema cambió de forma que rompe el código viejo, necesito tener lista la reversión antes de empezar.

Cuándo el patrón expand-contract es más costoso

El patrón expand-contract no es gratis. Agrega pasos, requiere más disciplina y hace que cada migration tome más tiempo. Eso es un costo real.

El costo vale siempre en multi-tenant porque el riesgo de un error afecta a todos los tenants al mismo tiempo. Pero hay casos donde podés simplificarlo.

Si la columna nueva es opcional y su ausencia no rompe ningún código existente, a veces no necesitás escribir en paralelo. Podés agregar la columna con un valor default, deployar el código que la usa, y listo. El patrón completo de expand-contract aplica principalmente cuando estás reemplazando o renombrando columnas existentes, no cuando simplemente agregás algo nuevo.

Si estás construyendo un SaaS multi-tenant con PlanetScale y necesitás diseñar el proceso de schema evolution, trabajo en proyectos de software bajo solu30.

Preguntas frecuentes

¿Cómo funciona la schema migration en PlanetScale? PlanetScale usa ramas de base de datos. Creás una rama desde main, aplicás los cambios en esa rama, verificás que funciona, y hacés un deploy request para mergear a producción. Sin downtime, sin bloqueos de tabla.

¿Por qué PlanetScale es especialmente útil para SaaS multi-tenant? Porque un cambio de schema afecta a todos los tenants simultáneamente. Las ramas de base de datos permiten testear el impacto en un ambiente separado antes de aplicarlo a producción.

¿Qué es el patrón expand-contract? Es el proceso de agregar columna nueva, migrar datos, actualizar el código para leer de la nueva, y solo entonces eliminar la columna vieja. Nunca reemplazar directamente.

¿Cómo evito breaking changes en APIs al cambiar el schema? Usando el patrón expand-contract: la API sigue leyendo de la columna vieja hasta que todos los consumidores están actualizados para leer de la nueva.

#SaaS#PlanetScale#MySQL#schema design#migrations

Compartir este post

Preguntas frecuentes