LoginSignup
47
29

Sidekiqチューニングのためのパラメータ整理

Last updated at Posted at 2020-02-11

これは何?

Sidekiqには同時実行数のパラメータがあるため、それをどのようにセットするべきか決める必要があります。
これは、Ruby on Rails 5, 6, 7でSidekiq v6, 7のパラメータのチューニングに必要な情報を事前調査してまとめたものです。

結論

  • concurrencyを許容するジョブの同時実行数に合わせる
  • config/database.ymlのpoolをconcurrency+1にしておく
  • 最終的には要求されるスループットのジョブを回してパフォーマンスを測定して決める :sweat_smile:

アーキテクチャ概要

キューとスレッドの関係は、Sidekiq v7から導入される Capsules (カプセル)機能によって、やや仕様変更が発生している可能性あり。検証中です。

アーキテクチャ.png

プロセス:スレッド=1:N
プロセス:キュー=1:N
キュー:スレッド=N:N
ワーカー:キュー=1:1
プロセス:コネクション数=1:N
プロセス:構成ファイル=1:1
コンピューター:プロセス=1:N

キューとスレッドの関係は定義できない。Sidekiq単体では、キューとスレッドはそれぞれプロセスとの紐付けの定義のみ。
つまり、1キューでの並列度は指定できない。プロセス全体の並列度(concurrency)のみ指定可能。
→参考:Sidekiq の Server プロセス内でバックグラウンド処理をするケースの手法を見てみた
紹介されているSidekiq-limit_fetch gemがSidekiq v6.1.0に追従できていない、かつメンテも活発でないので代替案を探したほうが良いと思われます。

concurrency

ps出力結果
502 13124   775   0  7:10PM ttys008    0:02.79 sidekiq 5.2.1 application-name [0 of 10 busy]

Sidekiqで言うところのスレッド

Sidekiqではジョブ(Woker)ごとに1スレッドを使用しているっぽい。
ソースコードレベルでは未確認だが、ジョブ起動する度にps結果のbusyの数が増えていったため。
実際の並列処理はCPUコア数に依存。

Connection Pool

Sidekiqで使うコネクションプールはActiveRecordのものと無関係。

ActiveRecord

connection_pool gem

Sidekiq v7から導入される Capsules (カプセル)機能によって、やや仕様変更が発生している可能性あり。検証中です。

  • このgemはSidekiqがRedisとのコネクションに使用。
  • 自分で制御したい場合は、config/initilizers/sidekiq.rbこう書く
  • コンストラクタに指定するsizeはプールするコネクション数。
    • ソースコード
    • connection_pool gem v2.4.1におけるデフォルトは5。
    • Sidekiqにおけるデフォルトは、serverならconcurrency + 5。clientならENV['RAILS_MAX_THREADS']
  • Sidekiq内で以下のように使用される(ソースコード
sidekiq/lib/sidekiq/redis_connection.rb
module Sidekiq
  class RedisConnection
    class << self
      def create(options = {})
        # ...
        ConnectionPool.new(timeout: pool_timeout, size: size) do
          build_client(symbolized_options)
        end
      end
    end
  end
end

拡張性

拡張可能な変数としては以下のとおり。

  • スレッド(concurrency)
    • 1プロセスで50スレッド以下にした方がよいとの公式見解のため、これを超えるならばプロセスを増やす(スケールアウト)する
    • もちろん、CPUコア数、CPUスレッド数を気にして実効性のある数を決める
    • 起点としては、APMツールなどでI/O待ち時間の割合を測定し、アムダールの法則を使って効果が飽和する手前の並列度を見積もるなどの定量的な手法もある
  • ワーカー
    • 非機能要件からではなく、仕事内容に応じてワーカーを定義していく。1つのワーカーの責務が大きくなったら、ワークフローを再考してワーカーの分割を検討する
  • キュー
    • 待ち行列となる(待つことになる、FIFOになる)ことを考慮して、キューの分割を考慮する。例えば、頻度が少ないが来たらすぐに処理したいワーカーには専用のキューを用意し、その優先度を上げる
    • 平均処理時間の異なる2つのワーカーは別キューに分ける。なぜなら、同じ優先度の異なるキューに入れば、それぞれから1つずつ処理してくれることになり、短時間タスクが長時間タスクによって待たされることが減る。
    • なお、優先キューを作ることはそのワーカーの仕事内容によっては許容されないケースもあるかも知れないので注意。例えば、1日の中では順序非依存だが、日付の前後が入れ替わる処理順では困る場合(意外と簡単に既存仕様を破壊しかねないので気を付けたい)
  • プロセス
    • キューごとの並列度、言い換えればワーカーごとの並列度はSidekiq単体では指定できない。concurrencyを共有するため。よって、別々の並列度を指定したい場合は、別プロセスに分割することも検討する。
    • concurrencyが上限に近づいたらプロセスをスケールアウトする
    • 最近はコンテナ単価も安いので、concurrency頑張るより水平スケーリングさせるのがいいかも
  • Sidekiqコネクションプール数
    • concurrencyよりも多く準備する
    • Redis側の実効性のあるコネクション数も気にしておく

参考

47
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
29