🧪 Problem-Driven Systems Lab
🧬 Caso 17 · Entrega

Migracion de esquema sin downtime

Un ALTER TABLE sobre una tabla caliente bloquea la aplicacion entera; expand-contract reparte el mismo trabajo en lotes que nadie nota.

OPERATIVO 🐘 PHP🐍 Python🟢 Node.js☕ Java🔵 .NET🐹 Go🦀 Rust

📈 Que cambia para el negocio

Permite cambiar el esquema de una tabla caliente sin ventana de mantenimiento ni 503 para el usuario final.

💼 Que demuestra tecnicamente

Muestra que el trabajo total de una migracion no cambia — lo que cambia es si se cobra junto con la app caida o repartido — y que el healthcheck en verde puede tapar 22 minutos de indisponibilidad.

✅ Evidencia que deja

  • Contrasta /migrate-blocking y /migrate-expand-contract con availability_pct medida DURANTE la migracion.
  • Hace visible longest_single_lock_ms: la metrica que decide si la app se cae, distinta del tiempo total.
  • Expone /migration/state con la fase actual, el progreso del backfill y el estado del feature flag.

👀 Que mirar al ejecutarlo

  • Si readers_failed es mayor que cero en la variante bloqueante y exactamente cero en la corregida.
  • Si longest_single_lock_ms baja de la duracion entera de la migracion a la de un solo lote.
  • Si lock_held_ms total es parecido en las dos: el trabajo no desaparece, se reparte.
Honestidad: No hay PostgreSQL detras: el lock de la tabla se modela con el read-write lock de cada runtime, que es honesto porque el mecanismo es el mismo — un escritor excluye a todos los lectores. La excepcion es PHP, donde flock SI es un lock del sistema operativo entre procesos. En PHP los lectores se recorren en secuencia porque el servidor embebido es de un solo proceso.

Como esta resuelto en cada stack

Expand-contract en cuatro fases con el read-write lock de cada runtime: flock LOCK_SH/LOCK_EX del sistema operativo en PHP — el unico que coordina procesos y no hilos —, RWLock construido a mano sobre Condition en Python porque la stdlib no lo trae, el event loop COMO lock en Node, ReentrantReadWriteLock en modo justo con tryLock(timeout) en Java, ReaderWriterLockSlim con TryEnterReadLock(ms) en .NET, sync.RWMutex con deadline armado por goroutine en Go, y RwLock con spin acotado en Rust — el unico caso del lab donde su respuesta es peor que la de los otros seis

StackHealth check localComposeDetalle
🐘 PHP 8.3http://localhost:8100/17/healthcompose.root.ymlREADME del stack
🐍 Python 3.12http://localhost:8200/17/healthcompose.python.ymlREADME del stack
🟢 Node.js 22http://localhost:8300/17/healthcompose.nodejs.ymlREADME del stack
☕ Java 21http://localhost:8400/17/healthcompose.java.ymlREADME del stack
🔵 .NET 8http://localhost:8500/17/healthcompose.dotnet.ymlREADME del stack
🐹 Go 1.23http://localhost:8600/17/healthcompose.go.ymlREADME del stack
🦀 Rust 1.83http://localhost:8700/17/healthcompose.rust.ymlREADME del stack

El expediente completo

El caso no empieza en el codigo: empieza en el sintoma y termina en el postmortem.

← Caso 16Caso 18 →