Ok ce n’est peut-être pas un bug à proprement parler et c’est peut-être conçu ainsi, mais j’ai quelques interrogations sur la façon dont la présence est mise à jour côté JS. Le fichier app/assets/javascripts/discourse/app/services/presence.js
Ce que j’ai compris jusqu’à présent, c’est que le client JS :
Limite la mise à jour à 1 par seconde lorsque la file d’attente des mises à jour n’est pas vide
Ensuite, une requête toutes les 30 secondes lorsqu’il n’y a pas de changements
Ce que j’ai remarqué, c’est que si le serveur génère une erreur pour une raison quelconque, le JS continuera à faire des requêtes chaque seconde. Cela crée également des erreurs dans la console Uncaught (in promise) ligne 565.
Ne devrait-il pas s’arrêter à un moment donné après N erreurs pour ne pas surcharger le serveur ?
Comment reproduire ?
Simuler une erreur dans la méthode update de presence_controller.rb. (Dans mon cas, j’ai rendu le statut 405 juste pour la démo)
def update
# Le client JS est conçu pour limiter à une requête par seconde
# Lorsqu'aucun changement n'est effectué, il fait une requête toutes les 30 secondes
RateLimiter.new(nil, "update-presence-#{current_user.id}", 20, 10.seconds).performed!
render json: { error: "Not authorized" }, status: 405
return
...
Ensuite, côté client, allez sur n’importe quel écran avec une gestion de présence. J’ai choisi l’écran de réponse au sujet et j’ai commencé à répondre.
La demande de fonctionnalité consiste-t-elle à “ralentir” en cas d’erreur, de sorte que nous ralentissions jusqu’à environ 30 secondes en cas d’erreurs répétées…
Même sans la mise à jour programmée toutes les 30 secondes, s’il y a une erreur du backend, l’algorithme essaiera à nouveau chaque seconde car la file d’attente des événements ne sera pas vide :
Dans _scheduleNextUpdate
} else if (this._queuedEvents.length > 0) {
this._cancelTimer();
cancel(this._debounceTimer);
this._debounceTimer = debounce(
this,
this._throttledUpdateServer,
this._presenceDebounceMs // Je crois que c'est 1 seconde
);
En fait, dans la méthode _updateServer → les événements sont remis dans la file d’attente en cas d’erreur :
} catch (e) {
// Remettre les événements échoués dans la file d'attente pour la prochaine fois
this._queuedEvents.unshift(...queue);
C’était juste quelque chose que j’avais remarqué par la suite… Quand tout se passe bien (c’est-à-dire sans erreurs), est-il vraiment nécessaire de continuer à mettre à jour le serveur toutes les 30 secondes ?
Je ne pense pas qu’il y ait de délai d’attente. Il continue de réessayer toutes les 1 seconde sans s’arrêter en cas d’erreur. Mais peut-être que j’ai manqué quelque chose.
Ok, je pense avoir compris pourquoi il doit se « enregistrer » à nouveau toutes les 30 secondes.
Il y a un job d’arrière-plan qui est déclenché toutes les minutes PresenceChannelAutoLeave qui fera quitter le canal aux membres expirés.
C’est aussi commenté dans presence_channel.rb pour la méthode present(user:, client_id:) :
# Marque le client d'un utilisateur comme présent dans ce canal. Le client_id doit être unique par
# onglet de navigateur. Cette méthode doit être appelée de manière répétée (au moins une fois toutes les DEFAULT_TIMEOUT)
# pendant que l'utilisateur est présent dans le canal.
def present(user:, client_id:)
Je me demande toujours quelle est la partie de l’erreur qui est déclenchée chaque seconde.
Nous gérions déjà correctement les erreurs 429 « rate limited ». Mais pour d’autres erreurs inattendues, j’ai ajouté une logique de « backoff exponentiel » (limitée à 30 secondes). Merci d’avoir attiré notre attention sur ce point @AhmedLoud