1. Qiita
  2. 投稿
  3. sidekiq

Sidekiqで非業の死を遂げたキューを知る方法

  • 76
    いいね
  • 0
    コメント
に投稿
この記事は最終更新日から1年以上が経過しています。

前置き:Rubyのキューイングシステム

Rails(Ruby)で非同期にキューイングしてくれるライブラリといえばSidekiqですよねっていうくらいSidekiqが好きなんですが、世の中のシェア的にはResqueなのかな。でも、Sidekiqの方が安定的に動かせているので僕は好きです(delayed_jobは知らない)。

ShoryukenというAWS SQSを使ったキューイングシステムもあり、RedisじゃなくてSQS使いたいっていう場合は、これも良いのかもしれないですね。まぁ、ローカルの場合面倒そうな感じもありますけど。

まぁともかくSidekiqを導入してキューイングするっていうのは、もう超絶簡単にできるわけです。できるんですけど、実際にラフに運用していくと、キューがいつの間にかこけて処理待ちで埋まってたり、レアなケースで例外投げて死んでたりするわけです。そんなSidekiqと仲良く暮らしていくために、死にゆくキューを知る方法をまとめてみました。

Sidekiq Proという存在

Sidekiqにはエンタープライズ向けなPro版があります。年間$950。それなりにしますね。このProと通常版、どう違うのかというと…

  • Reliability: プロセスが死んだりRedisが死んでも、キューを復元できる
  • Batches: 複数のジョブをグループ化して進行管理できる
  • Support: サポート!!!

ここらへんは、Sidekiq Proを1年ほど使ってみて良かったところ、困ったところという@sumyappさんのスライドが非常に参考になります。

失敗したキューを知る

普通にコードがミスってたり、データがおかしかったりして例外投げて死んでいったキューを知りたいというケースであれば、Pro版を使わなくともなんとかなります。

以降のサンプルスクリプトはRailsのActiveJob経由での話になります。が、まぁ適当に置き換えてもらえれば素のSidekiqでも問題ないと思います。

まず、Sidekiqを起動します。

$ bundle exec sidekiq -q default

適当なジョブファイルを作成します。そして、実行したら必ず例外を投げて死亡するようにしておきます。

jobs/hoge_job.rb
class HogeJob < ActiveJob::Base
  queue_as :default

  def perform(hoge_id:)
    raise "error!" # 明示的に例外投げる
  end
end

rails consoleからキューを積みます。すると、Sidekiqを起動しているコンソール上では、当然のようにエラーログが出ます。

> HogeJob.perform_later(hoge_id: 100)

=> Enqueued HogeJob (Job ID: e0693a53-9860-4e73-9479-674dd84ccaac) to Sidekiq(default) with arguments: {:hoge_id=>100}

次に、rails consoleでSidekiq::RetrySetを取得します。ここにはリトライ内容が入っています。countで件数を見ると1件ありますね。

> rs = Sidekiq::RetrySet.new
> rs.count
=> 1

次は中身を見てみましょう。

> rs.first
=> #<Sidekiq::SortedEntry:0x007ff571fece00
 @item=
  {"class"=>"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
   "queue"=>"default",
   "args"=>
    [{"job_class"=>"HogeJob",
      "job_id"=>"e0693a53-9860-4e73-9479-674dd84ccaac",
      "queue_name"=>"default",
      "arguments"=>[{"hoge_id"=>100, "_aj_symbol_keys"=>["hoge_id"]}]}],
   "retry"=>true,
   "jid"=>"1044bc2eb37b250b8eea6137",
   "created_at"=>1450159336.0921764,
   "enqueued_at"=>1450159336.0922325,
   "error_message"=>"error!",
   "error_class"=>"RuntimeError",
   "failed_at"=>1450159336.0988874,
   "retry_count"=>0},
 @parent=#<Sidekiq::RetrySet:0x007ff57208ef48 @_size=2, @name="retry">,
 @queue="default",
 @score=1450159361.0989082,
 @value=
  "{\"class\":\"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper\",\"queue\":\"default\",\"args\":[{\"job_class\":\"HogeJob\",\"job_id\":\"e0693a53-9860-4e73-9479-674dd84ccaac\",\"queue_name\":\"default\",\"arguments\":[{\"hoge_id\":100,\"_aj_symbol_keys\":[\"hoge_id\"]}]}],\"retry\":true,\"jid\":\"1044bc2eb37b250b8eea6137\",\"created_at\":1450159336.0921764,\"enqueued_at\":1450159336.0922325,\"error_message\":\"error!\",\"error_class\":\"RuntimeError\",\"failed_at\":1450159336.0988874,\"retry_count\":0}">

素晴らしい。どんな引数で、どんなエラーが発生したかが分かりますね。

Sidekiqダッシュボードに表示する

手動でSidekiq::RetrySetを見れば失敗したキューの情報を取得できます。が、このままでは使いにくいです。そこで、Sidekiqのダッシュボードに表示するようにします。ここで良い感じにやってくれるGemがあるので紹介します。

基本、こいつを入れればOKです。

gem 'sidekiq'
gem 'sinatra', require: false
gem 'sidekiq-failures'

rails serverを立ちあげて http://exampl.ecom/sidekiq にアクセスすると、「失敗」というメニューが追加されたダッシュボードが表示されます。

sidekiq_failer.png

一覧だとこんな感じ。

FailedJobs1.png

クリックして詳細も見ることが出来ます。

FailedJobs2.png

今すぐ再試行などもできるので、使い勝手もいいですね。

API的にも使えそうなヘルパーメソッドも用意されています。

Sidekiq::Failures.count

Sidekiq::Failures.reset_failures

ダッシュボード拡張Gem

mhfs/sidekiq-failures以外にもダッシュボードを拡張してキューの一覧が見られるGemを後輩エンジニアから教えてもらいました。どちらもGemfileに突っ込んであげるだけで使えるので簡単。

sidekiq-history

sidekiq-history.png

こんな感じで表示されます。失敗成功関係なしに、全キューの情報が見られるようです。これはこれで便利…!

sidekiq-statistic

sidekiq-statistic.png

sidekiq-statisticは高機能で、通常のキューと失敗したキューを見ることができます。また、各キューにかかった処理時間の平均値とか出してくれます。ただ、失敗したキューの詳細を見ることはできないようです。できるのかな?

失敗したらSlackに通知する

今やSlackに通知を送るのはデファクトスタンダード的なノリになっているのでSlackのWebHookを使ってキューが失敗したら通知してあげるようにしましょう。素晴らしいGemがあるので、それを利用します。

gem 'slack-incoming-webhooks'

Incoming WebHooksのAPIキーを発行するには、こちら

jobs/hoge_job.rb
class HogeJob < ActiveJob::Base
  queue_as :default

  rescue_from Exception do |e|
    slack = Slack::Incoming::Webhooks.new 'https://hooks.slack.com/services/poyo/fuga/hogepiyo'

    attachments = [{
      title: "Sidekiq failure",
      text: "%s failure \n QueueName %s \n Error %s \n JobId %s \n Arguments %s" % [self.class, self.queue_name, e.inspect, self.job_id, self.arguments],
      color: "#fb2489"
    }]
    slack.post "", attachments: attachments
  end

  def perform(hoge_id:)
    raise "error!"
  end
end

これで、予め設定しておいたSlackチャンネル宛にキューの情報が投げられます。

slack.png

いい感じですね!

あとは、再実行とかをSlackBotに指示できたりすればChatOps的ですね。

Zabbixで監視するためのAPI

ZabbixにSidekiqの状態を教えてあげるAPIサーバを設定します。

config/routes.rb
require 'sidekiq/api'

# 積んでいるキューの数
get "queue-status" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::Queue.new.size.to_s ]] }

# リトライ対象のキューの数
get "queue-retry" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::RetrySet.new.size.to_s ]] }

# キュー待ち時間
get "queue-latency" => proc { [200, {"Content-Type" => "text/plain"}, [Sidekiq::Queue.new.latency.to_s]] }

これで、 http://example.com/queue-retry でリトライ対象のキューの数が取得できます。あとはZabbixの設定をすればOK。nagiosはプラグインがあるみたいです。

AirBrakeを使う

AirBrakeという素晴らしいサービスを使っていればエラーを検知することができるので、こっちもオススメです。無料でも使えますが、有料プランにするか、自分でサーバをたててErrbitを動かすかが良いですね。お金があるなら、有料プランのほうが楽ちんです。

airbrake.png

AirBrakeでも失敗内容が詳しく見られるので、対応が可能です。

以上、こんな感じでSidekiqで静かにそして非業の死を遂げたキューを知る方法をまとめてみました。それぞれ、運用にあったやり方があると思うので、より良い方法あるよーというのがあれば是非教えて下さい。けっこうキューイングシステムを運用していくとぶち当たる壁って多いので…。