Unendliche Anfragen an `/presence/update`

Ok, es ist vielleicht kein Bug an sich und vielleicht wurde es so entworfen, aber ich habe einige Fragen dazu, wie die Anwesenheit auf der JS-Seite aktualisiert wird. Die Datei app/assets/javascripts/discourse/app/services/presence.js

Was ich bisher verstanden habe, ist, dass der JS-Client:

  • Die Aktualisierung auf 1 pro Sekunde drosselt, wenn die Warteschlange der Aktualisierungen nicht leer ist
  • Dann eine Anfrage alle 30 Sekunden, wenn keine Änderungen vorgenommen werden

Was mir aufgefallen ist, ist, dass, wenn der Server aus irgendeinem Grund einen Fehler ausgibt, das JS weiterhin jede Sekunde Anfragen sendet. Außerdem werden Fehler in der Konsole erzeugt Uncaught (in promise) Zeile 565.
Sollte es nicht irgendwann nach N Fehlern aufhören, um den Server nicht zu überlasten?

Wie kann man das reproduzieren?

Simulieren Sie einen Fehler in der update-Methode von presence_controller.rb. (In meinem Fall habe ich nur zu Demozwecken den Status 405 gerendert)

  def update

    # Der JS-Client ist darauf ausgelegt, auf eine Anfrage pro Sekunde zu drosseln
    # Wenn keine Änderungen vorgenommen werden, sendet er alle 30 Sekunden eine Anfrage
    RateLimiter.new(nil, "update-presence-#{current_user.id}", 20, 10.seconds).performed!
    render json: { error: "Not authorized" }, status: 405
    return
    ...

Dann gehen Sie auf dem Client zu einem beliebigen Bildschirm mit Anwesenheitsverwaltung. Ich habe den Bildschirm für Themenantworten gewählt und mit dem Antworten begonnen.

Das Ergebnis ist, dass es ewig und jede Sekunde weitergeht.

1 „Gefällt mir“

Ist es wirklich notwendig, die Anwesenheit alle 30 Sekunden neu zu berechnen? Denn es gibt:

  • eine Berechnung, wenn eine enter-Aktion ausgeführt wird
  • eine Berechnung, wenn eine leave-Aktion ausgeführt wird
  • und eine Berechnung, wenn sich die Benutzerpräsenz ändert

Ist die Feature-Anfrage hier, bei einem Fehler “zurückzuschalten”, damit wir bei wiederholten Fehlern bis zu etwa 30 Sekunden zurückschalten würden…

1 „Gefällt mir“

@AhmedLoud ist das das Problem, das Sie über FIX: use `_presentChannels.size` instead of `_presentChannels.length`… · discourse/discourse@589add7 · GitHub behoben haben, oder ist es ein separates Problem?

1 „Gefällt mir“

@david Entschuldigung für das Missverständnis.

Mein erster Beitrag zu diesem Thema hatte keinen Bezug zu diesem: FIX: use `_presentChannels.size` instead of `_presentChannels.length`… · discourse/discourse@589add7 · GitHub

Selbst ohne das geplante Update alle 30 Sekunden, wenn es einen Fehler vom Backend gibt, wird der Algorithmus jede Sekunde erneut versuchen, da die Ereigniswarteschlange nicht leer ist:

In _scheduleNextUpdate

   } else if (this._queuedEvents.length > 0) {
      this._cancelTimer();
      cancel(this._debounceTimer);
      this._debounceTimer = debounce(
        this,
        this._throttledUpdateServer,
        this._presenceDebounceMs // Ich glaube, das sind 1 Sekunde
      );

Tatsächlich werden in der Methode _updateServer → Ereignisse bei einem Fehler wieder in die Warteschlange gestellt:

  } catch (e) {
      // Die fehlgeschlagenen Ereignisse für das nächste Mal wieder in die Warteschlange stellen
      this._queuedEvents.unshift(...queue);

Das war nur etwas, das mir danach aufgefallen ist… Wenn alles gut geht (also keine Fehler), ist es dann wirklich notwendig, den Server alle 30 Sekunden weiter zu aktualisieren?

} else if (
      !this._nextUpdateTimer &&
      this._presentChannels.size > 0 &&
      !isTesting()
    ) {
      this._nextUpdateTimer = discourseLater(
        this,
        this._throttledUpdateServer,
        PRESENCE_INTERVAL_S * 1000 // Das sind 30 Sekunden
      );
    }
1 „Gefällt mir“

Ich glaube nicht, dass es ein Zurücksetzen gibt. Es wird bei jedem Fehler 1 Sekunde lang ohne Unterbrechung wiederholt. Aber vielleicht habe ich etwas übersehen.

1 „Gefällt mir“

Ok, ich glaube, ich verstehe jetzt, warum es sich alle 30 Sekunden erneut „anmelden“ muss.

Es gibt einen Hintergrundjob, der jede Minute ausgelöst wird (PresenceChannelAutoLeave), der abgelaufene Mitglieder aus dem Kanal entfernt.

Es ist auch in presence_channel.rb für die Methode present(user:, client_id:) kommentiert:

    # Mark a user's client as present in this channel. The client_id should be unique per
    # browser tab. This method should be called repeatedly (at least once every DEFAULT_TIMEOUT)
    # while the user is present in the channel.
    def present(user:, client_id:)

Ich frage mich immer noch, was den Fehler angeht, der jede Sekunde ausgelöst wird.

Wir haben bereits 429 ‘rate limited’-Fehler korrekt behandelt. Aber für andere, unerwartete Fehler habe ich eine exponentielle Backoff-Logik hinzugefügt (begrenzt auf 30s). Danke, dass Sie uns darauf aufmerksam gemacht haben @AhmedLoud

3 „Gefällt mir“

Dieses Thema wurde nach 9 Tagen automatisch geschlossen. Neue Antworten sind nicht mehr möglich.