취소했는데 채널에 반영되지 않는다는 문의가 왔고 우리 시스템에서는 취소가 완료로 보였다. order_cancels 를 열어 보니 status 는 completed 인데 channel_sync_status 는 failed 이고 channel_cancel_id 는 비어 있었다.
Table of contents
Open Table of contents
증상 — 재시도할 대상이 없던 실패
실패면 재시도하면 된다고 생각하고 SyncCancelJob 이 어디서 발행되는지부터 찾았다. RefundCompleted 이벤트를 처리하는 지점에서 dispatch() 되는 구조였다.
그런데 이 주문에는 refunds 행 자체가 없었으므로 발행 지점을 지나간 적이 없었다. 실패라는 표시는 시도했다가 실패한 것이 아니라 시도된 적이 없다는 뜻이었다.
원인 — 상태 값이 가리지 못한 두 상황
재시도가 의미 있는 실패는 작업이 존재하고 실행됐다가 오류가 난 경우다. 작업이 만들어지지도 않은 경우는 재시도로 아무것도 달라지지 않는다.
두 경우가 같은 failed 로 보이므로 상태만 봐서는 구분되지 않는다. 두 가지 방법이 있었는데 failed 를 그대로 두고 channel_cancel_id 의 유무로 가르는 것과 초기값을 pending 으로 바꾸고 dispatched 와 failed 를 나누는 것이다. 뒤쪽을 골랐다. 이유는 앞쪽은 식별자가 비는 다른 경우와 또 섞여서다. 대신 기존 행의 failed 를 두 값으로 다시 나누는 마이그레이션을 감수했다.
우연이 아닌 구조적 제외
왜 환불이 없었는지 보니 이 주문은 퇴점한 업체의 건이라 supplier_id 와 payments 행이 없었다. payments 가 없으면 환불 흐름을 탈 수 없다.
식별자 부재에서 결제 정보 부재로 그리고 환불 흐름 이탈과 작업 미발행까지 전 단계가 결정돼 있었다. 일시적 오류가 아니라 구조적으로 이 경로에서 제외되는 건이었다.
조치 — 대안을 붙인 회신
그래서 재시도하겠다는 답 대신 이 주문은 환불 흐름을 탈 수 없어 연동 작업이 생성되지 않는다고 회신했다. 재시도로는 해결되지 않는다는 사실을 명시했다.
다만 거기서 끝내지 않고 해당 마켓 관리자에서 직접 취소해야 한다는 경로를 함께 적었다. 안 된다는 답에는 대안을 붙여야 요청자가 다음 행동을 정할 수 있다.
판단 기준 — 진단 순서를 앞으로 당기기
같은 문의가 또 올 것에 대비해 순서를 정했는데 refunds 확인을 가장 앞에 뒀다. 없으면 작업이 생성되지 않았다는 뜻이므로 재시도 대상이 아니다.
jobs 테이블부터 보면 없다는 사실만 확인하고 왜 없는지는 알 수 없다. refunds 와 jobs 와 channel_cancel_id 순으로 봐야 원인까지 닿는다.
정리
- 후속 작업이 선행 완료 시점에 발행되면 선행을 못 탄 건은 영영 반영되지 않는다
- 실패 표시가 시도 실패인지 미시도인지 구분되지 않는다
- 초기값을
failed로 두면 두 상황이 섞인다 - 재시도가 소용없는 실패가 있다
- 식별자가 비어 있으면 구조적 제외일 수 있다
- 진단은 선행 레코드부터 본다
- 안 된다는 답에는 대안을 붙인다
- 상태 값 하나가 두 상황을 뜻하면 그 사실을 기록한다