Discourse 2026.10.0-latest.1 / Sidekiq 7.3.10 / Ruby 4.0.9
Резюме
После обновления самостоятельно размещенного форума с Discourse 2026.2.0-latest (Ruby 3.3.8) до версии 2026.10.0-latest.1 (Ruby 4.0.9, Rails 8.1.4, Sidekiq 7.3.10) от 26.10.2026 задания, добавленные в очередь через Jobs.enqueue_in, периодически и без уведомлений теряются: задание исчезает из sorted set schedule в момент наступления срока выполнения, но метод perform никогда не запускается. Повторов нет, в мертвых заданиях оно не появляется, исключения в логах отсутствуют (мы используем Logster::RedisStore, поэтому перехваченные исключения видны — их нет), а счетчик stat:failed остается равным 0. Наблюдаемый уровень потерь по 9 зафиксированным случаям (контролируемым и реальным): 4 потеряно / 5 выполнено (~40%).
Окружение
- Официальная docker-установка (
./launcher rebuild), один сайт - Discourse
2026.10.0-latest.1, образdiscourse/base:2.0.20261026-0138 - Ruby 4.0.9 (от 23.10.2026), Rails 8.1.4, Sidekiq 7.3.10
UNICORN_SIDEKIQS=1(Sidekiq управляетсяDemon::Sidekiq, форкается pitchfork)- Состояние Sidekiq хранится в отдельной базе данных Redis (
Discourse.sidekiq_redis_config), отделенной от основного Redis для Discourse - Процесс Sidekiq был здоров во всех наблюдениях: 0 перезапусков,
[0 of 5 busy], очереди пусты,retry = 0,dead = 0,stat:failed = 0 - Нет событий OOM, событий в dmesg, перезапусков контейнера
Наблюдения (30.10.2026, реальный случай + контролируемые эксперименты)
Затронутое задание — Jobs::NotifyMailingListSubscribers, добавленное в очередь через Jobs.enqueue_in(SiteSetting.email_time_window_mins.minutes, ...) (600 с) → в sorted set schedule со score = время исполнения.
| Время исполнения (UTC) | Задание | Исход |
|---|---|---|
| 15:33:05 | реальное (пост 282) | потеряно — исчезло из schedule, никогда не выполнено |
| 15:50:10 | контрольное (post_id 999999 → тихий ранний возврат) | выполнено (+1 stat:processed примерно через 8 с после срока) |
| 16:04:18 | реальное (пост 283) | выполнено (ошибки SMTP записаны, см. ниже) |
| 16:21:44 | реальные ×2 + контрольное | все выполнены |
| 17:34:39 | реальные ×2 | потеряно ×2 |
| 17:42:39 | контрольное | выполнено (+1, через 11 с после срока) |
| 17:55:59 | реальное | потеряно |
Данные, собранные для каждого «потерянного» задания:
zrange scheduleбольше не содержит payload (произошелzrem), и его нет ни вretry, ни вdead, ни в какой-либо очереди (queue:lowи др. пусты), ни в атрибуцииstat:processed.- Наблюдатель
pg_stat_activity, сэмплирующий каждые 300 мс в окне исполнения, не зафиксировал ни одного SQL-запроса от соединения Sidekiq — даже начальногоSELECT ... FROM posts, который выполняетexecute. - В
Logster::RedisStore(__LOGSTER__MAP) нет новых записей для этих заданий, тогда как для выполненных прогонов записи ПОСТУПАЮТ (ошибки SMTP, связанные с другой, нерелевантной проблемой, которую мы чинили параллельно). - Proctitle Sidekiq остается
[0 of 5 busy]; нет перезапусков sidekiq (тот же PID с момента загрузки), нет OOM. - Количество записей в
SkippedEmailLogдля потерянных заданий не изменилось (для выполненных и ошибочных прогонов строки туда записываются).
Выполненные прогоны положительно подтверждены: они породили записи об ошибках SMTP на уровне пользователя (сохранены в SkippedEmailLog.custom_reason), поэтому класс задания и структура payload идентичны между потерянными и выполненными случаями (тот же класс, та же очередь low, отличается только post_id).
Предыдущая установка (Discourse 2026.2.0-latest, Ruby 3.3.8) не демонстрировала такого поведения: те же отложенные задания рассылки успешно доставлялись 01.10.2026 и 21.10.2026 (подтверждено через EmailLog и очереди нижестоящего MTA).
Примененное обходное решение
Мы установили email_time_window_mins = 0 для этого сайта, чтобы enqueue_in(0) пропускал ключ at и напрямую помещал задание в его очередь (client_push без at), полностью обходя поллер запланированных заданий. После этого изменения все 6 ожидаемых строк EmailLog были созданы немедленно, и письма были приняты нижестоящим MTA.
Принятый компромисс: уведомления списка рассылки теперь отправляются немедленно, вместо пакетной отправки с задержкой +10 мин.
Вопросы
- Является ли это известной регрессией в комбинации 2026.10 / Sidekiq 7.3.10 (или в
Demon::Sidekiq/ поллере запланированных заданий под Ruby 4.0)? - Какие дополнительные диагностические данные вы хотели бы, чтобы мы собрали при следующем возникновении (например, включение инструментирования заданий через
DISCOURSE_LOG_SIDEKIQ=1, бэктрейсы исключений Sidekiq, дампы потоков поллера)?