PG Simulator 환경 기반 장애 전파 차단 전략 정리 (Timeout / Retry / CircuitBreaker)

zion·2025년 12월 4일

PG 시뮬레이터를 로컬 PC(M1 Air)에서 실행하며, 실제 운영 환경에서 적용할 Failure Ready System을 어떻게 설계할지 수치 기반으로 정리했습니다.


PG Simulator 사양 요약

구분
요청 성공 확률60%
요청 지연100ms ~ 500ms
처리 지연1s ~ 5s
처리 결과 성공률성공 70% / 한도초과 20% / 카드오류 10%

1. Timeout 설정

✔ Feign Connect/Read Timeout

connect-timeout

  • 서버와 TCP 연결을 맺는 데 걸리는 시간
  • PG 요청 지연 max = 500ms
    600ms로 설정

read-timeout

  • 서버가 응답을 보내기까지 기다리는 시간
  • PG 처리 지연 max = 5s
    6000ms 설정

✔ Resilience4j TimeLimiter

Feign timeout 외에 TimeLimiter는 전체 실행 시간을 제한한다.

특징:

  • 호출 전체가 일정 시간을 넘기면 강제 취소
  • fallback 가능
  • CircuitBreaker 실패로 카운트됨

PG 처리 지연 max = 5s
timeout-duration = 5.5s (~6s) 설정


2. Retry 설정

✔ 요청 단계 성공률 기반 계산

PG 요청 성공률 = 60%
→ 즉, Retries가 너무 많으면 PG 자체 부하만 키움

Retry 성공률 증가 효과

max-attempts성공률부하 증가율
160%0.6
284%0.6 + 0.6 * 0.4
393.6%0.6 + 0.24 + 0.096

→ 2회 → +24%
→ 3회 → +9.6% (효용 급감)


✔ Retry 부하 증가율

재시도는 성공률 증가보다 부하 폭증이 더 크다.

Retry 횟수최대 시도기대 시도부하 증가율
011.01.0x
121.41.4x
231.561.56x
341.6241.62x
561.7811.78x

➡ 재시도 1회만 해도 부하가 40% 증가
→ 실제로는 retry 를 하지 않는 것이 가장 현실적인 선택


3. CircuitBreaker 설정

PG 자체의 “정상적인 실패율”을 먼저 계산한다.

PG 성공률 =

요청 성공률 60% × 처리 성공률 70% = 42%

즉, 실패율:

100% - 42% = 58%

따라서 CircuitBreaker의 failure-rate-threshold
→ 최소 65% 이상이 되어야 정상 실패를 차단하지 않음.


4. 로컬 PG Simulator 성능 측정 (wrk)

brew install wrk
wrk -t2 -c200 -d30s -s post.lua http://localhost:8082/api/v1/payments

post.lua

wrk.method = "POST"
wrk.body   = [[
{
  "orderId": "1351039135",
  "cardType": "SAMSUNG",
  "cardNo": "1234-5678-9814-1451",
  "amount": "5000",
  "callbackUrl": "http://localhost:8080/api/v1/payments/callback"
}
]]
wrk.headers["Content-Type"] = "application/json"
wrk.headers["X-USER-ID"]    = "135135"

✔ wrk 결과 해석 (PG 단독)

  • TPS ≈ 300
  • 평균 지연 ≈ 625ms
  • Max latency = 2000ms
  • Timeout = 202건

2초 정도의 지연은 사용자가 느끼기에 긴 시간으로 느껴지지 않아, Slow-call 기준을 다음처럼 설정:

slow-call-duration-threshold: 2000ms

PG 자체가 감당 가능한 처리량 = 약 300 TPS
→ 이 이상의 호출이 오면 PG가 먼저 병목됨


5. 결제 요청 API : Retry 적용 전/후 성능 비교

Retry 여부를 검증하기 위해 동일 조건에서 테스트함.

wrk -t2 -c200 -d30s -s post.lua http://localhost:8080/api/v1/payments

✔ Retry 1회 적용 시 비교

지표Retry ORetry X변화
요청 수28542754+100
성공 TPS94.9691.65+3.3%
Latency1.26s1.24s거의 동일
Non-2xx109189-42% ↓
Timeout12361223비슷

결과

  • timeout·latency는 거의 변화 없음
  • Non-2xx는 42% 감소 (큰 개선)
  • 부하 증가는 아주 조금

➡ 따라서 retry 1회(max-attempts:2) 가 최적


최종 설정값

feign:
  client:
    config:
      pgClient:
        connect-timeout: 600 #PG 요청 지연 max = 500ms
        read-timeout: 6000 #PG 처리 지연 max = 5s
resilience4j:
  timelimiter:
    instances:
      pgTimeLimiter:
        timeout-duration: 5s #PG 처리 지연 max = 5s, read-timeout 보다 작은값
        cancel-running-future: true
  retry:
    instances:
      pgRetry:
        max-attempts: 2
        wait-duration: 1s
        retry-exceptions:
          - feign.RetryableException
        fail-after-max-attempts: true
  circuitbreaker:
    instances:
      pgCircuit:
        failure-rate-threshold: 65 # 요청 성공률 60% × 처리 성공률 70% = 42%, 실패율  58%
        slow-call-rate-threshold: 50
        slow-call-duration-threshold: 2s # Max latency = 2000ms
        permitted-number-of-calls-in-half-open-state: 30 # TPS 300 의 10~20%
        minimum-number-of-calls: 30 # 타임아웃 2s, TPS 300
        sliding-window-type: TIME_BASED # 트래픽 차이가 클경우, TIME_BASED
        sliding-window-size: 60 # 장애탐지가 중요하므로 작게
        wait-duration-in-open-state: 5s #회복하는데 걸리는 시간 기본 5s 적용
        automatic-transition-from-open-to-half-open-enabled: true
        record-exceptions:
          - feign.FeignException
          - org.springframework.web.client.HttpServerErrorException
          - java.io.IOException
          - java.util.concurrent.TimeoutException      

🔚 마무리

이번 글은 PG Simulator의 수치를 기반으로
Timeout, Retry, CircuitBreaker 값을 합리적으로 결정한 과정을 정리했습니다.

실제 운영 환경의 성능, 네트워크 품질에 따라 수치는 달라질 수 있어, 차후 실제 환경 부하 테스트를 통해 더 세밀한 수정이 필요합니다. 특히 위 방식이 로컬을 기준으로 측정된 값 + 제공된 PG 사양이 혼합되어 있지만, 수치를 기반으로 근거있는 결정을 해볼수 있었습니다.

profile
be_zion

0개의 댓글