Sidekiq Pro導入による非同期ジョブ処理の安定性強化

# はじめに
Railsで運用しているアプリケーションにおいて、Sidekiqを使った非同期ジョブ処理中に、SidekiqプロセスがSegmentation faultによって異常終了する事象が発生しました。原因の特定と対応は完了したものの、同様のクラッシュが将来的に再発した場合、処理中のジョブが失われてしまうリスクが大きな懸念として残りました。

また、可用性の観点では、Redisに一時的な障害が発生した際にジョブ送信が失敗するケースも以前確認されており、早期復旧や自動リカバリの仕組みが求められていました。

これらの課題を解決するために検討したのが、Sidekiq Proの導入です。
本記事では、Sidekiq Proが提供する**信頼性向上機能**に注目し、OSS版との違いや導入による効果について紹介していきます。

- 想定読者: Sidekiq Pro導入検討中の方、Sidekiq Proの機能を知りたい方

# Sidekiq Proとは？

Sidekiq Pro は、OSS版Sidekiqに商用機能を追加した有償版です。
OSS版とは異なり、信頼性向上・可観測性・バッチ処理・キュー制御などの独自機能が利用できます。

主な特徴としては以下のようなものがあります：
- reliable_push! による Redis障害時のジョブ送信バッファリング
- super_fetch! による ジョブの復旧（プロセスクラッシュ後の再実行）
- バッチ処理（Batches） によるグループ単位のジョブ管理
- ジョブの有効期限設定（Expiring Jobs）
- キューの一時停止／再開 によるメンテナンス制御
- Web UI / API の管理機能拡張

https://sidekiq.org/products/pro.html

# OSS版との違い（Sidekiq Proでできること）

[Sidekiq Pro](https://github.com/sidekiq/sidekiq/wiki#sidekiq-pro)では、OSS版では利用できないさまざまな機能が追加されています。以下は、Sidekiq Proで利用可能な主な機能を一覧にまとめたものです。

|  | 概要 | 主な用途 |
|---|---|---|
| [Batches](https://github.com/sidekiq/sidekiq/wiki/Batches) | 複数ジョブをバッチ単位で管理し、完了後にコールバックを実行できる | 複雑なワークフロー、データ一括処理など |
| [Complex Workflows](https://github.com/sidekiq/sidekiq/wiki/Complex-Job-Workflows-with-Batches) | Batchesの応用で依存関係のある複雑なジョブ構成を制御 | 並列＋順序制御が必要な処理（例：バッチ→個別通知→集計） |
| [Really Complex Workflows](https://github.com/sidekiq/sidekiq/wiki/Really-Complex-Workflows-with-Batches) | ネストされたバッチや段階的バッチの構成方法を解説 | 多段階・多依存ジョブフローの設計 |
| [Reliability](https://github.com/sidekiq/sidekiq/wiki/Reliability) | `super_fetch!` や `reliable_scheduler!` によるジョブの再実行・復旧 | プロセス異常終了時のジョブ損失を防止 |
| [Client-side Reliability](https://github.com/sidekiq/sidekiq/wiki/Pro-Reliability-Client) | `reliable_push!` によりRedisダウン中でもジョブ送信が可能 | 一時的なRedis障害でもジョブを失わない |
| [Metrics](https://github.com/sidekiq/sidekiq/wiki/Pro-Metrics) | StatsD/Datadogへのジョブ実行状況のメトリクス送信 | ジョブのパフォーマンス監視、可視化 |
| [Expiring Jobs](https://github.com/sidekiq/sidekiq/wiki/Pro-Expiring-Jobs) | 一定時間内に実行されないジョブを破棄する機能 | 時間制約付きの処理（例：期限付き通知など） |
| [Web UI Extensions](https://github.com/sidekiq/sidekiq/wiki/Pro-Web-UI) | Web UIでのジョブ検索・キュー操作・詳細表示などが強化される | 運用時のジョブ可視化・手動介入がしやすくなる |
| [API Extensions](https://github.com/sidekiq/sidekiq/wiki/Pro-API) | Web UIだけでなくAPI経由でもジョブ/キュー操作が可能 | 外部からの自動化や管理ツールとの連携が容易に |

今回は「信頼性向上」にフォーカスを当てて、特に強力な以下の3つの機能について紹介します。

| 機能名 | 説明 |
|--------|------------|
| `super_fetch!` / `reliable_scheduler!` | `super_fetch!` は実行中ジョブの再実行を可能にし、`reliable_scheduler!` は定期・遅延ジョブの安全なスケジューリングを保証します。併用することで、実行中とスケジュール中のジョブ両方の信頼性を向上させます。 |
| `reliable_push!` | Redisが一時的にダウンしていても、ジョブをメモリ上に一時保持し、Redis復旧後に送信します。これにより、ジョブ送信時の失敗やデータ損失を防ぎ、クライアント側の信頼性を向上させます。 |
| Web UIジョブ一時停止 | Sidekiq ProのWeb UIから特定のキューを一時停止・再開できます。これにより、メンテナンス時や一時的な停止が必要な場面で、ジョブの投入を制御できます。 |

# 導入手順
[Sidekiq Pro公式サイト](https://sidekiq.org/)から、`年間$995`または`月額$99`のサブスクリプション契約を行うことで、Sidekiq Proを利用できるようになります。

契約が完了すると、Sidekiq ProのGemをダウンロードするための **認証情報（ID と PASSWORD）** がメールで送付されます。この認証情報を `.bundle/config` に設定することで、Gemのインストールが可能になります。

Gemfile に以下を追加

```ruby
# Gemfile

gem 'sidekiq-pro', source: 'https://gems.contribsys.com/' # 追加
```

`.bundle/config` に認証情報を追記
※ 誤ってコミットしないよう適切に管理してください

```ruby
# .bundle/config

---
BUNDLE_PATH: "vendor/bundle"
BUNDLE_GEMS__CONTRIBSYS__COM: "sidekiq_pro_id:sideki_pro_password" # 追加
```

## ライセンスについて
Sidekiq Proの認証情報（ID / PASSWORD）はGemのダウンロード時のみ使用されます。認証が完了すれば、起動時に認証を求められることはありません。また、[公式サイト](https://sidekiq.org/products/pro.html)に明記されている通り、Sidekiq Proの商用ライセンスは組織単位で付与されます。そのため、同一組織内であれば、複数のプロジェクトや開発メンバー間でライセンスを共有して利用可能です。

> Sidekiq is available under the terms of the GNU LGPLv3 license.
In addition to its useful functionality, buying Sidekiq Pro grants your organization a Sidekiq commercial license instead of the GNU LGPL, avoiding any legal issues your lawyers might raise. 

# 検証用Jobについて
今回の検証では、Sidekiq Proの機能を確認するために以下のような簡易的なジョブクラスを使用します。

```ruby
# app/jobs/test_job.rb

class TestJob
  include Sidekiq::Worker

  queue_as :default

  sidekiq_options retry: 3, dead: true

  def perform(kind, duration = 1)
    case kind
    when "ok"
      puts "--------------------------------"
      puts "🔥[TestWorker] OK job (duration: #{duration}s)🔥"
      puts "--------------------------------"
      sleep duration.to_i
    when "long"
      puts "--------------------------------"
      puts "🔥[TestWorker] LONG job start🔥"
      sleep duration.to_i
      puts "🔥[TestWorker] LONG job end🔥"
      puts "--------------------------------"
    else
      puts "--------------------------------"
      puts "🔥[TestWorker] UNKNOWN kind=#{kind}🔥"
      puts "--------------------------------"
    end
  end
end
```

kind の値に応じて処理内容を切り替えることで、ジョブの正常系・長時間処理・異常系を簡単に再現できるようにしています。

# super_fetch! / reliable_scheduler!
通常、SidekiqはジョブをRedisから取り出した時点で削除してしまうため、Sidekiqプロセスがクラッシュするとジョブが失われてしまいます。しかし、`super_fetch!`, `reliable_scheduler!` を有効にすることで、ジョブは「予約状態」として扱われ、Sidekiqが落ちても一定時間後に再キューイングされます。

```ruby
# config/initializers/sidekiq.rb

Sidekiq.configure_server do |config|
  # Workerプロセスがクラッシュしてもジョブが再取得されるようにする
  config.super_fetch!
  config.reliable_scheduler!
end
```

上記の設定を有効にすることで、下記の動画のようにSidekiqプロセスが kill された場合でも、再起動後にジョブが自動で再キューイングされるようになります。これにより、予期せぬプロセスのクラッシュやサーバ再起動といった障害時にも、ジョブを手動で再投入する必要がなくなり、安定した運用が可能になります。

![super.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81341/1c9a4a03-ec55-4ed9-a1ca-95a11b29588a.gif)

- Sidekiqプロセスが異常終了しても、再起動後にジョブが自動的に再キューイングされる

# reliable_push!

Sidekiqでは、ジョブをキューに投入する際（perform_asyncなど）にRedisへの書き込みが発生します。しかし、Redisが一時的にダウンしている場合やネットワークが不安定な状況では、ジョブの送信に失敗し、そのジョブ自体が意図せず失われる可能性があります。こうした問題を解決するために、Sidekiq Proでは `reliable_push!` というクライアントサイドの信頼性向上機能が用意されています。

```ruby
# config/initializers/sidekiq.rb

# Redisが停止した場合に備え、ジョブをメモリ上に最大1000件までバックアップ保持する（超過時は最も古いジョブをFIFO方式で破棄）
# バックアップジョブの上限数は以下で設定値を変更可能(Sidekiq.default_configuration[:backup_limit] = 2_000)
Sidekiq::Client.reliable_push! if Rails.env.production?
```

`reliable_push!`を有効にすることで、ジョブ送信時にRedisへの書き込みに失敗した場合でも、ジョブはローカルメモリに一時保存されるようになります。一時保存の上限はデフォルトで最大1000件までとなっており、これを超えると、FIFO方式で最も古いジョブから順に破棄されます。

Redisが停止している間にメモリ上に一時保存されたジョブは、Redis復旧後に新たなジョブが追加されるタイミングをトリガーとして、再実行されます。

この仕組みにより、一時的な Redis 障害に対してもジョブロスを防ぐことができます。

※ Sidekiqプロセスが再起動されるとローカルメモリはクリアされるため、完全な耐障害性を担保するものではありません。

![reliable_push.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81341/7545e754-ab1a-46b9-b37a-bb2ff24c770e.gif)

- Redis が停止していても、ジョブをキューに追加することができる
- Redis 復旧後、新たなジョブが追加されたタイミングで、メモリ上に保持されていたジョブが再送・再実行される

## Web UIジョブ一時停止
Sidekiq Proでは、標準のWebダッシュボードにいくつかの拡張機能が追加されます。中でも便利なのが、「キューの一時停止」機能です。この機能を使うと、SidekiqのWeb UI上から特定のキューの処理を一時的に停止・再開できるようになります。設定はリアルタイムで反映され、Sidekiqプロセスの再起動などは不要です。

以下の設定を追加することで、Sidekiq Pro用のWebダッシュボード機能が利用できるようになります。

```ruby
# config/routes.rb

require 'sidekiq/pro/web' # 追加

Rails.application.routes.draw do
  mount Sidekiq::Web, at: '/sidekiq'
...
end
```

この「キューの一時停止」機能は、運用中のさまざまなシーンで活用できます。
- 障害発生時に、優先度の低いキューを一時停止し、重要なジョブ（例：通知・決済など）が詰まっているキューにリソースを集中させたいとき
- メンテナンス中に、特定のジョブだけ処理を止めたいとき（例：バッチ系の定期実行ジョブ）

また、メンテナンス作業中には、定期バッチ処理など一部のジョブだけを一時的に停止することで、不要な負荷を回避できます。さらに、キューごとの処理負荷を調整して全体のバランスを整えたい場面でも、この機能が役立ちます。

![スクリーンショット 2025-06-01 14.05.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81341/a2c4536e-8648-4756-b7fb-2f7c80a34c1a.png)

- Web UIから手軽にジョブキューを制御できるため、Sidekiqの運用における柔軟性が大きく向上する

# まとめ
本記事では、Sidekiq Proの信頼性向上機能や運用性強化のポイントについて紹介しました。
特に以下の機能が、実運用における大きな価値を発揮します。
- `super_fetch!` / `reliable_scheduler!` による、ジョブの自動復旧と安全なスケジューリング
- `reliable_push!` による、Redis停止時でも安全にジョブ送信できる仕組み
- Web UIからのキューの一時停止による、障害時やメンテナンス時の柔軟な運用対応

これらの機能により、ジョブの消失リスクを抑えながら、トラブル発生時の影響を最小限に留めることが可能になります。また、ライセンスは組織単位で利用できるため、より安定した本番運用を目指す際に、Sidekiq Proの導入を検討してみてはいかがでしょうか。