LabHub
学习 学习路径 课程

资本市场与清结算

重启后的进程把同一批订单又发了一遍

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

죽었다 살아난 전송 프로세스의 체크포인트와 전송 로그에서 재처리 구간을 찾고, 거래소 접수 확인과 대조해 실제로 두 번 접수된 주문만 골라낸 뒤, 결정적 주문 id 와 중복 제거 창 길이를 자료에서 구해 복구 절차서를 남깁니다.

왜 중요한가

재처리는 없앨 수 없습니다. 체크포인트를 보낸 뒤에 저장하면 죽었을 때 그 구간이 다시 나가고, 보내기 전에 저장하면 그 구간이 빠집니다. 둘 사이에 안전한 지점이 없기 때문에, 전송 경로는 대개 다시 보내는 쪽을 고르고 중복은 받는 쪽에서 거릅니다. 거르려면 같은 신호가 몇 번을 다시 읽혀도 같은 주문 id 를 내야 하는데, 실행마다 새로 매기는 일련번호나 시각을 섞은 id 는 그 조건을 깨뜨립니다. 그리고 정정과 취소는 이전 id 를 가리키므로, id 규칙이 흔들리면 사슬까지 끊어져 취소하려던 주문이 남습니다.

단계

  1. python3/root/cap/replay/data 에 여섯 개 자료 파일을 만듭니다. 생성 스크립트를 그대로 쓰세요.
  2. 체크포인트와 전송 로그를 견줘 재처리 구간을 /root/cap/replay/crash.txt 에 적습니다.
  3. 그 구간에서 다시 나갈 주문을 /root/cap/replay/replay.csv/root/cap/replay/replay.txt 에 적습니다.
  4. 신호의 안정된 필드만으로 주문 id 를 만들어 /root/cap/replay/clordid.csv/root/cap/replay/idcheck.txt 에 적습니다.
  5. 접수 확인과 대조해 두 번 받아들여진 주문을 /root/cap/replay/dup.csv/root/cap/replay/dup.txt 에 적습니다.
  6. 체크포인트 저장 시점 두 가지를 각각 계산해 /root/cap/replay/modes.csv 에 한 줄씩 적습니다.
  7. 원주문을 가리키지 못하는 취소와 정정을 /root/cap/replay/broken.csv/root/cap/replay/broken.txt 에 적습니다.
  8. 중복 제거 창 길이를 /root/cap/replay/window.txt 에, 복구 절차서를 /root/cap/replay/runbook.md 에 남깁니다.

참고

장애가 남긴 자료 만들기

python3/root/cap/replay/datasignals.jsonl·sent.csv·acks.jsonl·checkpoint.json·incident.json·policy.json 여섯 개를 만듭니다. 난수를 쓰지 않는 생성 스크립트를 그대로 쓰세요.

폐쇄망에는 내려받을 표본이 없으니 자료부터 직접 만듭니다. 난수를 쓰지 않아야 누가 몇 번을 돌려도 같은 자료가 나오고, 서로의 판정을 대조할 수 있습니다. 채점기는 파일 내용을 표준형으로 바꿔 지문을 대조하므로 자료를 손으로 고치면 뒤 단계가 전부 막힙니다.

어디부터 다시 읽었는지 찾기

/root/cap/replay/crash.txtcrashed_run·resumed_run·committed_offset·last_sent_offset·replay_from·replay_to 여섯 줄을 key=value 로 적으세요.

체크포인트는 어디까지 처리했다고 적혀 있고, 전송 로그에는 죽은 실행이 실제로 어디까지 보냈는지가 남아 있습니다. 두 값이 다르면 그 사이가 다시 읽히는 구간입니다. 재시작은 커밋한 다음 오프셋부터 읽습니다.

그대로 재시작하면 무엇이 다시 나가는지 세기

/root/cap/replay/replay.csv 에 첫 줄 offset,intent,symbol,side,qty,limit_px 를 두고 재처리 구간에서 주문이 나가는 신호를 한 줄씩 적고, /root/cap/replay/replay.txtwindow_signals·window_orders·window_notional_krw 세 줄을 적으세요.

구간은 앞 단계에서 구한 replay_from 부터 replay_to 까지입니다. 그 안의 신호를 모두 세는 것과 주문이 나가는 신호만 세는 것은 다릅니다. 금액은 주문 수량 곱하기 지정가를 더한 값이고, 노출을 만드는 신규와 정정만 더합니다.

다시 읽어도 같은 주문 id 만들기

/root/cap/replay/clordid.csv 에 첫 줄 offset,intent,clordid,orig_clordid 를 두고 주문이 나가는 신호마다 한 줄씩 적고, /root/cap/replay/idcheck.txtorder_signals·distinct_deterministic_ids·offsets_sent_twice·offsets_with_two_sent_ids·chain_resolvable 다섯 줄을 적으세요.

규칙은 policy.json 의 clordid 에 있습니다. fields 를 그 차례대로 sep 으로 이어 붙여 sha256 으로 요약하고 앞 hex_len 글자에 prefix 를 붙입니다. 신규는 orig_clordid 를 빈 칸으로 두고, 취소와 정정은 ref_offset 이 가리키는 신호에 같은 규칙을 적용한 값을 적습니다. chain_resolvable 은 그렇게 만든 orig 가 이 표 안에 실재하는 취소와 정정의 수입니다.

정말 두 번 받아들여진 주문만 남기기

/root/cap/replay/dup.csv 에 첫 줄 offset,msg_type,clordid_a,clordid_b,symbol,side,qty,notional_krw 를 두고 양쪽 실행의 메시지가 모두 접수된 오프셋을 적고, /root/cap/replay/dup.txtdup_accepted·dup_new·dup_cancel·dup_replace·dup_notional_krw 다섯 줄을 적으세요.

전송 로그에 두 번 나온 오프셋이 곧 이중 접수는 아닙니다. acks.jsonl 에서 두 메시지가 모두 accepted 인 것만 남기세요. clordid_a 는 실행 이름 오름차순으로 앞선 쪽, clordid_b 는 뒤선 쪽입니다. 금액은 수량 곱하기 가격이고, dup_notional_krw 는 신규 주문 줄만 더합니다.

체크포인트를 언제 저장하느냐로 갈리는 것

/root/cap/replay/modes.csv 에 첫 줄 mode,committed_offset,resume_offset,duplicate_orders,missing_orders 를 두고 commit_aftercommit_before 두 줄을 적으세요.

체크포인트는 commit_every 건마다 저장됩니다. 보낸 뒤에 저장하는 방식이면 죽기 직전에 끝난 마지막 배치까지만 커밋되어 있고, 보내기 전에 저장하는 방식이면 지금 처리 중이던 배치의 끝 오프셋이 이미 커밋되어 있습니다. 재시작은 커밋한 다음 오프셋부터 읽습니다. 앞의 것은 중복을, 뒤의 것은 누락을 냅니다.

취소가 가리키던 주문이 사라졌다

/root/cap/replay/broken.csv 에 첫 줄 run_id,clordid,offset,msg_type,orig_clordid,reason 을 두고 원주문을 가리키지 못하는 취소와 정정을 적고, /root/cap/replay/broken.txtchain_messages·broken_total·unknown_orig·orig_rejected 네 줄을 적으세요. reasonunknown_orig 또는 orig_rejected 입니다.

사슬이 끊기는 길은 두 가지입니다. 가리키는 주문 id 가 전송 로그 어디에도 없으면 unknown_orig 이고, 있기는 한데 거래소가 그 원주문을 거절했으면 orig_rejected 입니다. 둘 다 결과는 같습니다 — 취소하려던 주문이 남습니다. chain_messages 는 전송 로그의 취소와 정정 전부입니다.

중복 제거 창을 자료에서 구하고 절차서 쓰기

/root/cap/replay/window.txtoffsets_sent_twice·max_replay_delay_sec·outage_sec·recommended_window_sec 네 줄을 적고, /root/cap/replay/runbook.md## 무슨 일이 있었나, ## 왜 두 번 나갔나, ## 돈으로 얼마인가, ## 복구 절차, ## 무엇을 고쳐야 하나 다섯 절을 그 차례대로 각각 60자 이상 쓰세요. 절차서 본문에는 5단계의 dup_notional_krw 값과 이 단계의 recommended_window_sec 값을 숫자로 적습니다.

재처리 지연은 같은 오프셋이 처음 나간 시각과 다시 나간 시각의 차이입니다. 두 번 나간 오프셋 전부에서 재고 가장 큰 값을 씁니다. 권고 창은 그 값에 policy.json 의 dedup_safety_multiple 을 곱하고 dedup_round_sec 의 배수로 올림한 값입니다. outage_sec 은 incident.json 의 두 시각 차이입니다.