이관 뒤에 contents 와 site_contents 에 잘못 남은 재생 시간 값을 정리하는 배치를 만들어야 했고 대상이 수천 건이라 화면으로는 안 됐다. 바로 짜지 않고 화면으로 같은 값을 고칠 때 무엇이 남는지를 먼저 봤다.
Table of contents
Open Table of contents
배경 — 정상 경로가 남기는 여섯 종류
ContentService.update 하나가 본체 갱신과 content_history 와 audit_log 와 schedule_history 와 장비별 재생 순서와 device_jobs 까지 남기고 있었다. 내가 짜려던 배치는 UPDATE contents SET duration = ? 한 문장이라 그중 첫 번째만 한다.
나머지 다섯이 안 남으면 같은 값이지만 다른 상태가 된다. 우회하는 배치는 무엇을 안 하게 되는지를 먼저 세어야 하고 그것은 정상 경로를 따라가야 나온다.
원인 — 계층에 따라 남는 것이 달랐다
더 보니 콘텐츠와 현장 배포본이 다르게 동작하고 있었다. 콘텐츠는 앞의 셋만 남기고 뒤의 셋은 SiteContentService.update 에서만 남았다.
이유를 찾아보니 recordScheduleHistory() 자체는 조건을 안 보는데 호출하는 쪽이 현장 배포본일 때만 부르고 있었다. 함수만 읽으면 이 조건이 안 보이고 호출부를 봐야 드러난다.
해 본 것 — 상위 변경이 하위로 전파되는 구조
그러면 콘텐츠의 재생 시간 변경은 어떻게 기록되는지가 남는다. 콘텐츠를 고치면 각 현장 배포본으로 전파되고 배포본마다 그 함수가 호출된다.
즉 콘텐츠에는 안 남고 배포본에만 남는 것이 정상 상태였다. 이것을 모르고 콘텐츠에도 남기면 정상 경로가 만들지 않는 행을 배치가 만들게 된다.
조치 — 계층으로 분기한 배치
그래서 배치를 계층으로 갈라 콘텐츠는 셋만 남기고 배포본은 다섯을 남기게 했다. device_jobs 만 일부러 빼는 쪽을 골랐다. 이유는 이 정리가 장비에 다시 내려보낼 성격이 아니라서다. 대신 장비 쪽 값이 다음 정기 동기화까지 옛 값으로 남는 것을 감수했다.
뺀 것도 판단해서 뺀 것이므로 그 사실과 이유를 주석에 적었다. 안 적으면 다음 사람이 누락으로 보고 채워 넣는다.
검증 — 안 맞췄을 때 깨지는 가정
콘텐츠에도 schedule_history 를 남겼다면 당장은 아무 일도 안 생긴다. 그러나 그 표를 읽는 코드가 콘텐츠에는 없다는 것을 전제하고 있으면 그 조회가 틀린 결과를 낸다.
검증은 같은 변경을 화면으로 한 번 배치로 한 번 하고 각 표의 COUNT(*) 와 duration 값을 대조하는 방식으로 했다. 이 대조에서 두 군데를 고쳤는데 이력의 형식이 미묘하게 달라서 눈으로는 안 보였다.
정리
- 서비스 계층을 우회하는 배치는 정상 경로가 남기는 것을 먼저 센다
- 계층에 따라 남는 것이 다를 수 있다
- 함수가 아니라 호출부가 조건을 걸기도 한다
- 상위 변경이 하위로 전파되면서 기록되는 구조가 있다
- 배치를 계층으로 분기해 정상 경로와 같은 상태를 만든다
- 일부러 뺀 것은 이유와 함께 주석에 적는다
- 정상 경로가 안 만드는 데이터를 만들면 읽는 쪽의 가정이 깨진다
- 검증은 화면으로 한 번 배치로 한 번 하고 결과를 대조한다