Discourse 2026.10.0-latest.1 / Sidekiq 7.3.10 / Ruby 4.0.9
Resumen
Desde que actualizamos un foro autoalojado de Discourse 2026.2.0-latest (Ruby 3.3.8) a 2026.10.0-latest.1 (Ruby 4.0.9, Rails 8.1.4, Sidekiq 7.3.10) el 2026-10-26, los trabajos encolados con Jobs.enqueue_in se pierden de forma intermitente y silenciosa: el trabajo desaparece del zset schedule en su hora de vencimiento, pero perform nunca se ejecuta. No hay reintentos, no hay trabajos muertos y no hay excepciones registradas (usamos Logster::RedisStore, por lo que las excepciones capturadas serían visibles — no hay ninguna), y stat:failed permanece en 0. Tasa de pérdida observada en 9 ocurrencias controladas/reales: 4 perdidos / 5 ejecutados (~40 %).
Entorno
- Instalación oficial con Docker (
./launcher rebuild), sitio único - Discourse
2026.10.0-latest.1, imagendiscourse/base:2.0.20261026-0138 - Ruby 4.0.9 (2026-10-23), Rails 8.1.4, Sidekiq 7.3.10
UNICORN_SIDEKIQS=1(sidekiq supervisado porDemon::Sidekiq, bifurcado por pitchfork)- El estado de Sidekiq está en una base de datos Redis dedicada (
Discourse.sidekiq_redis_config), separada del redis de Discourse - El proceso de Sidekiq está saludable durante todas las observaciones: 0 reinicios,
[0 of 5 busy], colas vacías,retry = 0,dead = 0,stat:failed = 0 - Sin OOM, sin eventos en dmesg, sin reinicios de contenedores
Observaciones (2026-10-30, caso real + experimentos controlados)
El trabajo afectado es Jobs::NotifyMailingListSubscribers, encolado con Jobs.enqueue_in(SiteSetting.email_time_window_mins.minutes, ...) (600 s) → zset schedule con score = hora de vencimiento.
| Hora de vencimiento (UTC) | Trabajo | Resultado |
|---|---|---|
| 15:33:05 | real (post 282) | perdido — desaparece de schedule, nunca ejecutado |
| 15:50:10 | control (post_id 999999 → return temprano silencioso) | ejecutado (+1 stat:processed ~8 s después del vencimiento) |
| 16:04:18 | real (post 283) | ejecutado (se escribieron errores SMTP, ver abajo) |
| 16:21:44 | real ×2 + control | todos ejecutados |
| 17:34:39 | real ×2 | perdidos ×2 |
| 17:42:39 | control | ejecutado (+1, 11 s después del vencimiento) |
| 17:55:59 | real | perdido |
Evidencia recopilada para cada trabajo “perdido”:
zrange scheduleya no contiene el payload (se ha producido elzrem), y no está presente ni enretry, ni endead, ni en ninguna cola (queue:low, etc., todas en 0), ni en la atribución destat:processed.- Un monitor
pg_stat_activityque muestreaba cada 300 ms durante la ventana de vencimiento no registró ninguna consulta SQL desde la conexión de Sidekiq — ni siquiera elSELECT ... FROM postsinicial queexecuterealiza primero. Logster::RedisStore(__LOGSTER__MAP) no contiene nuevas entradas para estos trabajos, mientras que las entradas SÍ se escriben para las ejecuciones ejecutadas (fallos SMTP de un problema diferente e no relacionado que arreglamos en paralelo).- El proctitle de Sidekiq permanece en
[0 of 5 busy]; no hay reinicio de sidekiq (mismo PID desde el arranque), no hay OOM. - El recuento de
SkippedEmailLogno cambia para los trabajos perdidos (las ejecuciones fallidas sí escriben filas allí).
Las ejecuciones “ejecutadas” están positivamente confirmadas: produjeron entradas de error SMTP por usuario (capturadas en SkippedEmailLog.custom_reason), por lo que la clase del trabajo y la forma del payload son idénticas entre las ocurrencias perdidas y las ejecutadas (misma clase, misma cola low, solo difiere el post_id).
La instalación anterior (Discourse 2026.2.0-latest, Ruby 3.3.8) no mostraba este comportamiento: los mismos trabajos diferidos de lista de correo se entregaron de forma fiable el 2026-10-01 y el 2026-10-21 (verificado mediante EmailLog + colas del MTA descendente).
Mitigación aplicada
Establecimos email_time_window_mins = 0 para este sitio, de modo que enqueue_in(0) omite la clave at y envía el trabajo directamente a su cola (client_push sin at), evitando por completo el sondeo programado. Tras este cambio, las 6 filas EmailLog esperadas se crearon inmediatamente y los correos fueron aceptados por el MTA descendente.
Compensación aceptada: las notificaciones de lista de correo ahora se envían inmediatamente en lugar de agruparse +10 min.
Preguntas
- ¿Es esta una regresión conocida en la combinación 2026.10 / Sidekiq 7.3.10 (o en
Demon::Sidekiq/ el sondeo programado bajo Ruby 4.0)? - ¿Qué diagnósticos adicionales quisieran que capturemos en la próxima ocurrencia (por ejemplo, habilitar la instrumentación de trabajos
DISCOURSE_LOG_SIDEKIQ=1, backtraces de excepciones de Sidekiq, volcados de hebras del sondeo)?