로컬에선 완벽했던 수술이 프로덕션에서 401을 뿜었다

26년 08월 23일

프로덕션 반영은 수술이 끝이 아니었다

법원경매 권리분석 프로젝트의 DB 수술을 로컬에서 완주한 뒤 남은 일은 프로덕션 반영이었다.

격리된 로컬 DB에서 충돌 사건을 전부 분리하고 크롤러 실주행까지 검증한 상태였다.

워크플로우 자체는 검증 루프를 세 라운드 통과한 뒤라 자신이 있었다.

문제는 수술이 아니라 반영 방식에서 터졌다.

dump 교체가 권한을 지웠다

반영은 로컬에서 다 정리한 DB를 프로덕션에 통째 덮어씌우는 방식으로 했다.

로컬 덤프를 뜨서 복원하는 가장 단순한 경로였다.

곧바로 REST API가 401을 뱉기 시작했다.

원인을 찾아보니 복원 명령의 옵션이 Row Level Security 정책 15개와 역할 권한을 통째로 날리고 있었다.

데이터는 온전히 들어갔는데 데이터를 지키던 규칙이 사라진 상태였다.

pg_restore에 —no-privileges 옵션을 주면 권한 관련 객체를 아예 복원하지 않는다.

로컬 격리 DB에는 애초에 그 정책들이 없었으니 덤프 자체가 권한을 실어 나르지도 못했다.

응급 복구로 정책 전체를 원본과 한 글자 단위로 대조해서 되살리고 같은 사고가 재발하지 않도록 영구 마이그레이션으로 남겼다.

데이터만 바꿀 때는 스키마 스왑 금지

이 사고로 원칙 하나를 확립했다.

데이터만 바꾸는 작업일 때는 덤프 통째 교체를 쓰지 않는다.

로컬에서 검증된 UPDATE 스크립트를 프로덕션에 그대로 실행하는 방식만 쓴다.

변경 전에 백업 테이블을 만들어 원본 값을 스냅샷으로 남기고 동일 스크립트를 실행한 뒤 검증 쿼리로 확인한다.

백업 테이블은 롤백 보험이다.

수술하려던 대상 테이블의 변경 컬럼을 통째로 떠놓으니 무언가 잘못돼도 되돌릴 수 있었다.

검증 루프가 추가로 잡아준 것도 있었다.

크롤러 내부 테이블 다섯 개가 익명 사용자에게 쓰기 권한으로 열려 있었고 채팅 정책은 잘못된 테이블에 전사되어 있었다.

사람 눈으로 훑었다면 그냥 지나쳤을 부분이다.

스케줄러는 놓친 틱을 보충하지 않는다

반영 기간 동안 배치 스케줄을 잠시 멈춰뒀다가 다시 켰다.

여기서 예상 밖의 동작을 만났다.

Cloud Scheduler는 멈춰 있는 동안 놓친 실행을 채워주지 않는다.

다시 켠 뒤 그날 저녁 배치가 그냥 누락됐다.

스케줄러가 쓰는 것과 같은 인증과 경로로 수동 트리거를 날려서 해결했다.

비슷한 원리의 함정을 또 하나 발견했다.

시세 테이블이 비어 있던 원인은 실행 실패가 아니라 실행 이력 자체가 0회였다.

잡 정의는 올라와 있었는데 스케줄러가 멈춰 있었고 수동 실행 경로도 없었다.

배포했으니 돌고 있겠지가 아니라 실행 이력을 직접 확인해야 한다.

배포와 스케줄과 트리거는 각각 별개의 상태다.

고쳤다는 배포됐다와 다르다

같은 날 세션을 인계받으며 확인한 질문이 있었다.

물건 기일 매핑 버그는 수정돼서 배포됐다고 되어 있었는데 사실인지 확인해달라는 것이었다.

커밋 기록만 보면 수정이 존재했다.

그런데 배포 브랜치의 실제 파일을 열어 해당 경로를 확인하니 두 가지 수정 중 하나만 반영돼 있었다.

나머지 하나는 로컬 작업 트리에 커밋되지 않은 상태로만 존재했다.

배포된 저녁 크롤러는 계속 잘못된 매핑을 쌓고 있었던 셈이다.

이후 검증 방식을 바꿨다.

커밋 존재 여부가 아니라 배포 브랜치에서 해당 파일 경로를 직접 열어 수정 코드가 있는지 grep한다.

세션을 넘기며 고쳤다는 말은 믿지 않고 원격 브랜치 대조로만 판정한다.

API 응답이 물건 일련번호를 숫자로 주는데 문자열만 받는 파서가 조용히 버리던 버그도 같은 날 붙잡았다.

매핑 성공이 15건 중 0건이라는 이상한 숫자에서 시작해서 원본 응답 덤프와 매핑 결과를 나란히 놓고 나서야 보였다.

수정 후 재조회는 5건 중 5건으로 반전됐다.

남는 생각

하루를 관통한 교훈은 검증의 위치를 다시 세우는 것이었다.

로컬에서 완벽하게 검증된 것은 어디까지나 로컬 상태에 대한 검증이다.

프로덕션 반영은 데이터만 옮겨지는 게 아니라 권한, 스케줄, 배포 상태라는 별개의 축을 각각 지나간다.

수술 계획서에 반영 후 확인 항목을 적어두는 게 이제는 필수라고 생각한다.

정책 개수, 스케줄 상태, 배포 브랜치의 실제 코드.

세 가지만 확인했어도 이번 사고는 없었다.