Idempotently Reviving a Dead End-of-Day Batch
한국어 원문으로 표시합니다.
목표
도중에 죽은 일마감을 재수행해 되살리되, 이중 반영도 누락도 없이 끝낼 수 있게 됩니다. 그리고 계정일자와 시스템 시각을 구분해 마감 대상을 고르는 손을 갖게 됩니다.
왜 중요한가
은행 시스템에는 시계가 가리키는 시각(posted_at)과 장부가 인정하는 날짜(biz_date)가 따로 있습니다. 계정일자는 마감 배치가 끝나야 넘어가므로, 마감이 도는 동안 들어온 거래는 어느 날짜에 잡을지가 애매해집니다. 여기서 나는 사고가 은행 배치 장애의 큰 몫입니다.
마감 배치는 반드시 멱등해야 합니다. 같은 계정일자에 대해 몇 번을 돌려도 결과가 같아야 한다는 뜻입니다. 그렇지 않으면 배치가 죽었을 때 다시 돌리는 것 자체가 새로운 장애가 됩니다. 재수행 방식은 두 가지뿐인데, 이미 반영된 것 위에 안 된 것만 더하는 증분 방식은 "이미 반영된 것이 전부 옳다" 를 전제합니다. 그 전제가 깨져 있으면 증분 방식은 잘못 들어간 것을 빼낼 수단이 없습니다.
이번 사건은 이렇습니다. 2026-08-25 일마감 배치가 22:10 에 시작해 도중에 죽었습니다. 계정일자 2026-08-25 의 분개행 240줄 중 153줄이 반영된 상태이고, 마감 산출물의 잔액 합이 0 이 아닙니다. 게다가 마감이 도는 동안 온라인 채널 하나가 계속 옛 계정일자를 찍고 있었습니다. 컷오프는 2026-08-25T22:10:00 이고 익영업일은 2026-08-26 입니다.
단계
/root/bank/gen_eod.py를 만들어 실행해/root/bank/eod.db를 만드세요. 분개행 372줄과 마감 이력이 들어갑니다./root/bank/eod_state.txt에 일곱 줄을 적으세요.business_date=,eod_status=는sys_param에서,txn_total=,applied=,unapplied=는 그 계정일자의txn에서,balance_rows=,closing_sum=은daily_balance에서 냅니다.posted_at이 컷오프 이후이면서biz_date가2026-08-25인 줄을/root/bank/misdated.csv에txn_id,posted_at,biz_date,eod_applied머리글로 뜨세요. 그리고/root/bank/misdated.txt에misdated=,misdated_applied=,naive_after_cutoff=세 줄을 적습니다. 마지막 값은 계정일자를 보지 않고posted_at만으로 걸렀을 때의 줄 수입니다.- 그 줄들의
biz_date를2026-08-26으로 옮기고eod_applied도0으로 되돌리세요. 이미 마감이 끝난2026-08-24는 건드리지 않습니다. /root/bank/rerun_plan.md에## 두 가지 재수행 방식,## 선택과 근거,## 멱등성을 어떻게 보장하나,## 중단 기준네 절로 계획을 쓰세요. 선택과 근거 절에는 재계산 대상 줄 수가 숫자로 들어가야 합니다./root/bank/eod_rerun.sh를 만들어 한 번 실행하고, 산출물을/root/bank/db_run1.csv에biz_date,account,debit_total,credit_total,closing_balance머리글로 뜨세요.eod_run에 성공 이력도 남깁니다.- 같은 스크립트를 한 번 더 실행하고
/root/bank/db_run2.csv를 뜬 뒤,/root/bank/idempotency.txt에run1_rows=,run1_closing_sum=,run2_rows=,run2_closing_sum=,diff=다섯 줄을 적으세요. sys_param의eod_status를CLOSED로,business_date를2026-08-26으로 넘기고,/root/bank/eod_report.md에## 무엇이 멈췄나,## 계정일자가 왜 어긋났나,## 재수행을 어떻게 안전하게 만들었나,## 검증,## 남은 위험다섯 절로 보고서를 쓰세요.
참고
- 컷오프 값은 하드코딩하지 말고
SELECT value FROM sys_param WHERE name='eod_cutoff'로 읽으면 조건이 어긋날 여지가 줄어듭니다. - 마감 산출물의
closing_balance합이 0 인지가 가장 빠른 건전성 신호입니다. 0 이 아니면 전표 한쪽만 반영된 줄이 있습니다. - 흔한 실수 1: 3단계에서
posted_at만으로 거르는 것. 제대로 익일자를 찍은 다른 채널의 거래까지 딸려 옵니다. - 흔한 실수 2: 4단계에서
biz_date만 바꾸고eod_applied를 그대로 두는 것. 그 줄은 이전 마감의 산출물에 들어간 채로 다음 마감에서는 건너뛰어집니다. - 흔한 실수 3: 6단계 스크립트를
INSERT만으로 짜는 것. 두 번째 실행에서 값이 두 배가 됩니다.
마감이 죽은 순간 재현하기
/root/bank/gen_eod.py 를 만들어 실행해 /root/bank/eod.db 를 만드세요. 분개행 372줄과 마감 이력이 들어갑니다.
python3 로 /root/bank/eod.db 를 만듭니다. 표는 sys_param, txn, daily_balance, eod_run 네 개이고 분개행은 372줄입니다.
죽은 마감이 어디까지 갔는지 세기
/root/bank/eod_state.txt 에 일곱 줄을 적으세요. business_date=, eod_status= 는 sys_param 에서, txn_total=, applied=, unapplied= 는 그 계정일자의 txn 에서, balance_rows=, closing_sum= 은 daily_balance 에서 냅니다.
마감 대상은 posted_at 이 아니라 biz_date 로 고릅니다. 산출물의 closing_balance 합이 0 인지도 보세요 — 0 이 아니면 전표 한쪽만 반영된 것입니다.
계정일자가 잘못 찍힌 거래 찾기
posted_at 이 컷오프 이후이면서 biz_date 가 2026-08-25 인 줄을 /root/bank/misdated.csv 에 txn_id,posted_at,biz_date,eod_applied 머리글로 뜨세요. 그리고 /root/bank/misdated.txt 에 misdated=, misdated_applied=, naive_after_cutoff= 세 줄을 적습니다. 마지막 값은 계정일자를 보지 않고 posted_at 만으로 걸렀을 때의 줄 수입니다.
조건이 두 개입니다. posted_at 이 컷오프 이후이고, 동시에 biz_date 가 마감일인 줄. 앞의 조건만으로 거르면 제대로 익일자를 찍은 다른 채널의 거래까지 딸려 옵니다.
계정일자를 익영업일로 옮기기
그 줄들의 biz_date 를 2026-08-26 으로 옮기고 eod_applied 도 0 으로 되돌리세요. 이미 마감이 끝난 2026-08-24 는 건드리지 않습니다.
biz_date 만 바꾸면 안 됩니다. 이미 마감 산출물에 반영된 줄이 섞여 있으니 eod_applied 도 함께 되돌려야 합니다. 이미 마감이 끝난 전날은 건드리지 마세요.
재수행 방식 정하고 근거 쓰기
/root/bank/rerun_plan.md 에 ## 두 가지 재수행 방식, ## 선택과 근거, ## 멱등성을 어떻게 보장하나, ## 중단 기준 네 절로 계획을 쓰세요. 선택과 근거 절에는 재계산 대상 줄 수가 숫자로 들어가야 합니다.
네 절이 필요합니다. 선택과 근거 절에는 재계산 대상이 몇 줄인지 숫자로 적고, 멱등성 절에는 어느 표를 어떻게 다루는지 적으세요.
재수행 스크립트 만들어 한 번 돌리기
/root/bank/eod_rerun.sh 를 만들어 한 번 실행하고, 산출물을 /root/bank/db_run1.csv 에 biz_date,account,debit_total,credit_total,closing_balance 머리글로 뜨세요. eod_run 에 성공 이력도 남깁니다.
손으로 SQL 을 치지 말고 스크립트로 만드세요. 두 번째 실행이 첫 번째와 문자 그대로 같아야 멱등성을 시험할 수 있습니다. 그리고 1회차 산출물을 CSV 로 떠 두세요.
한 번 더 돌려 멱등성 증명하기
같은 스크립트를 한 번 더 실행하고 /root/bank/db_run2.csv 를 뜬 뒤, /root/bank/idempotency.txt 에 run1_rows=, run1_closing_sum=, run2_rows=, run2_closing_sum=, diff= 다섯 줄을 적으세요.
같은 스크립트를 그대로 다시 실행하고 산출물을 다시 뜨세요. 두 CSV 를 줄 단위로 비교한 결과가 diff 입니다. 값이 두 배가 됐다면 지우지 않고 더한 것입니다.
마감 확정하고 계정일자 넘기기
sys_param 의 eod_status 를 CLOSED 로, business_date 를 2026-08-26 으로 넘기고, /root/bank/eod_report.md 에 ## 무엇이 멈췄나, ## 계정일자가 왜 어긋났나, ## 재수행을 어떻게 안전하게 만들었나, ## 검증, ## 남은 위험 다섯 절로 보고서를 쓰세요.
산출물을 만든 것과 마감을 확정한 것은 다른 일입니다. sys_param 의 마감 상태와 계정일자를 함께 넘기세요. 그리고 보고서 다섯 절을 쓰세요.