← 모든 글

Migration Script, 한 번 더 돌려도 괜찮아?

멱등성 없는 migration script 가 새벽 장애를 부른다. 체크리스트 정리.

왜 갑자기 이 얘기

최근 hotfix 배포 중 migration 을 두 번 돌렸다. 결과는 duplicate row. 스크립트 자체는 테스트를 통과했지만 멱등성 보장 코드는 없었다. 새벽 두 시였고, 롤백에 40분 걸렸다.

사실 “한 번만 실행”은 보장이 아니다. CI 재시도, 수동 재실행, 파이프라인 타임아웃 재기동 — 실수로 두 번 실행될 경로는 생각보다 많다.


멱등성 체크리스트

스키마 변경

  • ADD COLUMN IF NOT EXISTS / CREATE TABLE IF NOT EXISTS 사용 여부
  • DROP 계열은 반드시 IF EXISTS. 없으면 두 번째 실행 즉시 에러
  • 인덱스 생성: CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_name ...

데이터 삽입/수정

  • INSERT INTO ... ON CONFLICT DO NOTHING 또는 UPSERT
  • 순수 INSERT 는 99% 멱등하지 않음
  • 보정값 계산 (예: amount * 1.1) 은 WHERE 절로 이미 보정된 행 제외 필수

실행 기록

  • migrations 테이블에 checksum + 실행 시각 남기기
  • 이미 실행된 hash 면 early return. Flyway·Liquibase 가 이걸 기본으로 해주는데 직접 짜면 빠뜨리기 쉽다
  • 기록 없이 “파일명으로 판단” 은 파일 수정 시 무력화됨

외부 호출 포함 스크립트

  • Stripe charge, 이메일 발송 등 side-effect 가 있으면 별도 플래그 컬럼 (notified_at, charged_at) 으로 guard
  • 멱등 키(idempotency key) 를 API 에 명시적으로 넘기거나, 넘길 수 없으면 호출 전에 DB 에서 실행 여부 먼저 확인

트랜잭션 범위

  • DDL 과 DML 을 같은 트랜잭션 안에 넣으면 PostgreSQL 에서는 괜찮지만 MySQL 에서는 DDL 이 암묵적 commit. 섞지 마라
  • 긴 migration 은 배치로 쪼개고, 배치 단위마다 checkpoint 기록

다음 한 가지

다음 PR 부터 migration script 하단에 주석 한 줄 추가: -- idempotent: yes/no, reason: .... 작성자가 직접 선언하게 만들면 리뷰어 눈에 바로 들어온다.


🛒 이 글과 어울리는 추천 상품

위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.