CommunityRedacción y edicióngithub.com

db-migration-safe

Planifica, analiza y ejecuta migraciones SQL sin tiempo de inactividad: detecta bloqueos, reescribe DDL inseguras, genera rollbacks y controla la ejecución (prioriza PostgreSQL, usa squawk + eugene).

¿Qué es db-migration-safe?

Esta skill es un asistente para migraciones de esquemas SQL seguras, centrado principalmente en PostgreSQL. Detecta automáticamente sentencias DDL que pueden causar bloqueos o detención de escrituras, las reescribe en migraciones multietapa sin tiempo de inactividad (usando `CONCURRENTLY`, `NOT VALID` → `VALIDATE CONSTRAINT`, etc.) y genera automáticamente el comando de reversión correspondiente. Su flujo de trabajo de tres fases (PLAN → VALIDATE → EXECUTE) garantiza que el análisis estático y la simulación en un servidor PostgreSQL efímero nunca toquen la base de datos de producción; la fase EXECUTE está restringida conductualmente a petición explícita del usuario. La skill se integra con las herramientas de linaje `squawk` y `eugene` (que deben instalarse por separado) y puede usarse tanto desde la línea de comandos como desde un IDE o agente conversacional. Aunque el soporte para MySQL existe, es reconocidamente más débil y se basa en heurísticas de InnoDB Online DDL.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add alexbobkovv/db-migration-safe

Installed? Explore more Redacción y edición skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace db-migration-safe?

Haz que las migraciones de esquema SQL sean seguras. Esta skill detecta riesgos de bloqueo/parada en una migración, reescribe DDL inseguras en migraciones multietapa sin tiempo de inactividad, genera una reversión y controla la ejecución real mediante un flujo planificar → validar → ejecutar.

PostgreSQL es la ruta fuerte y determinista. MySQL es un híbrido honesto (ver references/mysql-catalog.md) — materialmente más débil, y etiquetado como tal.

Esta skill invoca externamente a los linters (squawk, eugene) y herramientas OSC (gh-ost/pt-osc). No los incluye. Si faltan, instálalos según references/tool-setup.md. El análisis estático (squawk + eugene lint) no necesita base de datos; eugene trace y EXECUTE necesitan una base de datos PostgreSQL.

Cuándo usar / cuándo NO usar

Úsalo para cualquier DDL: ALTER TABLE, CREATE INDEX, ADD/DROP COLUMN, cambios de tipo, restricciones (NOT NULL, CHECK, UNIQUE, claves foráneas), renombrados y rellenados — ya sea SQL escrito a mano o generado por un ORM. No lo uses para consultas SELECT/DML de solo informes, ni para motores de esquema declarativos que gestionan su propio paso de aplicación (ej. Atlas) — esos tienen su propia ruta de seguridad.

Contrato de seguridad (léelo primero)

  • PLANIFICAR y VALIDAR nunca tocan la base de datos del usuario. Son solo análisis y pueden ejecutarse libremente.
  • eugene trace ejecuta la migración contra un servidor PostgreSQL efímero y desechable (o un clon desechable) y revierte por defecto. Nunca toca producción.
  • EJECUTAR aplica DDL real a una base de datos real. Nunca ejecutes EJECUTAR a menos que el usuario pida explícitamente aplicar / ejecutar / enviar la migración. Siempre haz una prueba en seco primero, siempre envuélvela en lock_timeout, nunca ejecutes CONCURRENTLY dentro de una transacción. EJECUTAR es una guía procedimental que el usuario maneja — no hay un script de auto-ejecución.

Nota sobre auto-invocación: esta es una skill única, por lo que su frontmatter gobierna todo el archivo. PLANIFICAR/VALIDAR son seguras de auto-disparar, por lo que la invocación del modelo se deja habilitada. La fase EJECUTAR está restringida conductualmente por la regla anterior. Si prefieres que toda la skill solo se active por el usuario, añade disable-model-invocation: true al frontmatter — requerirá invocación explícita incluso para el análisis.


PLANIFICAR

Ejecuta el analizador estático sobre el archivo de migración. No se necesita base de datos.

python3 scripts/analyze.py ruta/a/migracion.sql --dialect postgres
# añade --pg-version 13.0 para filtrar reglas específicas de versión
# añade --json para salida legible por máquina (CI)

analyze.py ejecuta squawk y eugene lint, normaliza ambos en un único veredicto, y termina con código distinto de cero si hay algún hallazgo de nivel error (así que también sirve como barrera en CI). Para --dialect mysql aplica las heurísticas integradas de InnoDB Online DDL (no existe un linter estático gratuito de bloqueos para MySQL; ver references/mysql-catalog.md).

Lee el veredicto combinado:

  1. Hallazgos — cada uno tiene un source (squawk/eugene/heuristic), un id de regla, un level (error/warning), la línea, el mensaje y una solución alternativa.
  2. Interpretación — mapea los ids de regla a su significado simple mediante references/squawk-rules.md y references/eugene-hints.md.
  3. Tamaño de la tabla — los linters estáticos no pueden diferenciar 100 filas de 100k. Verifica el tamaño real con scripts/table_size.sql (necesita acceso a la BD) antes de decidir si una operación "lenta" es realmente un problema:
    psql "$DATABASE_URL" -v nombre_tabla=public.pedidos -f scripts/table_size.sql
    
  4. Escalar a trace cuando el veredicto involucre una posible reescritura de tabla/índice (E5, E10), una FK sin índice de cobertura (E15), o cualquier bloqueo cuya duración real dependa del número de filas. trace observa el modo de bloqueo real y cuánto tiempo se mantiene:
    python3 scripts/trace.py ruta/a/migracion.sql            # servidor temporal efímero
    python3 scripts/trace.py ruta/a/migracion.sql --host bd.staging --port 5432 \
        --user app --database app                              # clon desechable
    

Luego genera tres artefactos para el usuario:

  • un INFORME DE RIESGO (el veredicto, en lenguaje sencillo),
  • una REESCRITURA SEGURA (siguiente sección), y
  • una REVERSIÓN generada (scripts/gen_rollback.py).

Reescribir seguras

Para cada hallazgo de error/advertencia, reemplaza la sentencia insegura con la reescritura sin tiempo de inactividad catalogada. La tabla completa de 11 operaciones → reescrituras está en references/postgres-catalog.md (MySQL: references/mysql-catalog.md). Los dos primitivos de PostgreSQL detrás de casi todas las reescrituras:

  • CONCURRENTLY — construir/eliminar índices sin un bloqueo ACCESS EXCLUSIVE de escritura (debe ejecutarse fuera de una transacción).
  • NOT VALID → VALIDATE CONSTRAINT — añadir una restricción en dos fases para que la costosa validación completa de la tabla tome un bloqueo débil SHARE UPDATE EXCLUSIVE en lugar de bloquear escrituras.

Referencia rápida (consulta el catálogo para el SQL multietapa exacto y los matices de versión de PG):

InseguraReescribir segura
CREATE INDEXCREATE INDEX CONCURRENTLY (fuera de txn)
ADD COLUMN ... NOT NULL [DEFAULT]columna nullable → establecer valor por defecto → rellenado por lotes → NOT NULL mediante CHECK
SET NOT NULLADD CHECK (c IS NOT NULL) NOT VALID → VALIDATE → SET NOT NULL → eliminar check
ADD FOREIGN KEY... NOT VALID → VALIDATE CONSTRAINT
ADD CHECK... CHECK (...) NOT VALID → VALIDATE CONSTRAINT
ADD UNIQUECREATE UNIQUE INDEX CONCURRENTLY → ADD CONSTRAINT ... USING INDEX
ALTER COLUMN ... TYPEexpandir-contraer (nueva columna → doble escritura → rellenado → intercambio → eliminar)
renombrar columna/tablaexpandir-contraer, o VIEW de compatibilidad
rellenado UPDATElotes acotados en transacciones cortas, con limitación de velocidad

Escribe las sentencias reescritas en un nuevo archivo (ej. migracion.segura.sql) para que pueda ser revalidado.

Reversión

Genera la migración inversa a partir de la DDL (reescrita):

python3 scripts/gen_rollback.py migracion.segura.sql > migracion.rollback.sql

gen_rollback.py emite la inversa de cada operación catalogada reconocida (ADD COLUMN→DROP COLUMN, CREATE INDEX CONCURRENTLY→DROP INDEX CONCURRENTLY, …). Revisa la salida:

  • Las operaciones irreversibles (DROP COLUMN, DROP TABLE, estrechamiento de tipo) no pueden restaurarse solo con DDL. El generador las marca y emite el SQL de respaldo de tabla que la migración hacia adelante debe ejecutar primero (<tabla>_<col>_backup_<fecha>) más una restauración de mejor esfuerzo. Nunca emite una inversa incorrecta silenciosamente.
  • Las formas de sentencia desconocidas producen un comentario explícito se requiere reversión manual: <motivo>, no una suposición.

VALIDAR

Esta es la barrera. Vuelve a ejecutar el analizador sobre la migración reescrita:

python3 scripts/analyze.py migracion.segura.sql --dialect postgres

Solo continúa cuando pase con cero hallazgos de nivel error (las advertencias son configurables mediante .squawk.toml / -- eugene: ignore). Si aún marca errores, regresa a Reescribir seguras y repite el ciclo. Vuelve a ejecutar trace si la reescritura cambió qué bloqueos se toman.

EJECUTAR (restringida — solo activada por el usuario)

No entres en esta fase a menos que el usuario haya pedido explícitamente aplicar la migración.

Aplica la migración validada con protecciones de bloqueo. Primero haz una prueba en seco.

  1. Acota cada bloqueo. Envuelve las sentencias bloqueantes para que fallen rápido en lugar de encolarse detrás de una transacción larga y detener todo el tráfico:
    SET lock_timeout = '5s';
    SET statement_timeout = '0';  -- o un límite razonable para la operación específica
    
  2. CONCURRENTLY se ejecuta fuera de una transacción. No pongas CREATE/DROP INDEX CONCURRENTLY en un bloque BEGIN/COMMIT o en un ejecutor de migraciones transaccional. Después de una construcción concurrente fallida, elimina el índice INVALID sobrante antes de reintentar.
  3. Restricciones en dos fases. Ejecuta el paso NOT VALID, deja que haga commit, luego VALIDATE CONSTRAINT en una sentencia separada.
  4. Rellenados por lotes. Actualiza en fragmentos acotados en transacciones cortas separadas, con una pausa breve entre lotes para que la replicación y el autovacío se mantengan al día:
    -- repetir hasta que 0 filas sean afectadas
    UPDATE t SET col = expr
    WHERE id IN (SELECT id FROM t WHERE col IS NULL ORDER BY id LIMIT 5000)
    
  5. Prueba en seco toda la secuencia contra el entorno efímero/clon mediante scripts/trace.py una última vez, confirma passed_all_checks, luego aplica al destino.

Para MySQL, fuerza ALGORITHM=…, LOCK=… para que el servidor devuelva error en lugar de caer silenciosamente en una reconstrucción COPY, y delega las reescrituras grandes a gh-ost/pt-osc — ver references/mysql-catalog.md.


Índice de referencias

  • references/postgres-catalog.md — las 11 operaciones inseguras → reescrituras seguras (conocimiento central).
  • references/mysql-catalog.md — matriz InnoDB Online DDL + delegación OSC (ruta más débil).
  • references/squawk-rules.md — ids de reglas de squawk y qué detecta cada una.
  • references/eugene-hints.md — eugene E1–E15 / W12–W14 y sus soluciones alternativas.
  • references/tool-setup.md — instalación de squawk / eugene / gh-ost; PostgreSQL efímero.

Scripts

  • scripts/analyze.py — orquesta squawk + eugene lint (o heurísticas MySQL) → un veredicto. Termina con código distinto de cero en hallazgos de nivel error.
  • scripts/trace.py — ejecuta eugene trace (servidor temporal efímero o clon) → informe de bloqueo real.
  • scripts/gen_rollback.py — genera la migración inversa; marca operaciones irreversibles.
  • scripts/table_size.sql — sonda de tamaño pg_class.reltuples.

Todos los scripts son solo biblioteca estándar de Python 3 — no requieren pip install.

Skills relacionados