27. 멱등 dedup 키의 TTL이 재시도 지평보다 짧아 발생하는 이중 처리 (C#)

난이도 상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// 시나리오: 결제 콜백 → 재화 지급의 멱등 처리 (C#, 서버-서버)
// - 결제사(PG)는 지급 콜백을 at-least-once 로 보낸다. 네트워크 오류/타임아웃 시
//   PG 는 수 분~수 시간에 걸쳐 재시도하고, 운영 중 DLQ 재투입도 있다.
// - 이중 지급을 막기 위해 결제 고유 키를 Redis 에 저장해 중복을 거른다.
//
// 요구사항: 같은 결제 콜백이 몇 번 오든 재화는 정확히 한 번만 지급돼야 한다.
//
// 과제: 대부분은 잘 막히지만, 특정 조건에서 같은 결제가 두 번 지급된다.
//       (A),(B) 를 근거로 원인과 수정안을 설명하라.

using System;

public interface IRedis
{
    bool Exists(string key);
    void SetEx(string key, string val, TimeSpan ttl);
}

public sealed class RewardGranter
{
    private readonly IRedis _redis;
    private static readonly TimeSpan DedupTtl = TimeSpan.FromMinutes(10);

    public RewardGranter(IRedis redis) { _redis = redis; }

    public void OnPaymentCallback(string paymentId, int playerId, long amount)
    {
        string key = $"dedup:pay:{paymentId}";

        if (_redis.Exists(key))          // (A) 이미 처리됨?
            return;

        GrantCurrency(playerId, amount); // 실제 지급 (DB 커밋)

        _redis.SetEx(key, "1", DedupTtl); // (B) 처리 표시
    }

    private void GrantCurrency(int playerId, long amount) { /* DB 트랜잭션 */ }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.