Ok, puede que no sea un bug como tal y quizás fue diseñado así, pero tengo algunas preguntas sobre cómo se actualiza la presencia en el lado de JavaScript. El archivo app/assets/javascripts/discourse/app/services/presence.js
Lo que he entendido hasta ahora es que el cliente JS:
Limitará la actualización a 1 por segundo cuando la cola de actualizaciones no esté vacía.
Luego, una solicitud cada 30 segundos cuando no haya cambios.
Lo que he notado es que si el servidor falla por cualquier motivo, el JS seguirá haciendo solicitudes cada segundo. También se crean errores en la consola Uncaught (in promise) en la línea 565.
¿No debería detenerse en algún momento después de N errores para no sobrecargar el servidor?
¿Cómo reproducirlo?
Simula un error en el método update de presence_controller.rb. (En mi caso, rendericé el estado 405 solo para la demostración)
def update
# El cliente JS está diseñado para limitar a una solicitud por segundo
# Cuando no se están realizando cambios, realiza una solicitud cada 30 segundos
RateLimiter.new(nil, "update-presence-#{current_user.id}", 20, 10.seconds).performed!
render json: { error: "Not authorized" }, status: 405
return
...
Luego, en el cliente, ve a cualquier pantalla con gestión de presencia. Elegí la pantalla de respuesta de temas y comencé a responder.
Incluso sin la actualización programada cada 30 segundos, si hay algún error del backend, el algoritmo intentará de nuevo cada segundo porque la cola de eventos no estará vacía:
En _scheduleNextUpdate
} else if (this._queuedEvents.length > 0) {
this._cancelTimer();
cancel(this._debounceTimer);
this._debounceTimer = debounce(
this,
this._throttledUpdateServer,
this._presenceDebounceMs // Creo que esto es 1 segundo
);
De hecho, en el método _updateServer, los eventos se vuelven a poner en la cola en caso de error:
} catch (e) {
// Volver a poner los eventos fallidos en la cola para la próxima vez
this._queuedEvents.unshift(...queue);
Esto fue solo algo que noté después… Cuando todo va bien (es decir, no hay errores), ¿es realmente necesario seguir actualizando el servidor cada 30 segundos?
} else if (
!this._nextUpdateTimer &&
this._presentChannels.size > 0 &&
!isTesting()
) {
this._nextUpdateTimer = discourseLater(
this,
this._throttledUpdateServer,
PRESENCE_INTERVAL_S * 1000 // Esto son 30 segundos
);
}
Ok, creo que entendí por qué necesita hacer “check-in” de nuevo cada 30 segundos.
Hay un trabajo en segundo plano que se activa cada minuto PresenceChannelAutoLeave que hará que los miembros expirados abandonen el canal.
También está comentado en presence_channel.rb para el método present(user:, client_id:):
# Marca el cliente de un usuario como presente en este canal. El client_id debe ser único por
# pestaña del navegador. Este método debe llamarse repetidamente (al menos una vez cada DEFAULT_TIMEOUT)
# mientras el usuario esté presente en el canal.
def present(user:, client_id:)
Aún me pregunto sobre la parte del error que se activa cada segundo.
Ya estábamos manejando correctamente los errores 429 ‘rate limited’. Pero para otros errores inesperados, he añadido algo de lógica de retroceso exponencial (limitada a 30 segundos). Gracias por llamar nuestra atención sobre esto @AhmedLoud