遅延ジョブがSidekiqスケジュールのzsetと実行の間で断続的に消失(performなし、retryなし、traceなし)

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 (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にスコア=期限時刻で登録されます。

期限時刻 (UTC) ジョブ 結果
15:33:05 実際 (post 282) 消失 — schedule から消去され、実行されなかった
15:50:10 対照 (post_id 999999 → 静かな早期リターン) 実行 (+1 stat:processed ~8s 後)
16:04:18 実際 (post 283) 実行 (SMTPエラーを記録、以下参照)
16:21:44 実際 ×2 + 対照 すべて実行
17:34:39 実際 ×2 消失 ×2
17:42:39 対照 実行 (+1, 11s 後)
17:55:59 実際 消失

各「消失」ジョブについて収集された根拠:

  • zrange schedule にはペイロードが含まれていない(zrem が実行された)、そして retry、dead、いずれのキュー(queue:low などすべて0)にも存在せず、stat:processed の帰属先にも見当たらない。
  • 期限ウィンドウ全体を300msごとにサンプリングする pg_stat_activity ウォッチャーは、Sidekiq接続からのSQLをゼロ件記録した — executeが最初に行う SELECT ... FROM posts でさえも。
  • Logster::RedisStore (__LOGSTER__MAP) にはこれらのジョブに関する新しいエントリがなく、一方で実行された稼働(並行して修正した別の、無関係な問題によるSMTP失敗)のエントリは書き込まれている。
  • Sidekiqのプロセスタイトルは [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-01および2026-10-21に確実に配信されました(EmailLog と下流MTAキューで検証済み)。

適用した緩和策

このサイトでは email_time_window_mins = 0 に設定しました。これにより enqueue_in(0) は at キーをスキップし、ジョブを直接キューにプッシュ(at なしでの client_push)します。これにより、スケジュールされたポーラーを完全に回避します。この変更後、期待される EmailLog 行6件がすべて即座に作成され、メールは下流MTAによって受け入れられました。

受け入れたトレードオフ:メーリングリスト通知は、バッチ処理+10分遅延ではなく、即座に送信されるようになりました。

質問

  1. これは2026.10 / Sidekiq 7.3.10の組み合わせ(またはRuby 4.0下での Demon::Sidekiq / スケジュールされたポーラー)における既知の回帰ですか?
  2. 次の発生時に収集してほしい追加の診断情報はありますか(例:DISCOURSE_LOG_SIDEKIQ=1 ジョブ計装の有効化、Sidekiq例外のバックトレース、ポーラースレッドダンプなど)?
「いいね!」 2