마스터 상품을 수정해도 마켓 상품에 반영되지 않는다는 문의가 들어왔는데 설정은 전부 켜져 있었다. 원인은 기간 할인이 진행 중인지 판정하는 조건 한 줄이었다.
->where('status', '!=', 'finish')
Table of contents
Open Table of contents
상태가 셋인데 조건은 둘
이 상태 컬럼에는 값이 셋이다. 할인이 진행 중인 start와 기간이 끝난 end와 종결 처리된 finish인데 조건은 종결이 아니면 진행 중으로 보고 있었다. 그러면 기간이 끝난 것까지 진행 중으로 취급된다.
더 나쁜 것은 기간이 끝난 상태에서 종결로 넘기는 코드가 어디에도 없다는 점이었다. 할인이 끝나면 그 상태에 머물고 아무것도 그것을 종결로 바꾸지 않는다. 결과적으로 기간 할인을 한 번이라도 걸었던 상품은 영원히 동기화가 막힌다. 영향 범위를 세어 보니 마켓 상품 151,233건에 입점사 44곳이었다.
부정 조건의 성질
이 조건은 지금 아는 값 중 하나가 아니면이 아니라 이 값 하나가 아닌 전부를 뜻한다. 상태값이 나중에 하나 추가되면 그것도 자동으로 이 조건에 들어오므로 코드를 안 고쳐도 동작이 바뀐다.
긍정으로 쓰면 그런 일이 없다. 통과시킬 값을 명시하면 새 값이 생겨도 통과하지 않고, 통과시켜야 하면 그때 명시적으로 추가하게 된다. 부정 조건은 미래의 값에 대해 통과를 기본값으로 두고 긍정 조건은 차단을 기본값으로 둔다.
같은 형태의 사례들
특정 상태만 뽑아 재조회하는 배치가 있었다. 승인 요청 상태인 것만 다시 조회하는데 통신 오류로 상태가 다른 값이 되면 그 조건에서 영영 빠지고 배치가 다시 보지 않는다.
특정 상태만 처리하는 분기도 있었다. 취소 클레임을 처리하는 코드가 처리 준비 상태만 다뤄서 수집 시점에 이미 완료나 거절이던 건은 그 분기를 타지 못했다. 재전송은 계속 도는데 상태가 안 바뀌어 영구히 쌓였다.
정리 대상을 상태로 좁힌 조회도 있었다. 결함이 있는 레코드를 정리하면서 한 상태만 대상으로 삼았더니 다른 상태의 같은 결함이 남아 다음 실행에서 또 걸렸다.
상태 조건을 쓸 때 보는 것
조건이 전제하는 전이가 실제로 일어나는지 먼저 본다. 코드에 없는 전이를 조건이 전제하고 있으면 그 조건은 영원히 성립한다. 상태 목록을 뽑고 각 전이를 누가 수행하는지 확인하면 드러난다.
상태값 전체를 나열하고 실제 분포도 조회한다. 값이 셋인지 다섯인지 모르면 부정 조건이 무엇을 포함하는지도 모르고, 분포를 보면 예상 밖의 값이 나오는 경우가 있다.
정리 작업의 대상은 상태가 아니라 실제 결함 조건으로 잡는다. 값이 비었다거나 참조가 없다는 것이 결함의 정의이고 상태는 그 결함의 결과일 뿐이다.
조건이 오래 남은 이유
조건 한 줄이 잘못된 것은 맞지만 그 줄이 오래 남은 이유는 따로 있다. 증상이 동기화가 안 된다는 것이었고 설정값은 전부 정상이었다. 확인할 곳이 여러 군데였고 이 조건은 그중 하나였을 뿐이라 어디를 봐야 하는지가 명확하지 않았다.
지금은 이런 판정 로직에 사유를 남긴다. 동기화를 차단하면서 기간 할인이 진행 중이어서 막았다는 것을 로그에 적으면 다음 사람은 조건 한 줄까지 바로 간다. 차단하는 코드는 차단했다는 사실보다 왜 차단했는지를 남겨야 조사에 쓰인다.
정리
- X가 아니면과 Y이면은 다르다
- 부정 조건은 나중에 생기는 값을 자동으로 편입한다
- 긍정으로 쓰면 새 값이 조용히 통과하지 않는다
- 조건이 전제하는 전이가 실제로 일어나는지 확인한다
- 일어나지 않는 전이를 전제하면 그 조건은 영원히 성립한다
- 상태값 전체를 나열하고 실제 분포를 조회한다
- 정리 대상은 상태가 아니라 실제 결함 조건으로 잡는다
- 차단 판정에는 사유 로그를 남긴다