Closed20

[キャッチアップ] SideKiq + Rails

shingo.sasakishingo.sasaki

スクラップについて

Rails アプリケーションにおいて、非同期ジョブを Sidekiq で運用している場合の全体感の理解を深めるための勉強ノート。

Sidekiq は OSS 版を想定し、エンプラ、プロプランについては扱わない。

shingo.sasakishingo.sasaki

Simple, efficient background processing for Ruby.
Sidekiq uses threads to handle many jobs at the same time in the same process. It does not require Rails but will integrate tightly with Rails to make background processing dead simple.

  • Sidekiq は Ruby のための非同期ジョブを効率的に実行するためのシステム
  • 同一プロセス内でのマルチスレッドによって同時に複数ジョブを効率的に実行できる
  • Rails に依存はしないが、Rails とのインテグレーションも容易
shingo.sasakishingo.sasaki

Requirements
Redis: 6.2+
Ruby: MRI 2.7+ or JRuby 9.3+.
Sidekiq 7.0 supports Rails 6.0+ but does not require it.

Redis が必須というのがポイント。Redis を用いたキューイングが Sidekiq の主要構成みたい。

shingo.sasakishingo.sasaki

Sidekiq には Web UI がデフォルトで付いてくる。ここからプロセスやキューの管理が出来る模様。

shingo.sasakishingo.sasaki

Sidekiq makes every effort to make usage with modern Rails applications as simple as possible. Please see the ActiveJob page if you want to use Sidekiq with ActiveJob.

Rails 必須ではないとはいえ、基本的には Rails (ActiveJob) と組み合わせて使うことを想定してそう。
なので Wiki 上でもActiveJob を使ったサンプルコードが続く。

shingo.sasakishingo.sasaki
  • Active Job はバックグラウンドで実行するキューイングするためのフレームワーク
  • ジョブ実行のためのインフラ配置が目的で、実際にジョブを実行する様々なツールの差異を吸収する
    • Sidekiq
    • DelayedJob
    • Resque
shingo.sasakishingo.sasaki

ジョブは ApplicationJob クラスを継承したクラスで実装される。

class GuestsCleanupJob < ApplicationJob
  queue_as :default

  def perform(*guests)
    # 後で実行するタスクをここに置く
  end
end

ジョブには perform メソッドを実装し、ここで実際のジョブを実装する

shingo.sasakishingo.sasaki

ジョブは perform_later(クラスメソッド) でキューイングできる

GuestsCleanupJob.perform_later guest
shingo.sasakishingo.sasaki

ジョブの実行タイミングを相対時刻または絶対時刻で遅延可能

GuestsCleanupJob.set(wait_until: Date.tomorrow.noon)
GuestsCleanupJob.set(wait: 1.week)
shingo.sasakishingo.sasaki

production環境でのジョブのキュー登録と実行では、キューイングのバックエンドを用意しておく必要があります。具体的には、Railsで使うべきサードパーティのキューイングライブラリを決める必要があります。

ので、何らかのキューイングライブラリは必要。本スクラップではそれが Sidekiq にあたる。

shingo.sasakishingo.sasaki

一旦 Sidekiq の wiki に戻る。
ActiveJob で Sidekiq を使用する場合は、queue_adapter を設定する。

class Application < Rails::Application
  # ...
  config.active_job.queue_adapter = :sidekiq
end
shingo.sasakishingo.sasaki

ここでこのような言説を見てしまった。

https://techracho.bpsinc.jp/hachi8833/2023_03_06/127675

どうやら、Sidekiq 固有の機能を活用する上では ActiveJob を経由すると不都合が多いらしい。
リトライ機構を Active Job と Sidekiq それぞれ実装していてそのあたりのデバッグも困難になるとか。

実際今関わってるプロダクトでも ActiveJob は使用してないことに気づいた。
ので、Active Job についてはここですべて忘れて、以降は Sidekiq 単体で利用することを前提に調べていく。

shingo.sasakishingo.sasaki

Sidekiq の基礎知識に戻る

https://github.com/sidekiq/sidekiq/wiki/The-Basics

Sidekiq を構成する3要素

  • Client
    • ジョブを生成するクライアント
    • ジョブは引数を含めてシリアライズした文字列を Redis にキューするため、複雑なオブジェクトは避けること
  • Redis
    • ジョブをキューするストレージ
    • 実行済みの過去データについても、Web UI で閲覧するために蓄積される
  • Server
    • Redis からジョブをデキューして実行するサーバー
    • 通常はクライアントと同じランタイムを使用する (クライアントが Rails ならサーバーの Rails を使えるランタイムであるべき)
shingo.sasakishingo.sasaki

ベストプラクティス

https://github.com/sidekiq/sidekiq/wiki/Best-Practices

ジョブのパラメータは小さくシンプルに

前述の通り、パラメータはシリアライズして小さく収まるようにしようね

ジョブは冪等性を持ち、トランザクショナルであるべき

ジョブは実行中にエラーが発生した場合に再実行される仕組みであるため、どこまで実行されるかが保証されない。そのため、何度同じジョブを実行しても結果が安定するようなジョブが望ましい。

また、途中で失敗しても良いように、トランザクション化してエラー時に適切にロールバックできるようにしよう。

並列処理に備える

Sidekiq は基本的に並列実行されるので注意しよう。例えば DB のコネクションプールを Sidekiq だけで枯渇してしまわないように気をつけよう。

用語を正しく使おう

ワーカー (worker) という単語は Sidekiq 上では曖昧なので、ジョブ、スレッド、プロセス、ジョブクラスという単語を使い分けよう。

プロセスが複数のスレッドを持ち、スレッドでジョブを実行する。ジョブはジョブクラスを実装したモジュール。

shingo.sasakishingo.sasaki

ジョブのライフサイクル

https://github.com/sidekiq/sidekiq/wiki/Job-Lifecycle

  • Scheduled Queue
    • 実行タイミングを指定され、将来的にキューに積まれる予定のジョブ
  • Enqueued Gauge
    • キューに積まれている実行待ちのジョブで、サーバーはここからジョブを取り出して実行する
  • Retries Queue
    • 実行に失敗し、自動でリトライ待ちになっているジョブ、サーバーはここからも取り出して再実行する
  • Dead Queue
    • リトライでも失敗したジョブの墓場だが、保持され続けるので手動で再実行することはできる
  • Busy Gauge
    • 現在実行のジョブ
  • Processed(Counter)
    • 正常終了したジョブ(の総数)

すべてのジョブは最終的には Processed か Dead のどちらかに行き着く

shingo.sasakishingo.sasaki

エラーハンドリング

https://github.com/sidekiq/sidekiq/wiki/Error-Handling

ベストプラクティス

  1. Sentry みたいなエラーレポートサービスを導入してエラーを検知しよう
  2. 例外を raise してSidekiqにリトライを促そう
  3. バグによって Dead したキューは、バグを修正したら手動で再実行しよう
  4. 6ヶ月でジョブは破棄されるので、その前に対応しよう
このスクラップは2023/11/11にクローズされました