Community라이팅 & 에디팅github.com

db-migration-safe

제로 다운타임 SQL 마이그레이션 안전성을 제공하는 Agent Skill: 잠금 위험 탐지, 안전하지 않은 DDL 재작성, 롤백 자동 생성 및 실행 게이트 (Postgres 우선, squawk + eugene 래핑).

db-migration-safe란 무엇인가요?

이 스킬은 데이터베이스 스키마 마이그레이션의 안전성을 보장하기 위해 설계된 Agent Skill입니다. 주로 SQL 마이그레이션 파일에서 잠금(locking)이나 블로킹(blocking) 위험을 감지하고, 안전하지 않은 DDL 문을 제로 다운타임 다단계 마이그레이션으로 재작성하며, 롤백을 자동 생성합니다. 전체 작업 흐름은 PLAN → VALIDATE → EXECUTE 단계로 구성되어 위험을 사전에 분석하고 검증한 후에만 실제 실행을 허용합니다. Postgres 데이터베이스에 최적화되어 있으며, MySQL은 제한적으로 지원합니다. 외부 린터(squawk, eugene)와 온라인 스키마 변경 도구(gh-ost, pt-osc)를 사용하지만 스킬에 포함되지 않으므로 별도 설치가 필요합니다. 정적 분석은 데이터베이스 연결 없이 가능하며, eugene trace나 EXECUTE 단계는 Postgres 데이터베이스가 필요합니다. EXECUTE 단계는 사용자가 명시적으로 요청할 때만 수행하도록 행동적으로 게이트되어 있어 자동 실행되지 않습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add alexbobkovv/db-migration-safe

Installed? Explore more 라이팅 & 에디팅 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

db-migration-safe은(는) 무엇을 하나요?

SQL 스키마 마이그레이션을 안전하게 만듭니다. 이 스킬은 마이그레이션에서 잠금/블로킹 위험을 감지하고, 안전하지 않은 DDL을 제로 다운타임 다단계 마이그레이션으로 재작성하며, 롤백을 생성하고, 실제 실행을 계획 → 검증 → 실행 워크플로우 뒤에 게이트합니다.

Postgres는 강력하고 결정적인 경로입니다. MySQL은 정직한 하이브리드입니다(references/mysql-catalog.md 참조) — 실질적으로 약하며, 그렇게 표시됩니다.

이 스킬은 외부 린터(squawk, eugene) 및 OSC 도구(gh-ost/pt-osc)를 쉘로 호출합니다. 포함되어 있지 않습니다. 누락된 경우 references/tool-setup.md에 따라 설치하십시오. 정적 분석(squawk + eugene lint)은 데이터베이스가 필요하지 않습니다. eugene trace와 EXECUTE는 Postgres가 필요합니다.

사용 시기 / 사용하지 말아야 할 시기

사용: 모든 DDL: ALTER TABLE, CREATE INDEX, ADD/DROP COLUMN, 타입 변경, 제약 조건(NOT NULL, CHECK, UNIQUE, 외래 키), 이름 변경, 백필(backfill) — 직접 작성한 SQL이든 ORM에서 생성되었든 관계없습니다. 사용하지 마십시오: 순수 SELECT/DML 보고 쿼리, 또는 자체 적용 단계를 소유하는 선언적 스키마 엔진(예: Atlas) — 해당 도구는 자체 안전 경로가 있습니다.

안전 계약 (먼저 읽으십시오)

  • PLAN과 VALIDATE는 사용자 데이터베이스에 절대 접촉하지 않습니다. 분석 전용이며 자유롭게 실행될 수 있습니다.
  • **eugene trace**는 임시 폐기용 Postgres(또는 disposable clone)에 대해 마이그레이션을 실행하고 기본적으로 롤백합니다. 프로덕션에 절대 접촉하지 않습니다.
  • EXECUTE는 실제 데이터베이스에 실제 DDL을 적용합니다. 사용자가 명시적으로 마이그레이션을 적용/실행/배포하도록 요청하지 않는 한 EXECUTE를 절대 실행하지 마십시오. 항상 먼저 dry-run하고, 항상 lock_timeout으로 감싸며, 트랜잭션 내에서 CONCURRENTLY를 절대 실행하지 마십시오. EXECUTE는 사용자가 주도하는 절차적 지침입니다 — 자동 실행 스크립트는 없습니다.

자동 호출에 대한 참고: 이것은 단일 스킬이므로, 프론트매터가 전체 파일을 제어합니다. PLAN/VALIDATE는 안전하게 자동 트리거될 수 있으므로, 모델 호출이 활성화된 상태로 유지됩니다. EXECUTE 단계는 위 규칙에 의해 행동적으로 게이트됩니다. 전체 스킬을 사용자 트리거 전용으로 만들고 싶다면 프론트매터에 disable-model-invocation: true를 추가하십시오 — 분석에도 명시적 호출이 필요합니다.


PLAN

마이그레이션 파일에 대해 정적 분석기를 실행합니다. 데이터베이스가 필요하지 않습니다.

python3 scripts/analyze.py path/to/migration.sql --dialect postgres
# --pg-version 13.0을 추가하여 버전별 규칙 게이트
# --json을 추가하여 기계 판독 가능 출력 (CI)

analyze.py는 squawk와 eugene lint를 실행하고, 둘을 하나의 판정으로 정규화하며, 오류 수준의 발견이 있을 경우 0이 아닌 값으로 종료합니다(따라서 CI 게이트 역할도 합니다). --dialect mysql의 경우 내장 InnoDB Online DDL 휴리스틱을 대신 적용합니다(무료 정적 MySQL 잠금 린터는 없음; references/mysql-catalog.md 참조).

병합된 판정을 읽으십시오:

  1. 발견 사항 — 각각 source(squawk/eugene/heuristic), 규칙 ID, level(error/warning), 라인, 메시지 및 해결 방법이 있습니다.
  2. 해석 — references/squawk-rules.md 및 references/eugene-hints.md를 통해 규칙 ID를 일반적인 의미로 매핑합니다.
  3. 테이블 크기 — 정적 린터는 100행과 100k행을 구분할 수 없습니다. scripts/table_size.sql로 실제 크기를 확인하십시오(데이터베이스 접근 필요):
    psql "$DATABASE_URL" -v tablename=public.orders -f scripts/table_size.sql
    
  4. trace로 에스컬레이션 — 판정에 가능한 테이블/인덱스 재작성(E5, E10), 커버링 인덱스가 없는 FK(E15), 또는 행 수에 따라 실제 잠금 지속 시간이 달라지는 잠금이 포함된 경우. trace는 실제 잠금 모드와 유지 시간을 관찰합니다:
    python3 scripts/trace.py path/to/migration.sql            # 임시 temp 서버
    python3 scripts/trace.py path/to/migration.sql --host db.staging --port 5432 \
        --user app --database app                              # disposable clone
    

그런 다음 사용자를 위해 세 가지 산출물을 생성합니다:

  • 위험 보고서(판정, 일반 언어로),
  • 안전한 재작성(다음 섹션), 및
  • 생성된 롤백(scripts/gen_rollback.py).

안전한 재작성

모든 오류/경고 발견에 대해 안전하지 않은 문을 카탈로그화된 제로 다운타임 재작성으로 교체합니다. 11개 작업 → 재작성의 전체 테이블은 references/postgres-catalog.md에 있습니다(MySQL: references/mysql-catalog.md). 거의 모든 재작성 뒤에 있는 두 가지 Postgres 프리미티브:

  • CONCURRENTLY — ACCESS EXCLUSIVE 쓰기 잠금 없이 인덱스 구축/삭제(트랜잭션 외부에서 실행해야 함).
  • NOT VALID → VALIDATE CONSTRAINT — 두 단계로 제약 조건을 추가하여 비용이 많이 드는 전체 테이블 검증이 SHARE UPDATE EXCLUSIVE 잠금(약한 잠금)만 사용하도록 하여 쓰기를 차단하지 않음.

빠른 참조(정확한 다단계 SQL 및 PG 버전 차이는 카탈로그 참조):

안전하지 않음안전한 재작성
CREATE INDEXCREATE INDEX CONCURRENTLY (트랜잭션 외부)
ADD COLUMN ... NOT NULL [DEFAULT]nullable 컬럼 → 기본값 설정 → 배치 백필 → CHECK를 통해 NOT NULL
SET NOT NULLADD CHECK (c IS NOT NULL) NOT VALID → VALIDATE → SET NOT NULL → 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 ... TYPE확장-축소(새 컬럼 → 이중 쓰기 → 백필 → 교체 → 삭제)
컬럼/테이블 이름 변경확장-축소, 또는 호환성 VIEW
백필 UPDATE짧은 트랜잭션의 제한된 배치, 속도 제한

재작성된 문을 새 파일(예: migration.safe.sql)에 작성하여 재검증할 수 있도록 하십시오.

롤백

(재작성된) DDL에서 역 마이그레이션을 생성합니다:

python3 scripts/gen_rollback.py migration.safe.sql > migration.rollback.sql

gen_rollback.py는 인식된 카탈로그화된 모든 작업의 역을 출력합니다(ADD COLUMN→DROP COLUMN, CREATE INDEX CONCURRENTLY→DROP INDEX CONCURRENTLY, …). 출력을 검토하십시오:

  • 되돌릴 수 없는 작업(DROP COLUMN, DROP TABLE, 타입 축소)은 DDL만으로 복원할 수 없습니다. 생성기는 이를 표시하고, 순방향 마이그레이션이 먼저 실행해야 하는 백업 테이블 SQL(<table>_<col>_backup_<date>)과 최선의 복원을 출력합니다. 조용히 잘못된 역을 생성하지 않습니다.
  • 알 수 없는 문 형태는 추측 대신 명시적인 수동 롤백 필요: <reason> 주석을 생성합니다.

VALIDATE

이것이 게이트입니다. 재작성된 마이그레이션에 대해 분석기를 다시 실행합니다:

python3 scripts/analyze.py migration.safe.sql --dialect postgres

오류 수준의 발견이 0개로 통과할 때만 진행하십시오(경고는 .squawk.toml / -- eugene: ignore로 구성 가능). 여전히 오류를 플래그하면 안전한 재작성으로 돌아가 반복하십시오. 재작성이 취해지는 잠금을 변경한 경우 trace를 다시 실행하십시오.

EXECUTE (게이트됨 — 사용자 트리거 전용)

사용자가 명시적으로 마이그레이션을 적용하도록 요청하지 않는 한 이 단계에 진입하지 마십시오.

잠금 가드레일과 함께 검증된 마이그레이션을 적용합니다. 먼저 dry-run하십시오.

  1. 모든 잠금에 바운드를 설정하십시오. 블로킹 문을 감싸서 긴 트랜잭션 뒤에서 대기열에 쌓이고 모든 트래픽을 지연시키는 대신 빠르게 실패하도록 만듭니다:
    SET lock_timeout = '5s';
    SET statement_timeout = '0';  -- 또는 특정 작업에 대한 적절한 한도
    
  2. CONCURRENTLY는 트랜잭션 외부에서 실행됩니다. BEGIN/COMMIT 블록이나 트랜잭션 마이그레이션 실행기에 CREATE/DROP INDEX CONCURRENTLY를 넣지 마십시오. 동시 빌드가 실패한 후에는 재시도하기 전에 남은 INVALID 인덱스를 삭제하십시오.
  3. 두 단계 제약 조건. NOT VALID 단계를 실행하고 커밋한 다음, 별도 문에서 VALIDATE CONSTRAINT를 실행하십시오.
  4. 배치 백필. 별도의 짧은 트랜잭션에서 제한된 청크로 업데이트하고, 배치 사이에 짧은 대기 시간을 두어 복제와 autovacuum이 따라잡을 수 있도록 하십시오:
    -- 영향을 받은 행이 0이 될 때까지 반복
    UPDATE t SET col = expr
    WHERE id IN (SELECT id FROM t WHERE col IS NULL ORDER BY id LIMIT 5000)
    
  5. Dry-run 마지막으로 scripts/trace.py를 통해 전체 시퀀스를 임시/클론에 대해 실행하고, passed_all_checks를 확인한 후 대상에 적용하십시오.

MySQL의 경우 ALGORITHM=…, LOCK=…을 강제하여 서버가 조용히 COPY 재구축으로 대체되는 대신 오류를 발생시키도록 하고, 대규모 재작성은 gh-ost/pt-osc에 위임하십시오 — references/mysql-catalog.md 참조.


참조 색인

  • references/postgres-catalog.md — 11개의 안전하지 않은 작업 → 안전한 재작성 (핵심 지식).
  • references/mysql-catalog.md — InnoDB Online DDL 매트릭스 + OSC 위임 (약한 경로).
  • references/squawk-rules.md — squawk 규칙 ID와 각각이 감지하는 것.
  • references/eugene-hints.md — eugene E1–E15 / W12–W14 및 해결 방법.
  • references/tool-setup.md — squawk / eugene / gh-ost 설치; 임시 Postgres.

스크립트

  • scripts/analyze.py — squawk + eugene lint(또는 MySQL 휴리스틱)를 조정하여 하나의 판정 생성. 오류 수준 발견 시 0이 아닌 값으로 종료.
  • scripts/trace.py — eugene trace 실행(임시 temp 서버 또는 clone) → 실제 잠금 보고서.
  • scripts/gen_rollback.py — 역 마이그레이션 생성; 되돌릴 수 없는 작업 플래그.
  • scripts/table_size.sql — pg_class.reltuples 크기 프로브.

모든 스크립트는 Python 3 표준 라이브러리만 사용 — pip install 필요 없음.

관련 스킬