LabHub
배우기 러닝패스 코스

은행 현장의 언어 · 동시 이체와 한도, 그리고 트랜잭션 · 실습

두 창구가 같은 잔액을 봤다 — 동시 출금과 한도

LabHub 에서 이어서 보기

목표

같은 계좌에 겹쳐 들어온 출금으로 마이너스 잔액을 재현 가능하게 만들어 보고, 확인과 갱신을 한 문장에 합쳐 그 틈을 없앤다. 일일 누적 한도를 같은 방식으로 걸고, 잠금 경합에만 상한 있는 재시도를 붙인 뒤, 불변식 점검과 증거 보고서로 닫는다.

왜 중요한가

잔액을 읽어 확인하고 나서 따로 차감하는 구조는 한 요청씩 들어오는 동안에는 영원히 정상이다. 같은 계좌에 요청이 겹치는 순간 둘 다 같은 잔액을 근거로 승인되고, 차감은 두 번 다 반영돼 잔액이 음수가 된다. 이때 원장과 잔액은 서로 어긋나지 않아서 "원장 합계 == 잔액" 만 보는 마감 점검은 이 사고를 통과시킨다.
부하를 걸어 재현하려 들면 확률에 기대게 된다. 이 실습은 부하 대신 겹치는 지점을 맞추는 방법으로 결정적인 재현을 만든다. 요청 마흔 건이 1초도 걸리지 않고, 돌릴 때마다 같은 숫자가 나온다.
채점기는 여러분의 문장을 믿지 않는다. 남긴 기록 파일의 값을 실물 DB 와 하나씩 대조한다. 채점기가 경쟁을 다시 일으키지는 않는다 — 재현이 확률이면 채점도 흔들리기 때문이다.

단계

1. /root/limit/make_bank.py 를 만들어 실행해 /root/limit/bank.db 에 계좌 6개와 개시 원장 6줄을 넣는다.
2. /root/limit/race_naive.py 로 읽고-고치고-쓰기 경로를 만들어 A-RACE 계좌에 2명 x 20라운드로 겹쳐 돌리고 결과를 /root/limit/race_naive.json 에 남긴다.
3. /root/limit/transfer.py 에 조건부 갱신 경로를 만들어 A-SAFE 계좌에 같은 조건으로 돌리고 결과를 /root/limit/race_safe.json 에 남긴다.
4. A-FEE 계좌로 쓰기 잠금을 직접 겪어 보고 busy_timeout 0과 넉넉한 값의 차이를 /root/limit/lock_probe.json 에 남긴다.
5. A-DAILY 계좌에 일일 누적 한도 300000원을 갱신 문장 안에서 걸고 결과를 /root/limit/daily_limit.json 에 남긴다.
6. A-RETRY 계좌에 3명 x 10라운드로 요청을 넣고, 잠금 경합에만 거는 상한 있는 재시도 정책과 그 결과를 /root/limit/retry_policy.json 에 남긴다.
7. 계좌 6개에 불변식 세 가지를 걸어 위반 목록을 /root/limit/invariant.json 에 남긴다.
8. 여기까지의 숫자를 원장에서 뽑아 /root/limit/limit_report.md 에 네 절로 정리한다.

참고

단계 8개

  1. 계좌 스냅샷 만들기
  2. 겹치는 두 요청으로 마이너스 잔액 재현하기
  3. 확인과 갱신을 한 문장에 넣기
  4. 쓰기 잠금과 busy_timeout 직접 겪어 보기
  5. 일일 누적 한도를 같은 문장 안에 넣기
  6. 재시도를 정책으로 적고 상한 지키기
  7. 불변식 세 가지로 계좌 여섯 개 점검하기
  8. 증거 보고서로 닫기