Migration Script, 한 번 더 돌려도 안전한가
멱등성 체크리스트 — 두 번 돌려서 망한 경험을 정리했다.
두 번 돌려서 망한 기억
롤백 직후 같은 migration을 재실행했다. INSERT 가 중복 PK 에러를 냈고, 그걸 무시하도록 --force 를 붙였더니 데이터 일부가 덮였다. 총 피해 복구에 3시간. 원인은 단순했다 — script 를 “한 번만 돌릴 것” 전제로 짰다.
막상 운영에서는 다시 돌려야 하는 상황이 생각보다 잦다. 중간 실패, 부분 롤백, 환경 재현, QA 반복 실행. 그래서 지금은 script 를 짤 때 첫 줄부터 멱등성 전제로 시작한다.
체크리스트
테이블/컬럼 DDL
CREATE TABLE IF NOT EXISTS— 기본.ADD COLUMN은 존재 여부를 먼저 확인. PostgreSQL 기준:ALTER TABLE foo ADD COLUMN IF NOT EXISTS bar TEXT;- index 생성은
CREATE INDEX IF NOT EXISTS.
데이터 DML
INSERT ... ON CONFLICT DO NOTHING또는ON CONFLICT DO UPDATE.- DO NOTHING: 기존 행 건드리지 않아야 할 때.
- DO UPDATE: 실행할 때마다 최신 상태로 수렴해야 할 때.
UPDATE는 대부분 멱등 — 단, 값을 누적하는 경우(count + 1) 는 절대 안전하지 않다. 플래그로 guard 걸 것.DELETE후INSERT조합은 위험. 가능하면 upsert 로 교체.
실행 상태 추적
schema_migrations같은 테이블에 script 이름 + 실행 완료 timestamp 를 남긴다.- script 진입부에서 이미 완료된 기록이 있으면 early exit.
- 직접 구현하기 귀찮으면 Flyway / Liquibase 가 이걸 기본으로 해준다. 단, 이미 커스텀 runner 가 있다면 굳이 교체 안 해도 된다.
부수 효과 처리
- 이메일 발송, 외부 API 호출이 migration 중에 끼어 있다면 별도 job 으로 분리. script 와 묶으면 재실행 때 중복 발송 막기가 훨씬 복잡해진다.
- S3 업로드, 파일 생성 등도 마찬가지. 존재 확인 후 skip 로직 필수.
검증
- 로컬에서 같은 script 를 연속 두 번 돌린다. 에러 없고 row count 동일하면 통과.
- CI 파이프라인에
migrate && migrate를 넣어두면 리뷰 없이 잡힌다.
다음 한 가지
이번 sprint 안에 CI에 migrate && migrate 이중 실행 스텝 하나 추가한다.
🛒 이 글과 어울리는 추천 상품
위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.