Хорошо, возможно, это не совсем баг, а так и задумано, но у меня есть вопросы о том, как обновляется статус присутствия на стороне JS. Файл app/assets/javascripts/discourse/app/services/presence.js
Насколько я понял, JS-клиент:
Ограничивает частоту обновлений до одного раза в секунду, пока очередь обновлений не пуста
Затем делает один запрос каждые 30 секунд, если изменений нет
Однако я заметил, что если сервер по какой-то причине возвращает ошибку, JS продолжает отправлять запросы каждую секунду. Кроме того, в консоли появляются ошибки: Uncaught (in promise) на строке 565.
Разве не должно быть остановки после N ошибок, чтобы не перегружать сервер?
Как воспроизвести?
Сымитируйте ошибку в методе update контроллера presence_controller.rb. (В моём случае я просто вернул статус 405 для демонстрации)
def update
# JS-клиент предназначен для ограничения частоты запросов до одного в секунду
# При отсутствии изменений он делает один запрос каждые 30 секунд
RateLimiter.new(nil, "update-presence-#{current_user.id}", 20, 10.seconds).performed!
render json: { error: "Not authorized" }, status: 405
return
...
Затем на клиенте перейдите на любой экран с управлением присутствием. Я выбрал экран ответа в теме и начал писать ответ.
Даже без запланированного обновления каждые 30 секунд, если с бэкенда поступит какая-либо ошибка, алгоритм будет пытаться снова каждую секунду, поскольку очередь событий не будет пустой:
Я не думаю, что есть механизм задержки (back off). При возникновении любой ошибки он продолжает повторять попытку каждую секунду без остановки. Но, возможно, я что-то упустил.
Хм, я думаю, понял, почему нужно снова «регистрироваться» каждые 30 секунд.
Есть фоновая задача, которая запускается каждую минуту — PresenceChannelAutoLeave, — и она заставляет истёкших участников покинуть канал.
Об этом также есть комментарий в файле presence_channel.rb для метода present(user:, client_id:):
# Пометить клиента пользователя как присутствующего в этом канале. client_id должен быть уникальным для каждой
# вкладки браузера. Этот метод должен вызываться повторно (хотя бы один раз каждые DEFAULT_TIMEOUT)
# пока пользователь находится в канале.
def present(user:, client_id:)
Всё ещё неясно, что касается части с ошибкой, которая срабатывает каждую секунду.
Мы уже корректно обрабатывали ошибки 429 «rate limited». Однако для других, неожиданных ошибок я добавил логику экспоненциальной задержки (с ограничением до 30 секунд). Спасибо, что обратили на это наше внимание @AhmedLoud