콘텐츠의 분류 코드를 새 체계로 일괄 매핑해야 했고 건수가 십만을 넘었다. 한꺼번에 장비로 전송하면 현장 회선에 부하가 크므로 데이터를 먼저 바꾸고 전송은 나중에 나눠서 할 계획이었다.
Table of contents
Open Table of contents
배경 — 계획과 정상 메서드
원칙대로 서비스 계층의 ContentService.update 를 쓰려 했다. 직접 질의보다 안전하다는 것이 그 원칙의 근거다.
그런데 이 계획에서는 그 선택이 계획 자체를 깼다. 무엇이 안전한가는 무엇을 하려는가에 따라 달라진다.
원인 — 이력이 스케줄러를 부른다
그 메서드를 따라가니 값을 바꾸면서 sync_log 에 행을 하나 만들고 있었다. 별도의 SyncScheduler 가 그 표를 읽어 device_jobs 를 만든다.
즉 sync_log 를 만드는 순간 전송이 예약된다. 데이터만 바꾸고 전송은 나중에라는 계획이 성립하지 않고 십만 건의 전송이 한꺼번에 걸리게 된다.
막힌 지점 — 기대한 곳이 아닌 이력 테이블
요구사항에 변경 이력을 남기라는 항목이 있었다. 정상 메서드가 이력을 남기기는 하는데 content_history 가 아니라 sync_log 에 남겼다.
그러면 요구된 이력은 안 남고 남는 것은 스케줄러용 이력이다. 이력을 남긴다는 말이 어느 테이블을 뜻하는지까지 맞춰 봐야 했다.
해 본 것 — 두 테이블에 걸친 값
더 보니 category_code 가 두 테이블에 걸쳐 있었다. 관리 화면은 contents 의 단일 현재값을 보고 장비는 site_contents 의 현장별 정본을 본다.
하나만 바꾸면 절반만 반영되는데 정상 메서드는 한쪽만 손댄다. 결국 직접 질의로 두 테이블을 함께 갱신하는 쪽을 골랐다. 이유는 sync_log 를 안 만드는 경로가 그것뿐이라서다. 대신 서비스 계층이 해 주던 검증을 손으로 다시 하는 부담을 감수했다. 이력은 content_history 에 따로 넣었다.
검증 — 재생성 경로가 참조하는 것
여기에 하나가 더 있었는데 템플릿의 현장별 행 site_templates 도 함께 바꿔야 했다. 안 바꾸면 이후 배포본을 재생성할 때 옛 코드를 그대로 물려받는다.
당장은 안 보이고 나중에 드러나는 종류라 재생성 경로가 무엇을 참조하는지 확인이 필요했다. 검증에 실행보다 오랜 시간이 들었지만 그것을 안 봤으면 되돌리기 어려운 전송이 한꺼번에 나갔을 것이다.
정리
- 정상 메서드가 부수효과로 다른 동작을 예약할 수 있다
- 먼저 바꾸고 나중에 전송하는 계획은 그 예약 때문에 깨진다
- 무엇이 안전한가는 무엇을 하려는가에 따라 달라진다
- 이력이 기대한 테이블이 아닌 곳에 남을 수 있다
- 한 값이 두 테이블에 걸쳐 있으면 함께 갱신해야 한다
- 하나만 바꾸면 절반만 반영되고 그것도 조용히 그렇다
- 재생성 경로가 무엇을 참조하는지 확인한다
- 되돌리기 어려운 작업일수록 앞에서 오래 본다