본문으로 건너뛰기
홍성찬
뒤로

SDK가 '성공'을 잘못 정의하고 있었다

0-컨펌 정산 시스템(관련 글)은 받은 트랜잭션을 네트워크에 뿌려야 한다. 브로드캐스트는 트랜잭션 처리 API(ARC)로 하고, 주 공급자가 실패하면 폴백으로 넘어가게 이중으로 뒀다.

그런데 배치를 보낼 때마다 이상하게 느렸다. 우리 코드도 네트워크도 아니었다.

멀쩡한 공급자가 매번 폴백당한다

브로드캐스트는 배치로 나간다. 트랜잭션 묶음을 한 번에 제출하고, 나머지 txid들의 상태를 재확인해서 성공 여부를 판정한다.

주 공급자는 살아 있었다. 트랜잭션도 실제로 네트워크에 잘 올라갔다. 그런데 SDK는 매 배치를 실패로 기록했고, 시스템은 폴백으로 넘어갔다. 폴백 공급자에는 일일 1,000회 한도가 있다. 정상 트래픽이 그 예비 한도를 계속 갉아먹고 있었다는 뜻이다. 배치마다 이 무의미한 폴백에 5.5초쯤이 나갔다.

”접수됨”을 “실패”로 읽는다

SDK가 상태를 어떻게 판정하는지 뜯어봤다. 제출 직후 상태를 재확인하면서 SEEN_ON_NETWORKSTORED, 이 둘만 성공으로 인정하고 있었다.

브로드캐스터의 트랜잭션 수명주기는 여러 단계다.

STORED → ANNOUNCED_TO_NETWORK → SENT_TO_NETWORK → SEEN_ON_NETWORK → MINED
(저장됨)   (네트워크에 알림)        (전송됨)          (관측됨)          (채굴됨)

제출 직후에 재확인하면 대개 중간 단계에 있다. 정상적인 진행 상태다. 그런데 SDK는 이걸 성공 목록에 안 넣으니 그대로 실패가 된다.

앞뒤가 안 맞는 판정이다. 성공으로 치는 STORED는 수명주기에서 가장 이른 단계인데, 그보다 더 진행된 ANNOUNCED_TO_NETWORK는 실패로 본다. 로그를 열어보니 사실상 모든 브로드캐스트가 txStatus=ANNOUNCED_TO_NETWORK를 에러로 기록하고 있었다.

고친 것

트랜잭션이 브로드캐스터에 접수된 이후의 상태는 전부 성공으로 인정하고, 실제 거부(이중지급, 유효성 실패)만 실패로 보도록 판정 함수를 다시 썼다.

// 트랜잭션 수명주기에서 "접수 이후" 상태 전부를 성공으로 인정한다.
// 더 이른 단계(STORED)는 성공인데 더 진행된 단계(ANNOUNCED)가 실패로
// 판정되는 벤더 논리 결함의 우회. 거부·이중지급류만 에러.
const ACCEPTED_TX_STATUSES = new Set([
  'STORED',
  'ANNOUNCED_TO_NETWORK',
  'REQUESTED_BY_NETWORK',
  'SENT_TO_NETWORK',
  'ACCEPTED_BY_NETWORK',
  'SEEN_ON_NETWORK',
  'MINED',
]);

대신 나머지 골격은 벤더 구현을 그대로 뒀다. 포맷 변환, 배치 제출, 재확인 순서는 손대지 않고 상태 판정만 교체했다. 성능을 이유로 이미 검증된 경로를 통째로 새로 짜는 게 제일 위험하다고 봤다. 바꾸는 면적을 줄이는 쪽을 택했다.

남은 문제

상태 목록을 이제 내가 관리한다. 브로드캐스터가 수명주기에 새 상태를 추가하면 이 목록도 따라 갱신해야 한다. 벤더 판정을 우회한 대가로 프로토콜 변화를 직접 쫓게 됐다.

“접수 이후 전부 성공”이라는 판정 자체도 낙관적이다. 접수는 됐는데 이후 네트워크에서 탈락하는 경우를 이것만으로는 못 잡는다. 그건 별도 계층(0-컨펌 글의 대사·재시도)이 본다는 전제 위에서만 성립한다.

5.5초도 단일 환경 실측이라 폴백 쪽 응답 지연이 달라지면 숫자가 달라진다. 애초에 이 작업의 성과는 “5.5초를 줄였다”보다 “무의미한 폴백을 없앴다”에 가깝다.


영속화가 조용히 죽어 있던 건과 뿌리가 같은 버그다. 라이브러리가 무엇을 성공으로 치고 무엇을 실패로 치는지 확인하지 않고 믿었다.

브로드캐스트처럼 부분 성공과 중간 상태가 있는 작업에서는 벤더의 이분법이 도메인의 실제 의미와 어긋나기 쉽다. ANNOUNCED_TO_NETWORK는 벤더한테는 아직 성공이 아니었지만 우리한테는 접수됐으면 충분한 성공이었다.


이 글 공유:

이전 글
입금 하나에 69초가 걸렸다
다음 글
첫 프레임만 깜빡이는 버그 세 개