분산 RPS 리미터에서 포아송 흐름을 이용한 429 오류 모델링
초당 평균 129개의 성공 요청과 150 RPS 제한에서 7개의 429 오류가 발생할 때, 리미터가 왜 지속적으로 작동하는지 의문이 생깁니다. 40분간의 그래프는 15초 간격으로 일정 수준의 오류를 보여줍니다. 요청량의 포아송 분포가 이 현상을 설명합니다: λ = 136.28일 때, 1초 동안 150개 요청을 초과할 확률은 11.27%입니다.
확률 계산 코드:
def get_prob_at_least(border, lmbd):
sum = 1
mult = 1
for i in range(1, border):
mult = mult * lmbd / i
sum += mult
return 1 - sum * math.exp(-lmbd)
get_prob_at_least(151, 129.01 + 7.27)
# 0.1127
이 값이 15초 간격에서 >1이면 관찰된 그래프와 일치합니다.
오류의 조건부 기댓값
제한을 초과할 때 예상 요청 수를 간단히 계산하면 156.4, 오류는 초당 0.6 RPS입니다. 7.27 RPS와의 차이는 설명이 필요합니다.
조건부 기댓값 계산 코드:
def get_expectation_via_conditional_at_least(border, lmbd):
sum_prob = 0
exp_sum = 0
mult = 1
for i in range(1, 10000):
mult = mult * lmbd / i
if (i >= border):
exp_sum += i * mult
sum_prob += mult
return exp_sum / sum_prob
def get_expected_errors_num(events, border):
return (get_expectation_via_conditional_at_least(border, events) - border) * get_prob_at_least(border, events)
모델 조정: 재시도와 샤딩
오류당 1회 재시도(원래 트래픽 132.64 RPS)를 고려하면 오류가 초당 0.72 RPS로 증가하지만, 여전히 부족합니다.
핵심 요소: 6개의 포드 서비스(3개 DC당 2개 포드). 샤딩을 사용하면 각 리미터가 150/6 = 25 RPS를 나누며, 경계값 = 26입니다.
한 리미터에 대한 계산:
limiters_num = 6
retry_num = 2
rps_one_limiter=(129.01 + 7.27 / 2)/limiters_num
border_one_limiter=int(150/limiters_num) + 1
one_limiter_erros = get_expected_errors_num(rps_one_limiter, border_one_limiter)
limited_num = one_limiter_erros * limiters_num * retry_num
# limited_num ≈ 6.85
모델은 관찰된 7.27 RPS 오류 대비 6.85 RPS를 설명합니다. 리미터는 권한 동기화 없이 작동하며, 주기적인 이웃 교환과 카운터 리셋을 수행합니다.
분산 리미터의 구현 특징
가능한 옵션:
- Leaky Bucket: 지속적인 토큰 누출.
- 초 시작 시 리셋: 고정된 주기, 급증에 취약.
- 동적 분할: 할당 계산을 위한 하트비트 교환.
동기화 없는 샤딩 시나리오에서 각 포드는 제한을 독립적으로 결정하여 전체 실패가 샤드 수에 비례해 선형 증가합니다.
핵심 요약
- 포아송 분포는 봇 없이(무한 사용자의 제한적 경우) 소비자 트래픽을 정확히 모델링합니다.
- 동기화 없이 RPS 리미터를 샤딩하면 오류가 포드 수에 비례해 증가합니다.
- 클라이언트 재시도는 문제를 악화시켜 모델 조정이 필요합니다.
- 조건부 기댓값(E[X | X ≥ 경계값])은 실패 예측에 중요합니다.
- 11%의 급증 확률은 집계 그래프에서 안정적인 429 오류를 설명합니다.
— Editorial Team
아직 댓글이 없습니다.