A multi-step order workflow fails at the charge step but leaves the stock reservation from step 1 in place. Add reverse-order compensation so a mid-flight failure undoes everything already done.
You'll predict the failure first, then write the fix and watch your own code drive the behavior.
주문 하나가 한 번에 끝나지 않고 세 단계로 나뉜다. 먼저 재고를 잡고, 그다음 카드로 결제하고, 마지막에 배송한다. 끝낸 단계는 'done' 목록에 하나씩 적어둔다.
// step 1
int stock = inventory.getOrDefault(item, 0);
if (stock <= 0) return false;
inventory.put(item, stock - 1);
done.add("reserve");
// step 2 charge, step 3 ship follow in order“An order runs three steps in order: reserve stock, charge the card, then ship.”
checking microphone…
문제는 중간에 실패할 때다. 재고는 잡았는데 지갑에 돈이 모자라서 결제가 실패한다. 그러면 주문은 안 됐는데 재고만 묶여 있다.
⚖️ Trade-off: 그냥 false만 돌려주고 끝내면, 잡아둔 재고가 영원히 묶인다. 주문도 없는데 물건은 못 판다.
🔧 도구:반환값 false (그냥 멈추기 — 여기선 X)
// charge fails but step 1 already mutated inventory
int bal = wallet.getOrDefault(user, 0);
if (bal < price) {
return false; // BUG: 'reserve' in done, stock stuck at 0
}“If the charge fails, step one already reserved stock, so the item is stuck with no order.”
checking microphone…
왜 DB 트랜잭션처럼 그냥 롤백하면 안 되나? 단계들이 서로 다른 시스템(재고, 결제, 배송)이라 하나의 트랜잭션으로 묶을 수가 없다. 자동 되돌리기가 없다.
⚖️ Trade-off: DB 한 곳이면 COMMIT/ROLLBACK 한 줄로 끝나는데, 시스템이 여러 개라 그게 안 된다.
🔧 도구:DB 트랜잭션 ROLLBACK (한 DB 전용 — 여기선 X)분산 트랜잭션 2PC (느리고 무거움 — 보통 X)
“There's no automatic rollback here because each step lives in a different system.”
checking microphone…
해결: 각 단계마다 '되돌리는 짝꿍 동작'을 미리 하나씩 만든다. 재고 잡기의 짝꿍은 재고 풀기, 결제의 짝꿍은 환불. 이게 보상(compensation) 동작이다.
⚖️ Trade-off: 단계마다 되돌리는 코드를 손으로 직접 다 짜야 한다. 공짜로 안 생긴다.
✅ Fix: 어차피 단계가 몇 개 안 되니, 짝꿍 하나씩 만드는 건 감당 가능. 이게 saga의 핵심.
🔧 도구:Saga 패턴보상 트랜잭션(compensating action)
// each forward step has a matching inverse (compensation)
Map<String, Runnable> compensate = Map.of(
"reserve", () -> inventory.merge(item, 1, Integer::sum),
"charge", () -> wallet.merge(user, price, Integer::sum)
);“So for each step I write a matching undo action — the compensation.”
checking microphone…
되돌리는 순서가 중요하다. 끝낸 순서의 '거꾸로', 즉 가장 최근에 한 것부터 되돌린다. done 목록을 뒤에서부터 읽으면서 짝꿍 동작을 실행한다.
⚖️ Trade-off: 순서를 거꾸로 안 하면, 아직 안 일어난 일을 되돌리려다 꼬일 수 있다.
✅ Fix: done 목록을 역순으로 돌면 끝. 결제 실패 때는 재고 풀기만 실행돼서 깨끗해진다.
🔧 도구:done 로그 역순 순회reverse-order compensation
// undo completed steps newest-first from the done log
for (int i = done.size() - 1; i >= 0; i--) {
compensate.get(done.get(i)).run();
}
done.clear();
return false;“I undo the completed steps in reverse order, newest first, from the done log.”
checking microphone…
마지막 함정: 되돌리는 동작 자체도 실패할 수 있다. 예를 들어 환불을 누르는 순간 네트워크가 끊긴다. 그래서 보상은 여러 번 눌러도 안전해야 하고, 실패하면 다시 시도할 수 있어야 한다.
⚖️ Trade-off: 보상이 실패하면 시스템이 어중간한 상태로 남는다. 그냥 두면 더 위험.
✅ Fix: 보상 동작을 멱등하게(같은 걸 두 번 해도 결과 같게) 만들고, 실패하면 재시도하게 한다.
🔧 도구:멱등 보상(idempotent compensation)재시도(retry)데드레터/수동 개입
// idempotent + retryable compensation (set, don't blind-add)
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
try { inventory.put(item, RESERVED_STOCK); break; }
catch (Exception e) { /* retry */ }
}“Compensation can fail too, so each undo must be idempotent and retryable.”
checking microphone…
6단계 영어를 다 말하면 → 이 메커니즘 전체를 영어로 설명할 수 있게 된다.
Say it out loud in your own words — hit these terms — then check against the gold answer.
Say it in your own words — but hit these: saga · compensation · distributed transactions · rollback · java
checking microphone…