Modélisation des erreurs 429 dans un limiteur de RPS distribué avec flux de Poisson
Avec un trafic moyen de 129 requêtes réussies par seconde et 7 erreurs 429 pour une limite de 150 RPS, la question se pose : pourquoi le limiteur se déclenche-t-il de manière constante ? Un graphique de 40 minutes montre un niveau constant d'erreurs sur des intervalles de 15 secondes. La distribution de Poisson du volume de requêtes explique le phénomène : avec λ = 136,28, la probabilité de dépasser 150 requêtes en 1 seconde est de 11,27 %.
Code pour calculer la probabilité :
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
Cette valeur >1 sur un intervalle de 15 secondes correspond au graphique observé.
Espérance conditionnelle des erreurs
Un simple calcul du nombre attendu de requêtes lors du dépassement de la limite donne 156,4, et les erreurs—seulement 0,6 RPS. La différence avec 7,27 RPS nécessite une clarification.
Code pour l'espérance conditionnelle :
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)
Ajustements du modèle : Nouvelles tentatives et partitionnement
Tenir compte d'une nouvelle tentative par erreur (trafic original 132,64 RPS) augmente les erreurs à 0,72 RPS—toujours insuffisant.
Facteur clé : un service à 6 pods (2 pods par 3 DC). Avec partitionnement, chaque limiteur divise 150/6 = 25 RPS, limite = 26.
Calcul pour un limiteur :
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
Le modèle explique 6,85 RPS d'erreurs contre 7,27 observés. Le limiteur fonctionne sans synchronisation des permissions, avec échange périodique entre voisins et réinitialisation des compteurs.
Caractéristiques d'implémentation d'un limiteur distribué
Options possibles :
- Seau percé : fuite constante de jetons.
- Réinitialisation au début d'une seconde : période fixe, vulnérable aux pics.
- Division dynamique : échange de battements de cœur pour le calcul des parts.
Dans un scénario partitionné sans synchronisation, chaque pod décide indépendamment de la limite, augmentant les échecs globaux linéairement avec le nombre de partitions.
Points clés à retenir
- La distribution de Poisson modélise avec précision le trafic des consommateurs sans bots (un cas limite d'utilisateurs infinis).
- Le partitionnement des limiteurs de RPS sans synchronisation augmente les erreurs proportionnellement aux pods.
- Les nouvelles tentatives des clients exacerbent le problème, nécessitant des ajustements dans les modèles.
- L'espérance conditionnelle (E[X | X ≥ limite]) est cruciale pour prédire les échecs.
- Une probabilité de 11 % d'un pic explique les erreurs 429 stables sur les graphiques agrégés.
— Editorial Team
Aucun commentaire pour le moment.