Repository navigation
payment recovery model
확정 도중 멈춘 결제를 되살리는 복구 스케줄러가 정상 결제를 해치지 않도록 도착 상태와 전이 조건을 다시 세운 기록
| 용어 | 짧은 풀이 |
|---|---|
| 복구 스케줄러 |
IN_PROGRESS 로 오래 머문 결제를 주기로 훑어 옮기는 안전망 |
| 확정 결과 | pg-service 가 벤더 판정을 payment-service 로 돌려보내는 메시지 |
| 비재시도 분류 | 재시도해도 결과가 바뀌지 않는 예외를 백오프 없이 DLQ 로 보내는 목록 |
| 1차 기준 시간 |
IN_PROGRESS 를 멈춘 것으로 판정하는 경과 시간 |
| 2차 기준 시간 |
AWAITING_RESULT 를 회신 불가로 판정하는 경과 시간 |
확정은 payment-service 가 명령을 보내고 pg-service 가 결과를 돌려주는 왕복이라, 그 사이에 프로세스가 죽으면 결제가 IN_PROGRESS 로 남는다.
- 아무도 다시 건드리지 않으면 그대로 영구히 남음
- 복구 스케줄러 (
PaymentReconciler)가 주기로 훑어 다른 상태로 옮김
일정 시간이 지나면 해당 결제 상태를 READY 로 변경했으나, READY 는 확정에 진입하기 전을 뜻하므로 확정 결과로 전이되지 않는 상태가 된다.
| 기대 | 실제 |
|---|---|
| 재시도 스케줄러가 재처리 | 재시도 대상은 payment_outbox 의 PENDING 행이라, 상태를 옮기는 것만으로는 아무 재처리도 일어나지 않음 |
| 늦게 온 승인으로 완료 |
DONE 전이가 IN_PROGRESS 에서만 허용돼 상태 예외로 거부 |
| 종결 못 하면 만료 |
EXPIRED 전이는 주문이 시작 전일 때만 허용 — 확정에 진입한 주문은 이미 실행 중 |
결국엔 결제가 종결도 만료도 못 하는 상태로 쌓였고, 운영자 목록에도 뜨지 않아 영구적으로 남는 결제가 생겼다.
한 건의 거부가 뒤 메시지를 막고, 그 지연이 다시 기준 시간을 넘기는 결제를 만들어 복구 스케줄러가 READY 로 되돌리는 악순환이 반복된다.
flowchart TD
A[부하 지속 - 확정 회신이 1차 기준 시간을 넘김] --> B[복구 스케줄러가 READY 로 되돌림]
B --> C[뒤늦은 승인이 거부됨]
C --> D[재시도가 리스너 스레드를 잡음]
D --> E[뒤 메시지가 전부 대기]
E -->|회신 지연| A
- 거부 예외가 비재시도 목록에 없으면 컨슈머가 백오프를 끝까지 소진
- 2026-09-01 실측에서 기준 시간을 넘긴 결제가 25초 만에 965 건에서 1,879 건으로 증가
| 안 | 장점 | 단점 |
|---|---|---|
READY 에서 DONE 전이 허용 |
코드 변경 최소, 상태 추가 없음 | 확정에 진입한 적 없는 결제가 승인으로 완료 — 상태의 정의가 무너진다 |
AWAITING_RESULT 신설 (채택) |
거부 자체가 사라지고, 회신 없는 건도 끝낼 길이 생김 | 상태가 늘어 판정하는 곳도 함께 늘어남 |
이미 확정에 진입했고 결과를 기다린다는 뜻만 갖는 상태인 AWAITING_RESULT 를 신설하고, 1차 기준 시간 초과 시 IN_PROGRESS 에서 옮기도록 했다.
- 복구 스케줄러는 결제 상태만 바꾸고 주문은 실행 중에 그대로 둠
- 주문 단위 완료 전이는 원래부터 성립했고, 막고 있던 것은 결제 쪽 상태 하나뿐
-
DONE전이 가드를IN_PROGRESS와AWAITING_RESULT두 상태를 받도록 확장
stateDiagram-v2
[*] --> READY: 결제 접수
READY --> IN_PROGRESS: 확정 진입
IN_PROGRESS --> DONE: 승인 결과
IN_PROGRESS --> FAILED: 실패 결과
IN_PROGRESS --> QUARANTINED: 격리 결과
IN_PROGRESS --> AWAITING_RESULT: 1차 기준 시간 초과
AWAITING_RESULT --> DONE: 늦은 승인
AWAITING_RESULT --> FAILED: 늦은 실패
AWAITING_RESULT --> QUARANTINED: 늦은 격리 또는 2차 기준 시간 초과
READY --> FAILED: 실패 결과
READY --> EXPIRED: 만료 - 주문이 시작 전일 때만
QUARANTINED --> FAILED: 운영자 안전 종결
-
READY에서DONE으로 가는 화살표가 없는 것이 이 상태 머신의 요점 - 부수 효과로 만료 경로 정상화 —
READY를 세팅하는 곳이 결제 접수 하나뿐이라READY인 결제는 전부 주문이 시작 전
| 단계 | 대상 | 기준 시각 | 도착 상태 |
|---|---|---|---|
| 1차 |
IN_PROGRESS + 1차 기준 시간 초과 |
확정 진입 시각 | AWAITING_RESULT |
| 2차 |
AWAITING_RESULT + 2차 기준 시간 초과 |
상태 변경 시각 | QUARANTINED |
확정 진입 시각은 진입할 때 한 번 박히고 이후 갱신되지 않는다.
- 그 값으로 재면 방금
AWAITING_RESULT로 옮긴 건이 같은 주기에 곧바로 격리됨 - 뒤늦게 도착할 정상 승인이 적용 불가 상태를 만나게 됨
벤더가 승인했는지 모르는 채로 끝내면 실제 청구와 기록이 어긋나므로 판단을 보류한다.
- 판단 보류는 이 시스템에서
QUARANTINED가 담당하는 역할 - 운영자가 벤더 상태를 조회한 뒤 안전 종결
확정 사이클의 어느 지점에서 멈추든 담당 주체가 정해져 있고, 결제는 종결이나 운영자 확인 중 하나에 도달한다.
| 멈춘 지점 | 결제 상태 | 회수 주체 | 다음 상태 |
|---|---|---|---|
| 확정에 진입하기 전 | READY |
만료 스케줄러 (5분 주기) | EXPIRED |
| 확정 명령이 아직 나가지 못함 |
IN_PROGRESS + Outbox PENDING
|
OutboxWorker (5초 주기) |
IN_PROGRESS 유지 — 명령만 재발행 |
| 명령은 나갔으나 회신이 없음 | IN_PROGRESS |
복구 스케줄러 1차 | AWAITING_RESULT |
| 회신이 뒤늦게 도착 | AWAITING_RESULT |
확정 결과 소비 경로 |
DONE / FAILED
|
| 회신이 끝내 오지 않음 | AWAITING_RESULT |
복구 스케줄러 2차 | QUARANTINED |
| 자동 판정 불가 | QUARANTINED |
운영자 안전 종결 | FAILED |
- 상태 기반 복구 모델 및 재시도 로직 — 복구 모델의 최초 도입 (Phase 2)
- 도메인 상태 머신과 장애 내성 복구 체계 — 상태 머신 재정비 (Phase 5)
- PG 결제 확인 흐름 — 확정 결과를 만들어 보내는 쪽
- 메시지 전달 보장 + dedupe — DLQ 와 소비 멱등성 일반론
🏠 Home
- MSA 전환
- 이벤트 드리븐 코레오그래피
- 재고 캐시 보상 회복 — Lua atomic
- Outbox 패턴
- 메시지 전달 보장 + dedupe
- 결제 복구 모델
- PG 결제 확인 흐름
- 분산 트레이싱
Phase 1 · 데이터 정합성 확립
Phase 2 · 결합도 해소 및 자가 복구력
Phase 3 · 운영 가시성 및 안정성
Phase 4 · 데이터 정합성 심화 및 중복 제어
Phase 5 · 비동기 결제 아키텍처 전환
ETC · 설계 유연성
ETC · AI 기반 개발 워크플로우