준하 파트

  1. [RedisOutboxRetryScheduler.java]에서 REFILL 성공 후 markSuccess를 먼저 찍고, 바로 뒤의 clearIdempotency 예외는 바깥 catch[RedisOutboxRetryScheduler.java]가 다시 FAIL로 되돌립니다. 그 상태로 재시도 상한을 넘기면 [RedisOutboxRetryScheduler.java]에서 DB 원천 잔량까지 복구해 버려, 이미 Redis에 반영된 refill과 중복 크레딧이 생길 수 있습니다.
    1. 개선
  2. [TrafficPolicyBootstrapService.java]에서 주기 reconcile이 사실상 제거됐습니다. 그런데 새 Lua self-heal은 키가 “없을 때만” GLOBAL_POLICY_HYDRATE를 내보내고[deduct_indiv.lua], stale value는 고치지 않습니다. 그래서 write-through/outbox가 끝내 실패하면 Redis에 남은 오래된 정책값이 무기한 유지될 수 있습니다.
  3. [TrafficHydrateRefillAdapterService.java]와 [TrafficHydrateRefillAdapterService.java]에서 hydrate 재시도 소진 시 ERROR로 승격하지 않고 GLOBAL_POLICY_HYDRATE/HYDRATE를 그대로 반환합니다. 그런데 오케스트레이터는 [TrafficDeductOrchestratorService.java]에서 ERROR만 FAILED로 보기 때문에, 실제로는 캐시 복구 실패인 요청이 PARTIAL_SUCCESS로 기록됩니다.
    1. 개선

피드백

1. 두 이슈의 관계 및 통합 분석

두 사안은 모두 "성공적인 리필 이후의 부가 단계에서 발생하는 장애 처리 미흡"으로 인해 데이터 정합성이 깨지는 동일한 성격의 문제입니다.

구분 이슈 A (중복 차감) 이슈 B (중복 충전)
발생 지점 요청-응답 스레드 (Adapter) 백그라운드 스케줄러 (Outbox)
장애 시점 리필 성공 후 실제 잔량 차감 시 리필 성공 후 멱등성 키 제거 시
잘못된 복구 Redis 실패 시 DB Fallback 차감 실행 멱등키 제거 실패 시 DB 잔량 반납(Restore)
결과 Double-Decrement (DB 잔량 2회 삭감) Double-Credit (DB 잔량 복구 + Redis 충전됨)
해결 핵심 리필 후 재시도 시 DB Fallback 금지 멱등키 제거 실패 시 무시 및 성공 유지

2. 제안된 수정 계획 검토 결과 (RedisOutboxRetryScheduler)

제안하신 수정 계획을 코드로 검증한 결과, 데이터 정합성을 위해 반드시 필요한 조치임을 확인했습니다.

3. 최종 종합 의견

검토하신 두 가지 수정 사항은 서로 보완적이며 트래픽 서비스의 데이터 신뢰도를 높이는 데 필수적입니다.

  1. 실시간 경로(Issue A): 리필 후 Redis 장애 시 DB Fallback을 막아 DB 잔량의 과도한 소멸을 방지합니다.
  2. 배치 경로(Issue B): 부차적인 스크립트 실패 시 DB 환불을 막아 DB 잔량의 부당한 생성을 방지합니다.