Skip to content
isdnetworks
Go back

단계적 조인으로 좁힌 실패 원인

(수정됨)

점검 전 일괄 주문 취소 작업에서 수백 건이 실패했고 로그를 몇 개 열어 보니 이미 체결된 주문으로 보였다. 그런데 표본 몇 개가 그렇다는 것과 전체가 그렇다는 것은 다른 얘기였다.

Table of contents

Open Table of contents

증상 — 수백 건의 실패와 첫 표본

표본에서 본 것을 전체로 옮기려면 나머지도 같은 원인인지를 확인해야 했다. 그 확인 없이 처리 방안을 정하면 소수의 다른 원인이 그 방안에 섞여 들어간다.

그래서 실패 목록 전체를 대상 표와 JOIN 해 COUNT(*) 를 내기로 했다. 몇 건인지가 아니라 전체 중 몇 퍼센트인지가 필요한 숫자였다.

조사 — 세 단계의 조인

첫 단계는 실패 건 가운데 취소 대상을 뽑은 스냅샷 시각 이후에 trades 에 체결이 잡힌 것이 몇 건인지였다.

SELECT COUNT(DISTINCT cj.id)
  FROM cancel_jobs cj
  JOIN orders o ON o.order_no = cj.order_no
  JOIN trades t
    ON t.order_no = o.order_no
   AND t.traded_at > cj.snapshot_at
 WHERE cj.id IN (<실패 수백 건>);

한 건을 뺀 전부가 나왔다. 99% 를 넘는 비율이라 사실상 단일 원인이라고 볼 수 있었다.

두 번째 단계는 그 체결을 o.market 으로 GROUP BY 한 것이고 전부 한 마켓이었다. 마켓 메이커가 호가를 촘촘히 대는 마켓이라 스냅샷과 취소 사이의 몇 초에 체결이 난 것이다. 분포가 몰려 있으면 원인이 하나다. 세 번째 단계는 그 마켓에서 실제로 자리를 차지하고 있는 행을 세는 것이었다.

SELECT COUNT(*) FROM cancel_queue
 WHERE market = '<그 마켓>'
   AND (engine_ack IS NULL OR engine_ack = '')
   AND order_no IS NOT NULL;

engine_ack 가 비었는데 order_no 는 잡고 있는 행들이었다. 엔진이 「이미 체결됨」으로 거절한 취소가 큐에서 안 빠지고 다음 취소를 막고 있었다.

각 단계가 다음 단계의 조건을 만든다

첫 단계에서 비율이 낮게 나왔다면 두 번째 단계를 볼 이유가 없었다. 여러 원인이 섞여 있다는 뜻이므로 원인별로 나누는 것이 먼저였을 것이다.

두 번째에서 여러 마켓에 흩어져 있었다면 마켓 공통 문제가 아니라 개별 사정으로 갈렸을 것이다. 각 단계의 결과가 다음 단계를 볼지 말지와 무엇을 볼지를 정해 주는 구조였다.

대응 — 처리 방안과 안전 근거

세 번째 단계에서 나온 방해 행을 DELETE 하면 나머지가 통과한다는 것이 방안이었다. 다만 운영 자료를 지우는 것이므로 왜 안전한지를 함께 밝혀야 했다.

그 행들의 주문이 이미 filled 상태이고 어느 활성 잔액 잠금에서도 참조되지 않는다는 것을 NOT EXISTS 로 적었다. MySQL 5.7 참고 매뉴얼의 EXISTS 절대로 서브쿼리가 한 행이라도 돌려주면 NOT EXISTS 는 거짓이 되므로, 잠금이 하나라도 남은 주문은 이 조건에서 자동으로 빠진다. 큐를 통째로 비우는 것 대신 조건 삭제를 골랐다. 이유는 안전하다는 말이 아니라 어떤 조건에서 안전한지를 SELECT 로 보이는 것이 근거가 돼서다.

검증 — 나머지 한 건의 분리

한 건을 뺀 전부가 확인됐으므로 남은 한 건은 다른 원인이었고 그것을 별도 항목으로 뺐다. 비율이 높다고 해서 전부라고 쓰면 그 한 건이 조용히 남는다.

그 한 건은 따로 로그를 열어서 원인을 확인하고 별도로 처리했다. 엔진 응답이 타임아웃으로 끊긴 건이었다. 큰 덩어리를 처리하는 문서에 작은 예외를 함께 적어 두는 것이 그 예외를 살리는 방법이었다.

비율이 처리 방식을 정한다

99% 를 넘는 비율이 한 원인이라는 것이 일괄 처리를 정당화하는 근거가 됐다. 절반쯤이었다면 원인별로 나눠서 각각 다른 방식으로 처리했을 것이다.

일괄 삭제와 건별 처리라는 두 가지 방법 중 무엇을 고를지를 그 숫자가 정한 셈이다. 즉 비율은 원인을 확인하는 숫자이기도 하지만 처리 방식을 고르는 숫자이기도 했다. 세어 보기 전에 방식을 정하면 그 숫자가 쓰일 자리가 없어진다.

정리


Share this post on:

Previous Post
긴 작업의 결정 범위 긋기
Next Post
수동 재시도 절차