C.W.K.
Stream
Lesson 05 of 05 · published

outbox

~10 min · outbox, store-and-forward, durability, replay

Level 0Empty Shelf
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"이 응답엔 이미 결제했어. 잃으면 다시 사는 거야. 그러니 뭘 하기 전에 먼저 저장해."

마지막 틈: 결제했는데, 보고를 못 해

happy path 를 마지막 단계까지 걸어. executor 가 예약하고, 제출하고, provider 한테서 좋은 응답을 받았어. 돈은 썼고 — 이게 핵심인데 — 결과가 이제 진짜 가치 있어, 얻는 데 돈이 들었으니까. 다음 일은 그 결과를 durable 수락을 위해 control plane 에 건네는 거야. 바로 거기 마지막 틈이 있어: 딱 그 순간 control plane 이 잠깐 도달 불가면? 네트워크 깜빡, control plane 재시작, Wi-Fi 한 순간. executor 는 배달 못 하는 결제된 응답을 쥐고 있어.

executor 가 그 응답을 떨궈버리면 — 크래시, 포기, 넘어감 — 결과가 사라지고, 되찾는 유일한 방법은 다시 결제하는 거야. 트랙 전체가 막아온 바로 그 실패가 결승선에서 다시 나타나. 예약 ledger 는 지출을 보호했어; 이제 뭔가가 네가 이미 산 응답을 보호해야 해.

확인하기 전에 로컬에 저장해

Recall 의 답은 outbox 야. 성공한 provider 응답이 도착하는 즉시, executor 가 그걸 로컬 outbox 파일에 atomic 하게 써 — control plane 에 완료를 확인하려 하기 전에. 그 로컬 사본이 디스크에 안전히 놓인 뒤에야 보고를 시도해. 이제 순서가 생존성을 보장해:

  • control plane 도달 불가? 결제된 응답이 outbox 에 안전해. 연결이 돌아오면 executor 가 replay 해 — 두 번째 구매 없음.
  • executor 가 결제 후 보고 전에 크래시? 재시작하면 outbox 가 여전히 응답을 쥐고, 제출 준비 완료.
  • control plane 이 자기 ledger 에 응답을 durably 기록했다고 확인? 그때야 executor 가 outbox 사본을 삭제해 — 가치가 딴 데 안전하니, 로컬 사본은 더 안 필요해.

이건 store-and-forward 패턴이고, 예약의 거울상이야. 예약은 지출 전에 의도를 기록하고; outbox 는 지출 후에 결과를 보존해. 둘이 유료 호출을 괄호로 묶어서, 그 어느 쪽 크래시도 절대 두 번 결제하게 강요 안 해.

두 번 결제 안 하는 것의 모양

물러서서 트랙 전체를 하나의 메커니즘으로 봐. 예약은 지출이 일어나기 전에 보이게 만들어. content-addressed identity 는 같은 구매를 알아보게 만들어 한 번 사게 해. ambiguous 상태랑 no-retry 규칙은 추측이 돈을 쓰는 데선 추측을 거부해. 그리고 outbox 는 이미 결제한 응답이 일시적 outage 에 절대 안 잃히게 보장해. 각 조각이 크래시 창 하나를 닫고; 함께 'pay once' 를 희망에서 기계가 강제하는 불변식으로 바꿔. 그게 시스템 설계에서 돈을 일급 관심사로 다룬다는 뜻이야.

Code

확인 전에 결제된 응답을 outbox 에 저장·python
response = provider.transcribe(chunk)   # 돈 이미 씀

# 1. 결제된 결과를 먼저 로컬에 보호 — atomic write.
outbox.save_atomic(submission_id, response)

# 2. 이제서야 control plane 에 건네려 시도.
try:
    control_plane.complete(submission_id, response)
except Unreachable:
    pass  # 괜찮음: outbox 가 쥐고 있음; 나중에 replay, 재구매 없음

# 재연결 / 재시작 시: outbox 에 남은 걸 replay.
for pending in outbox.list():
    control_plane.complete(pending.id, pending.response)

# control plane 이 ledger 에 durable 하다고 확인 -> 그때 삭제.
outbox.remove(submission_id)

External links

Exercise

네 일에서 비싸거나 재현하기 힘든 걸 얻고 나서 즉시 어딘가로 보내는 곳을 찾아(DB 저장, 서비스에 POST, enqueue). 물어: 그 전송이 실패하면 비싼 결과가 잃혀? outbox 를 설계해: 결과를 먼저 durable 로컬 저장에 쓰고, 배달 시도하고, 실패 시 replay 하고, 목적지가 수신 확인한 뒤에야 로컬 사본을 삭제해. 방금 없앤 재비용을 적어봐.
Hint
취약한 모양은 '비싼 걸 계산/구매하고, 그다음 보내고, 전송 실패하면 사라짐' 이야. durable 로컬 hop 을 넣어: 전송 전에 결과를 persist 해, 실패한 배달이 재구매가 아니라 replay 가 되게. 로컬 사본은 확인된 수신 시에만 삭제해 — 그 확인이 가치가 딴 데 안전하다고 알려주는 거야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.