Solicitudes infinitas a /presence/update

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.

El resultado es que continuará para siempre y cada segundo.

1 me gusta

Además, ¿es realmente necesario recalcular la presencia cada 30 segundos? Porque hay:

  • un cálculo cuando se realiza una acción de enter
  • un cálculo cuando se realiza una acción de leave
  • y un cálculo cuando cambia la presencia del usuario

¿La solicitud de funcionalidad es “retroceder” ante un error, de esa manera retrocederíamos hasta unos 30 segundos ante errores repetidos…

1 me gusta

¿Es este el problema que solucionaste, @AhmedLoud, a través de FIX: use `_presentChannels.size` instead of `_presentChannels.length`… · discourse/discourse@589add7 · GitHub, o es un problema aparte?

1 me gusta

@david lo siento por el malentendido.

Mi primera publicación en el tema no estaba relacionada con esto: FIX: use `_presentChannels.size` instead of `_presentChannels.length`… · discourse/discourse@589add7 · GitHub

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
      );
    }
1 me gusta

No creo que haya un retroceso. Sigue reintentando cada segundo sin parar cuando hay algún error. Pero tal vez me perdí algo.

1 me gusta

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

3 Me gusta

Este tema se cerró automáticamente después de 9 días. Ya no se permiten nuevas respuestas.