Retour à l'accueil

Erreurs 429 avec limite RPS : Poisson et Sharding

L'article analyse les causes des erreurs 429 constantes dans le limiteur RPS lorsque le trafic moyen est inférieur à la limite. En utilisant la distribution de Poisson, la probabilité de rafales et l'espérance conditionnelle sont calculées. La prise en compte du sharding sur 6 pods et des retentatives client explique les 7 rejets RPS observés.

Pourquoi 7 RPS 429 à limite 150 : Poisson + Sharding
Advertisement 728x90

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é.

Google AdInline article slot

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.

Google AdInline article slot

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.

Google AdInline article slot

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

Advertisement 728x90

Lire ensuite