"이 응답엔 이미 결제했어. 잃으면 다시 사는 거야. 그러니 뭘 하기 전에 먼저 저장해."
마지막 틈: 결제했는데, 보고를 못 해
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' 를 희망에서 기계가 강제하는 불변식으로 바꿔. 그게 시스템 설계에서 돈을 일급 관심사로 다룬다는 뜻이야.