Projet

Général

Profil

Bug #44730

Erreur SMS OVH : Too much requests. Please retry in 3 seconds.

Ajouté par Thomas Noël il y a plus de 3 ans. Mis à jour il y a plus de 2 ans.

Statut:
Fermé
Priorité:
Normal
Version cible:
-
Début:
02 juillet 2020
Echéance:
% réalisé:

0%

Temps estimé:
Patch proposed:
Oui
Planning:
Non

Description

On a cette nouvelle erreur de temps en temps :

error running send_job job (OVH error: {'status': 429, 'message': 'Too much requests.\nPlease retry in 3 seconds.\n'})

Question : est-ce que le passage en mode "jobs" provoque de nombreux appels simultanés vers OVH ? (envoi d'un seul coup de tous les SMS "jobbés") ?

Si oui, ça pourrait être la cause de ce problème et il faudrait trouver une parade.


Fichiers

0001-ovh-retry-on-too-many-requests-error-44730.patch (3,99 ko) 0001-ovh-retry-on-too-many-requests-error-44730.patch Nicolas Roche (absent jusqu'au 3 avril), 02 septembre 2021 16:14
0001-ovh-retry-on-too-many-requests-error-44730.patch (5,67 ko) 0001-ovh-retry-on-too-many-requests-error-44730.patch Nicolas Roche (absent jusqu'au 3 avril), 03 septembre 2021 10:23
0001-ovh-retry-on-too-many-requests-error-44730.patch (5,87 ko) 0001-ovh-retry-on-too-many-requests-error-44730.patch Nicolas Roche (absent jusqu'au 3 avril), 10 septembre 2021 13:58

Demandes liées

Lié à Passerelle - Development #44815: avoir la possibilité d'ajouter un job en lui demandant de s'exécuter tout de suiteNouveau04 juillet 2020

Actions
Lié à Passerelle - Development #42230: Uniformiser les retours des connecteurs SMSRejeté28 avril 2020

Actions

Révisions associées

Révision a214de3d (diff)
Ajouté par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 2 ans

ovh: retry on too many requests error (#44730)

Historique

#1

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 3 ans

  • Assigné à mis à Nicolas Roche (absent jusqu'au 3 avril)
#2

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 3 ans

Question : est-ce que le passage en mode "jobs" provoque de nombreux appels simultanés vers OVH ?

Oui, les demandes sont différées pour être envoyées à la suites toutes les 5 minutes (sur le SaaS et idem à Toulouse) :

*/5 * * * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants jobs

Nos plus gros consommateurs de SMS sont :
  • Toulouse : 2036 pages de jobs
  • cd59 : 87 pages de jobs
  • Mauguio : 66 pages de jobs

J'ai retrouvé le bug (à priori uniquement) sur Toulouse :
(je n'ai trouvé aucune information relative dans la doc d'OVH)
https://passerelle.eservices.toulouse-metropole.fr/ovh/sms/?page=15#logs

Le 2 juillet à 10:26:32 il y a un semblant de campagne de SMS pour dire aux usagers que la piscine n'est plus confinée :
https://passerelle.eservices.toulouse-metropole.fr/ovh/sms/?page=675#logs
Je pensais donc que ce message d'erreur était lié à cette utilisation exceptionnelle, mais en fait ce n'est pas un envoie si massif que ça : les envois ont duré jusqu'au 2 juillet à 11:17:25, soit 3375 envois en 50 minutes, ou encore 1.125 SMS par seconde.

Si oui, ça pourrait être la cause de ce problème et il faudrait trouver une parade.

Ici on voit que Toulouse et Maugio envoient des SMS en même temps (2 juillet 2020 à 11:16:56):
https://passerelle.eservices.toulouse-metropole.fr/ovh/sms/?page=11#logs
https://passerelle.demarches.mauguio-carnon.com/ovh/sms-via-ovh-eo/?page=6#logs
Résoudre le problème uniquement sur une instance n'est qu'un pis-aller, mais je peux proposer un patch dans ce sens si besoin.

#3

Mis à jour par Benjamin Dauvergne il y a plus de 3 ans

Le souci il me semble c'est d'envoyer 1 par 1 ce qui est en fait un mailing, sachant que l'API supporte une liste de destinataires. Ce serait peut-être plus utile de bosser sur une sorte de dédoublonnage, avec une latence défini par l'appelant. Quand on poste un SMS, on peut définir qu'il reste en queue 5 minutes avant d'être envoyé, si un nouveau message avec le même contenu arrive, on l'ajoute aux destinataires du message existant et on ne crée rien de nouveau.

#4

Mis à jour par Frédéric Péters il y a plus de 3 ans

Ici on voit que Toulouse et Maugio envoient des SMS en même temps (2 juillet 2020 à 11:16:56):

Pour info, Toulouse n'est pas sur le SaaS.

#5

Mis à jour par Thomas Noël il y a plus de 3 ans

Nicolas Roche a écrit :

Oui, les demandes sont différées pour être envoyées à la suites toutes les 5 minutes
(sur le SaaS et idem à Toulouse) :

(...)

Je pensais donc que ce message d'erreur était lié à cette utilisation exceptionnelle,
mais en fait ce n'est pas un envoie si massif que ça : les envois ont duré jusqu'au 2
juillet à 11:17:25, soit 3375 envois en 50 minutes, ou encore 1.125 SMS par seconde.

Justement, 50 minutes = 10 envois en lot (chaque 5 minutes).
Donc il y a eu des salves de centaines d'envois d'un seul coup.

C'est ça qui est gênant avec le principe des jobs : ils provoquent des envois en masse.

(Je n'ai pas encore de solution, on est dans un soucis pas simple, pour les mails on a souvent le même soucis, comment "ralentir", et c'est de la configuration assez touchy dans les MTA)

#6

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 3 ans

si un nouveau message avec le même contenu arrive, on l'ajoute aux destinataires du message existant et on ne crée rien de nouveau.

Oui, en plus d'une latence défini par l'appelant, il faudrait fixer une limite raisonnable au nombre maximum de destinataires d'un message, pour dédoublonner les envois.
(Parce que j'imagine qu'il y a une limite au nombre de destinataires)
https://forum.ovh.com/showthread.php/111455-Envoi-de-SMS-en-masse-par-l-API-POST-ou-fichier

Question : est-ce que je relance les jobs en erreurs (à la main/souris) ?

#9

Mis à jour par Thomas Noël il y a plus de 3 ans

  • Lié à Development #44815: avoir la possibilité d'ajouter un job en lui demandant de s'exécuter tout de suite ajouté
#13

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 3 ans

ça pourrait se faire si l'envoi de SMS sortait une SkipJob dans ce cas...?

yep (c'est ce que j'ai compris)
https://dev.entrouvert.org/issues/21465#note-8

#14

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 3 ans

#15

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 2 ans

A défaut de pouvoir reproduire cette exception, OVH parle de code d'erreur API
https://docs.ovh.com/gb/en/sms/send_sms_messages_via_url_-_http2sms/#step-3-analyse-sent-messages
et donc je n'ai pas présagé qu'elle suivait la RFC.
https://datatracker.ietf.org/doc/html/rfc6585#section-4

Aussi, j'ai l'impression qu'elle n'a pas eu lieu avec la nouvelle API d'OVH et ne l'ai prise en compte que sur celle où elle a été vue.

#16

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 2 ans

J'ai ajouté le traitement de l'exception dans la vue qui permet à l'utilisateur de tester l'envoi d'un SMS,
connector.send_msg() y est appelé de manière synchrone.

#17

Mis à jour par Thomas Noël il y a plus de 2 ans

En plus de tester le code 429, on pourrait vérifier qu'il y a un "Please retry" dans le message (ce qui va en plus "clarifier" la situation dans le code).

Mais surtout, si on cherche à envoyer 50 messages d'un coup, les repousser tous de 3 secondes ne va pas vraiment améliorer l'affaire (ça va re-coincer 3 secondes plus tard). Faire plutôt de l'aloha avec un « after_timestamp=random.randint(4,10) »

#19

Mis à jour par Benjamin Dauvergne il y a plus de 2 ans

Thomas Noël a écrit :

Mais surtout, si on cherche à envoyer 50 messages d'un coup, les repousser tous de 3 secondes ne va pas vraiment améliorer l'affaire (ça va re-coincer 3 secondes plus tard). Faire plutôt de l'aloha avec un « after_timestamp=random.randint(4,10) »

<superpedantman>Aloha ça utilise une distribution de poisson pas uniforme</superpedantman>.

https://git.entrouvert.org/authentic.git/tree/src/authentic2/utils/models.py#n25

#20

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 2 ans

Et pourtant...
https://docs.python.org/3/library/random.html#functions-for-integers
Changed in version 3.2: randrange() is more sophisticated about producing equally distributed values.

Mais c'est vrai qu'en testant j'ai vu beaucoup fois la borne max apparaître.
C'est quoi la bonne pratique, random.uniform(a, b) ?

#21

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 2 ans

Bon j'ai compris, tu me charries

>>> res = [0] * 11
>>> for i in range(0,100000):
...   res[random.randint(4,10)] +=1
... 
>>> res
[0, 0, 0, 0, 14354, 14499, 14506, 14548, 14392, 14308, 14393]

>>> res = [0] * 11
>>> for i in range(0,100):
...   res[random.randint(4,10)] +=1
... 
>>> res
[0, 0, 0, 0, 17, 19, 18, 16, 5, 13, 12]

#22

Mis à jour par Thomas Noël il y a plus de 2 ans

Je ne sais absolument pas de quoi vous parlez et quitte ce ticket sur la pointe de pieds.

#23

Mis à jour par Benjamin Dauvergne il y a plus de 2 ans

Thomas Noël a écrit :

Je ne sais absolument pas de quoi vous parlez et quitte ce ticket sur la pointe de pieds.

Moi non plus je ne comprends plus; il est fort ce Nicolas.

#24

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 2 ans

http://www.enseignement.polytechnique.fr/informatique/profs/Georges.Gonthier/pi97/baccelli/proto.html
ALOHA: Dans ce protocole, un message qui vient de subir une collision doit attendre un temps tiré aléatoirement (et uniformément) sur l'intervalle entier avant une nouvelle tentative de retransmission. Ces variables aléatoires de retard sont supposées mutuellement indépendantes.

Reviens Thomas !

#25

Mis à jour par Thomas Noël il y a plus de 2 ans

Il ne faut pas prendre tout ce qu'on dit au pied de la lettre. On se fout un peu de la répartition, je voulais juste dire qu'il faut éviter de tout envoyer 3 secondes plus tard, et randomiser un peu.

Mais after_timestamp=random.randint(4, 10) ça ne va pas, il faut un timestamp -- et là je vois que c'est ce que j'avais proposé, et donc vraiment il ne faut pas prendre tout ce qu'on dit au pied de la lettre ;)

#26

Mis à jour par Frédéric Péters il y a plus de 2 ans

il faut un timestamp

en fait le nom est resté mais ça peut prendre un nombre qui représentera le nombre de secondes,

    def set_after_timestamp(self, value):
        if isinstance(value, datetime.datetime):
            self.after_timestamp = value
        elif isinstance(value, six.integer_types + (float,)):
            self.after_timestamp = timezone.now() + datetime.timedelta(seconds=value)
#27

Mis à jour par Thomas Noël il y a plus de 2 ans

  • Statut changé de Solution proposée à Solution validée

Frédéric Péters a écrit :

il faut un timestamp

en fait le nom est resté mais ça peut prendre un nombre qui représentera le nombre de secondes,

Même moi il ne faut pas que je prenne ce que dit mon cerveau au pied de la lettre ;)

Go !

#28

Mis à jour par Benjamin Dauvergne il y a plus de 2 ans

Nicolas Roche a écrit :

http://www.enseignement.polytechnique.fr/informatique/profs/Georges.Gonthier/pi97/baccelli/proto.html
ALOHA: Dans ce protocole, un message qui vient de subir une collision doit attendre un temps tiré aléatoirement (et uniformément) sur l'intervalle entier avant une nouvelle tentative de retransmission. Ces variables aléatoires de retard sont supposées mutuellement indépendantes.

Le niveau d'enseignement a bien baissé à polytechnique : https://cs.stackexchange.com/questions/65206/the-throughput-of-the-aloha-protocol-if-the-binomial-distribution-was-used

#29

Mis à jour par Nicolas Roche (absent jusqu'au 3 avril) il y a plus de 2 ans

  • Statut changé de Solution validée à Résolu (à déployer)
commit a214de3d53533eefb7b8e2c710d1019eee238d01 (HEAD -> main)
Author: Nicolas ROCHE <nroche@entrouvert.com>
Date:   Thu Sep 2 16:12:19 2021 +0200

    ovh: retry on too many requests error (#44730)
#30

Mis à jour par Frédéric Péters il y a plus de 2 ans

  • Statut changé de Résolu (à déployer) à Solution déployée

Formats disponibles : Atom PDF