한 입점사에서 order_cancels 에 취소 요청이 수백 건 쌓여 있었고 화면에는 PENDING 이나 CHANNEL_FAILED 로 뜨고 있었다. 취소 테이블만 보지 않고 그 주문들의 orders.status 부터 확인했다.
Table of contents
Open Table of contents
배경 — 이미 완료된 주문
주문 상태를 세니 대부분이 CANCELED 였고 실제 취소와 환불은 외부에서 이미 처리된 상태였다. order_cancels.status 만 전환되지 않은 것이었다.
입점사가 신고한 처리 실패가 실제 실패가 아니었다. 밖에서 보이는 표현과 실제 상태가 다를 수 있으므로 신고 문구를 그대로 증상으로 삼으면 안 된다.
검증 — 분해가 드러낸 모양
수백 건을 한 덩어리로 보면 미처리 수백 건이지만 GROUP BY channel_id, order_no 로 분해하니 모양이 갈렸다. 한 주문에서 엿새 동안 20분 간격으로 요청이 수백 건 생겼고 마지막에 완료가 하나 생겼는데 앞선 것들이 안 정리된 상태였다.
다른 주문 둘은 channel_claims 수집이 성공인데 우리 취소 상태만 안 바뀌었다. 또 다른 주문은 클레임이 아예 안 만들어졌는데도 주문은 취소 완료였다.
원인 — 원인이 셋인 같은 증상
넷 다 주문 상태는 같았지만 원인은 셋이었다. 폭주 후 미정리와 상태 미전환과 클레임 부재다.
원인이 다르면 대응도 달라서 첫째는 중복 정리와 폭주 원인 조사가 필요하고 둘째는 전환 로직 확인이며 셋째는 왜 클레임이 안 생겼는지에 대한 별도 조사다. 한 덩어리로 수백 건을 정리하면 세 문제가 전부 그대로 남는다.
조치 — 즉시와 근본으로 나눈 대응
당장의 대응을 둘로 나눴는데 잔존을 정리해 입점사가 보는 화면을 해소하는 것과 CancelSyncService 의 전환 로직을 점검하는 것이다. 앞의 것은 즉시 할 수 있고 뒤의 것은 안 하면 또 쌓인다. 정리 방식은 두 가지 방법이 있었는데 전부를 한 번에 DONE 으로 밀어 버리는 것과 유형별로 나눠 폭주분만 먼저 닫는 것이다. 뒤쪽을 골랐다. 이유는 한 덩어리로 밀면 세 원인이 전부 흔적 없이 사라져서다. 대신 입점사 화면에 두 건과 한 건이 며칠 더 남는 것을 감수했다.
다만 근본 원인은 아직 미규명이라고 INCIDENT.md 에 적었는데 뒤쪽을 하려면 코드를 봐야 하고 그것이 이번 조사 범위 밖이었기 때문이다. 진단 순서로는 취소 테이블만 보지 않고 주문 상태와 클레임을 확인한 뒤 채널과 주문별로 분해하는 것을 남겼다.
주의 — 비중이 정하는 순서
이 조사에서 배운 것은 같은 증상의 건들을 분해하면 유형이 갈린다는 점이다. 수백 건이라는 한 숫자가 폭주분 대부분과 두 건과 한 건 등으로 나뉜다.
각각 대응이 완전히 다르고 가장 큰 덩어리가 무엇인지도 함께 나온다. 비중을 알면 어디부터 잡을지가 정해지고 한 덩어리로 세면 원인이 여럿이라는 것 자체를 못 본다.
정리
- 신고된 처리 실패가 실제 실패가 아닐 수 있다
- 취소 테이블만 보지 말고 주문 상태와 클레임을 교차 확인한다
- 같은 증상의 건들을 분해하면 유형이 갈린다
- 증상이 같아도 원인이 여럿이면 대응이 다르다
- 한 덩어리로 정리하면 나머지 문제가 그대로 남는다
- 가장 큰 덩어리를 보면 어디부터 잡을지 정해진다
- 근본 원인을 못 찾았으면 미규명으로 적는다
- 즉시 대응과 근본 대응을 나눠 적는다