Richieste infinite a `/presence/update`

Ok, potrebbe non essere un bug in sé e forse è stato progettato così, ma ho alcune domande su come viene aggiornata la presenza sul lato JS. Il file app/assets/javascripts/discourse/app/services/presence.js

Quello che ho capito finora è che il client JS:

  • Limiterà l’aggiornamento a 1 al secondo quando la coda di aggiornamenti non è vuota
  • Quindi una richiesta ogni 30 secondi quando non ci sono modifiche

Quello che ho notato è che se il server genera un errore per qualsiasi motivo, il JS continuerà a effettuare richieste ogni secondo. Inoltre, crea errori nella console Uncaught (in promise) alla riga 565.
Non dovrebbe fermarsi a un certo punto dopo N errori per non sovraccaricare il server?

Come riprodurre?

Simula un errore nel metodo update di presence_controller.rb. (Nel mio caso, ho reso lo stato 405 solo per scopi dimostrativi)

  def update

    # Il client JS è progettato per limitare a una richiesta al secondo
    # Quando non vengono apportate modifiche, effettua una richiesta ogni 30 secondi
    RateLimiter.new(nil, "update-presence-#{current_user.id}", 20, 10.seconds).performed!
    render json: { error: "Not authorized" }, status: 405
    return
    ...

Quindi, sul client, vai a qualsiasi schermata con gestione della presenza. Ho scelto la schermata di risposta all’argomento e ho iniziato a rispondere.

Il risultato è che continuerà all’infinito e ogni secondo.

1 Mi Piace

Inoltre, è davvero necessario ricalcolare la presenza ogni 30 secondi? Perché c’è:

  • un calcolo quando viene eseguita un’azione enter
  • un calcolo quando viene eseguita un’azione leave
  • e un calcolo quando cambia la presenza dell’utente

La richiesta di funzionalità è di fare un “passo indietro” in caso di errore, in modo da fare un passo indietro fino a circa 30 secondi in caso di errori ripetuti…

1 Mi Piace

@AhmedLoud è questo il problema che hai risolto tramite FIX: use `_presentChannels.size` instead of `_presentChannels.length`… · discourse/discourse@589add7 · GitHub, o è un problema separato?

1 Mi Piace

@david mi dispiace per l’incomprensione.

Il mio primo post sull’argomento non era correlato a questo: FIX: use `_presentChannels.size` instead of `_presentChannels.length`… · discourse/discourse@589add7 · GitHub

Anche senza l’aggiornamento programmato ogni 30 secondi, se c’è un errore dal backend, l’algoritmo riproverà ogni secondo perché la coda degli eventi non sarà vuota:

In _scheduleNextUpdate

   } else if (this._queuedEvents.length > 0) {
      this._cancelTimer();
      cancel(this._debounceTimer);
      this._debounceTimer = debounce(
        this,
        this._throttledUpdateServer,
        this._presenceDebounceMs // Credo che questo sia 1 secondo
      );

Infatti, nel metodo _updateServer → gli eventi vengono rimessi in coda in caso di errore:

  } catch (e) {
      // Rimetti gli eventi falliti in coda per la prossima volta
      this._queuedEvents.unshift(...queue);

Questa era solo una cosa che ho notato in seguito… Quando tutto va bene (cioè nessun errore), è davvero necessario continuare ad aggiornare il server ogni 30 secondi?

} else if (
      !this._nextUpdateTimer &&
      this._presentChannels.size > 0 &&
      !isTesting()
    ) {
      this._nextUpdateTimer = discourseLater(
        this,
        this._throttledUpdateServer,
        PRESENCE_INTERVAL_S * 1000 // Questo sono 30 secondi
      );
    }
1 Mi Piace

Non credo ci sia un back off. Continua a riprovare ogni secondo senza fermarsi in caso di errore. Ma forse mi è sfuggito qualcosa.

1 Mi Piace

Ok, penso di aver capito perché deve fare il “check-in” ogni 30 secondi.

C’è un job in background che viene attivato ogni minuto PresenceChannelAutoLeave che farà uscire dal canale i membri scaduti.

È anche commentato in presence_channel.rb per il metodo present(user:, client_id:):

    # Segna il client di un utente come presente in questo canale. Il client_id dovrebbe essere univoco per\n    # scheda del browser. Questo metodo dovrebbe essere chiamato ripetutamente (almeno una volta ogni DEFAULT_TIMEOUT)\n    # mentre l'utente è presente nel canale.\n    def present(user:, client_id:)\n```

Mi chiedo ancora della parte di errore che viene attivata ogni secondo.

Gestivamo già correttamente gli errori 429 ‘rate limited’. Ma per altri errori imprevisti, ho aggiunto una logica di exponential backoff (limitata a 30 secondi). Grazie per avercelo segnalato @AhmedLoud

3 Mi Piace

Questo argomento è stato chiuso automaticamente dopo 9 giorni. Non sono più consentite nuove risposte.