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.
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…
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
);
}
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