은행 현장의 언어 · 일마감과 계정일자 · 실습
죽은 일마감을 멱등하게 되살리기
목표
도중에 죽은 일마감을 재수행해 되살리되, 이중 반영도 누락도 없이 끝낼 수 있게 됩니다. 그리고 계정일자와 시스템 시각을 구분해 마감 대상을 고르는 손을 갖게 됩니다.
왜 중요한가
은행 시스템에는 시계가 가리키는 시각(posted_at)과 장부가 인정하는 날짜(biz_date)가 따로 있습니다. 계정일자는 마감 배치가 끝나야 넘어가므로, 마감이 도는 동안 들어온 거래는 어느 날짜에 잡을지가 애매해집니다. 여기서 나는 사고가 은행 배치 장애의 큰 몫입니다.
마감 배치는 반드시 멱등해야 합니다. 같은 계정일자에 대해 몇 번을 돌려도 결과가 같아야 한다는 뜻입니다. 그렇지 않으면 배치가 죽었을 때 다시 돌리는 것 자체가 새로운 장애가 됩니다. 재수행 방식은 두 가지뿐인데, 이미 반영된 것 위에 안 된 것만 더하는 증분 방식은 "이미 반영된 것이 전부 옳다" 를 전제합니다. 그 전제가 깨져 있으면 증분 방식은 잘못 들어간 것을 빼낼 수단이 없습니다.
이번 사건은 이렇습니다. 2026-08-25 일마감 배치가 22:10 에 시작해 도중에 죽었습니다. 계정일자 2026-08-25 의 분개행 240줄 중 153줄이 반영된 상태이고, 마감 산출물의 잔액 합이 0 이 아닙니다. 게다가 마감이 도는 동안 온라인 채널 하나가 계속 옛 계정일자를 찍고 있었습니다. 컷오프는 2026-08-25T22:10:00 이고 익영업일은 2026-08-26 입니다.
단계
1. /root/bank/gen_eod.py 를 만들어 실행해 /root/bank/eod.db 를 만드세요. 분개행 372줄과 마감 이력이 들어갑니다.
2. /root/bank/eod_state.txt 에 일곱 줄을 적으세요. business_date=, eod_status= 는 sys_param 에서, txn_total=, applied=, unapplied= 는 그 계정일자의 txn 에서, balance_rows=, closing_sum= 은 daily_balance 에서 냅니다.
3. 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 만으로 걸렀을 때의 줄 수입니다.
4. 그 줄들의 biz_date 를 2026-08-26 으로 옮기고 eod_applied 도 0 으로 되돌리세요. 이미 마감이 끝난 2026-08-24 는 건드리지 않습니다.
5. /root/bank/rerun_plan.md 에 ## 두 가지 재수행 방식, ## 선택과 근거, ## 멱등성을 어떻게 보장하나, ## 중단 기준 네 절로 계획을 쓰세요. 선택과 근거 절에는 재계산 대상 줄 수가 숫자로 들어가야 합니다.
6. /root/bank/eod_rerun.sh 를 만들어 한 번 실행하고, 산출물을 /root/bank/db_run1.csv 에 biz_date,account,debit_total,credit_total,closing_balance 머리글로 뜨세요. eod_run 에 성공 이력도 남깁니다.
7. 같은 스크립트를 한 번 더 실행하고 /root/bank/db_run2.csv 를 뜬 뒤, /root/bank/idempotency.txt 에 run1_rows=, run1_closing_sum=, run2_rows=, run2_closing_sum=, diff= 다섯 줄을 적으세요.
8. 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만으로 짜는 것. 두 번째 실행에서 값이 두 배가 됩니다.
단계 8개
- 마감이 죽은 순간 재현하기
- 죽은 마감이 어디까지 갔는지 세기
- 계정일자가 잘못 찍힌 거래 찾기
- 계정일자를 익영업일로 옮기기
- 재수행 방식 정하고 근거 쓰기
- 재수행 스크립트 만들어 한 번 돌리기
- 한 번 더 돌려 멱등성 증명하기
- 마감 확정하고 계정일자 넘기기