Discourse 2026.10.0-latest.1 / Sidekiq 7.3.10 / Ruby 4.0.9
概述
自2026年10月26日将自建论坛从 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)以来,通过 Jobs.enqueue_in 入队的任务偶尔会静默丢失:任务在到期时从 schedule zset 中消失,但 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(2026-10-23)、Rails 8.1.4、Sidekiq 7.3.10
UNICORN_SIDEKIQS=1(sidekiq 由Demon::Sidekiq监督,由 pitchfork fork)- Sidekiq 状态保存在专用的 Redis 数据库中(
Discourse.sidekiq_redis_config),与 Discourse redis 分离 - 在所有观察期间 Sidekiq 进程健康:0 次重启,
[0 of 5 busy],队列空,retry = 0,dead = 0,stat:failed = 0 - 无 OOM,无 dmesg 事件,无容器重启
观察(2026年10月30日,真实案例 + 受控实验)
受影响的任务是 Jobs::NotifyMailingListSubscribers,通过 Jobs.enqueue_in(SiteSetting.email_time_window_mins.minutes, ...) 入队(600秒)→ schedule zset,score = 到期时间。
| 到期时间 (UTC) | 任务 | 结果 |
|---|---|---|
| 15:33:05 | 真实 (帖子 282) | 丢失 — 从 schedule 消失,从未执行 |
| 15:50:10 | 控制 (post_id 999999 → 静默提前返回) | 执行(在到期后 ~8 秒增加 1 个 stat:processed) |
| 16:04:18 | 真实 (帖子 283) | 执行(SMTP 错误已写入,见下文) |
| 16:21:44 | 真实 ×2 + 控制 | 全部执行 |
| 17:34:39 | 真实 ×2 | 丢失 ×2 |
| 17:42:39 | 控制 | 执行(+1,在到期后 11 秒) |
| 17:55:59 | 真实 | 丢失 |
对于每个“丢失”的任务收集的证据:
zrange schedule不再包含该载荷(zrem已发生),且它不在retry、dead或任何队列中(queue:low等均为 0),也不在stat:processed归属中。pg_stat_activity监视器在到期窗口期间每 300 ms 采样一次,记录到 Sidekiq 连接的零条 SQL——甚至没有execute首先执行的初始SELECT ... FROM posts。Logster::RedisStore(__LOGSTER__MAP)中没有这些任务的新条目,但已执行的运行确实写入了条目(SMTP 失败来自我们并行修复的另一个无关问题)。- Sidekiq proctitle 保持
[0 of 5 busy];没有 sidekiq 重启(自启动以来 PID 相同),无 OOM。 - 对于丢失的任务,
SkippedEmailLog计数未变(已执行且失败的运行确实会在其中写入行)。
“已执行”的运行得到正面确认:它们生成了每用户 SMTP 错误条目(捕获在 SkippedEmailLog.custom_reason 中),因此任务类和载荷形状在丢失和执行的实例之间是相同的(类相同,队列 low 相同,仅 post_id 不同)。
先前的安装(Discourse 2026.2.0-latest,Ruby 3.3.8)未表现出此行为:相同的延迟邮件列表任务在 2026年10月1日和 2026年10月21日可靠投递(通过 EmailLog + 下游 MTA 队列验证)。
已应用的缓解措施
我们将该站点的 email_time_window_mins = 0,以便 enqueue_in(0) 跳过 at 键并直接将任务推送到其队列(不带 at 的 client_push),从而完全绕过调度轮询器。在此更改后,所有 6 个预期的 EmailLog 行均立即创建,并且邮件被下游 MTA 接受。
接受的权衡:邮件列表通知现在立即发送,而不是批处理 +10 分钟。
问题
- 这是 2026.10 / Sidekiq 7.3.10 组合(或 Ruby 4.0 下的
Demon::Sidekiq/ 调度轮询器)中已知的回归吗? - 您希望我们在下一次发生时捕获哪些额外的诊断信息(例如启用
DISCOURSE_LOG_SIDEKIQ=1任务插桩、Sidekiq 异常回溯、轮询器线程转储)?