주문이 자동으로 중복 생성되고 있다는 문의가 운영 쪽에서 들어왔다. 주문 자체가 중복이면 큰일이므로 받자마자 데이터를 조회했다.
Table of contents
Open Table of contents
배경 — 중복이 아닌 주문
order_no 를 기준으로 세어 보니 주문은 하나뿐이었고 중복이 아니었다. 그 주문에 딸린 returns 를 보니 반품이 둘 만들어져 있었다.
즉 중복인 것은 주문이 아니라 그 주문에 걸린 클레임이었고 문의에 적힌 표현과 실제로 중복이 생긴 대상이 서로 달랐다.
원인 — 시간순으로 본 전이
channel_claims 를 ORDER BY collected_at 으로 나열해 보니 REQUEST 와 WITHDRAW 와 REQUEST 셋이 있었다. 첫 REQUEST 에서 반품이 하나 생기고 마지막 REQUEST 에서 또 하나가 생긴 것이었다.
철회를 수집했는데 첫 반품이 안 닫혔고 재요청 때 살아 있는 반품을 무시하고 새로 만들었다. 외부 상태 전이의 중간 단계 처리가 빠지면 그 자리에서 중복이 만들어진다.
주의 — 미검증으로 표시한 추정
추정해 보면 철회를 받았을 때 기존 반품을 닫는 로직과 재요청 시 중복을 확인하는 로직 둘 중 하나만 있어도 중복이 안 생긴다. 다만 이 부분은 실제 코드를 열어서 확인하지는 않은 상태였다.
관측에서 추정한 것이므로 실제로 그 로직이 없는지는 ClaimSyncService 확인이 필요하다고 ISSUES.md 에 미검증으로 표시했다. 추정이 관측을 잘 설명한다는 것과 그것을 확인했다는 것은 다른 얘기다.
조치 — 유사 패턴의 연결
다른 채널에서 겪은 유사 이슈와 패턴이 비슷하다는 것도 함께 적어 뒀다. 거기서도 외부 상태 전이를 수집하면서 중간 상태를 처리하지 않아 문제가 됐다.
같은 계열의 문제일 수 있으므로 연결해 두면 한쪽을 고칠 때 다른 쪽도 같이 보게 된다. 진단 순서도 바꿔서 주문 중복 생성이라는 문의는 주문 테이블이 아니라 클레임 전이 이력부터 확인하기로 했다.
검증 — 정상 중복과의 구분
삭제 대상을 고를 때 조심할 것이 있었는데 같은 주문에 정상적으로 반품이 여럿 붙는 경우다. 한 주문에서 여러 상품을 각각 반품하면 그런 상태가 만들어진다.
삭제 대상은 두 가지 방법이 있었는데 같은 주문에 반품이 둘인 건을 전부 잡는 것과 WITHDRAW 뒤 REQUEST 가 있는 건만 잡는 것이다. 뒤쪽을 골랐다. 이유는 전이 이력을 봐야 정상 중복과 갈려서다. 대신 이력이 수집되지 않은 옛 건은 이 조건으로 못 잡는 것을 감수했다. 값만 비교해서 지우면 정상 건까지 지우게 된다. 조사 중에 channel_claims 의 claim_id 와 channel_claim_id 가 직관과 반대라는 것도 확인해 기록해 뒀다.
정리
- 주문 중복이 실제로는 딸린 레코드 중복일 수 있다
- 클레임 전이 이력을 시간순으로 보면 어디서 생겼는지 나온다
- 외부 상태 전이의 중간 단계 처리가 빠지면 중복이 생긴다
- 관측에서 추정한 것은 미검증으로 표시한다
- 유사 패턴이 있으면 연결해 기록한다
- 정상 중복과 구분해 전이 이력이 있는 것만 대상으로 삼는다
- 값만 비교해서 지우면 정상 건도 지운다
- 컬럼 이름이 직관과 반대면 반드시 기록한다