『Railsで非同期処理 - Sidekiqを使うための準備と使ってみて気づいたこと』 via Kanazawa.rb meetup #36
Railsで⾮同期処理Sidekiqを使うための準備と使ってみて気づいたこと@wtnabeKanazawa.rb meetup 362015-08-29 (Sat) at IT-Plaza MUSASHI
View Slide
お品書き⾮同期処理とはSidekiq⾮同期Worker作成時の注意課題はテスト
⾮同期処理とは
Railsで⾮同期処理とはHTTP requestと同期しないものジョブの実⾏指定と実際の実⾏時間が同期しないもの伝統的なバッチジョブは違う?クライアントサイドの⾮同期処理とは意味が違う
例えばPOST時の処理を後回しにメールの送信画像のサムネイル⽣成response time短縮向け⼤量の作業をスケジュール実⾏
⾮同期⽤のGemRails 4.2+ ActiveJob ( adaptor )Rails 4.1- (various methods)DelayedJobBackgrounDrbRescueSidekiq
Sidekiq
Sidekiqの特徴http://sidekiq.org/Queue ⽤のストレージは RedisCelluloid を使ったスレッドモデルSinatra 製の Dashboard 付きSidekiq Pro アリ
今回選んだ理由もともとよく名前を⽬にしていたredis 環境の⽤意が楽になっていたSidekiq Pro があるってことはビジネスを起こせるレベルのツールと看做せるDashboard がレポートとしても使えそう
セットアップbundle installProcfileinitializers/sidekiq.rbconfig/sidekiq.ymlconfig/routes.rb ( if you wish Dashboard )
Redisのインストールは省略
bundle installin Gemfilegem 'sidekiq'
Foremanの話はこちら前回 meetup #35 で紹介済みLightweight PHP development environment with Foreman and built-inserver // Speaker Deck
ProcfileSidekiq⽤に別なRailsを⽴ち上げるイメージweb: bundle exec rails sredis: redis-serverworker: mkdir -p tmp/pids && \bundle exec sidekiq \-C config/sidekiq.ymlUnicornと同じように全部bundle execで済んじゃう
Herokuでは初期化に注意RailsのバージョンによってDATABASE接続の設定反映の挙動が違うHerokuは環境変数設定を推奨しているが、特にその辺りが違うsee Advanced Options · mperham/sidekiq Wiki
Concurrencyに注意RailsのDB connection poolはdefault 5Sidekiqのconcurrencyはdefault 25溢れるに決まってる><
sidekiq.ymlの例:concurrency: 3:staging::concurrency: 9:production::concurrency: 9development は SQLite, production と staging で web と worker 2つのサーバから max 20 conn までの DB サーバに繋ぐ想定
Dashboardin Gemfilegem 'sinatra'in routes.rbrequire 'sidekiq/web'mount Sidekiq::Web => '/sidekiq'もちろんアクセス制限は忘れないで><
Sidekiq Dashboard Demo
Workerの作り⽅
in Gemfilegem 'sidekiq'
in application.rbapp/workers ディレクトリを作るautoload 対象にeager_load 対象にeager_load については ggl `rails thread'
in foo_worker.rbclass FooWorkerinclude Sidekiq::Workerdef perform(*args)endend
How to useFooWorker.perform_async(arg1, ...)FooWorker.perform_in(time, arg1, ...)etc
worker⽤のRails上でperform()が呼ばれるrequest を受けた Rails とは別な⼈であることに注意perform_in() を使うと遅延時間が指定できる
Worker作成時の注意点
基本⽤語ここでは以下のように⾔葉を⽤いるWorker実際に⾮同期の動作を担当するclassジョブWorkerが実⾏する⼀つ⼀つの処理
注意点perform() にはプリミティブを与えるできるだけ細かくジョブを分ける※ Rubyにはプリミティブなんて存在しないけど
serializeしたオブジェクトじゃダメなの?
オブジェクト渡し簡単だしDBに負担掛けないQueueの容量膨らむ安定してdeserializeできるとは限らない処理の記録の意味が分からない
処理の記録重要⾮同期処理はいつ終わるか分からない「ポチッとやって結果を⽬視」みたいなやり⽅は通⽤しない
Sidekiqの記録Workerの名前と引数がそのまま残るreadableな名前と引数を使う
class BulkThumbnailGeneratorWorkerinclude Sidekiq::Workerdef perform(k, id, attr, size)r = Object.const_get(k).find(id)...endend例えば⼀括サムネイル⽣成
こうするとどのModelのどのIDのどのカラムの画像をどのサイズにするのかこれらが記録に残る。readableで丸分かり。
しかも⼩分けにできる
ジョブの⼩分け重要× 1万回のeachのある1つのジョブ○ 1万のジョブ※ ⼩分けなら記録も明確で途中からretryできる
ジョブの分け⽅⼤量の作業が必要な場合peform の中で each しないeach の中で perform_in を呼ぶ処理に掛かる時間を予測して遅延時間を指定しておくもちろん必要な遅延時間はあらかじめ予測して検証する
課題はテスト
今のところ⾃動テストなしそんなに変更しないいつも⾛らせる必要はない
⼤きな課題はpeform の呼び⽅呼ばれ⽅のコードというよりは遅延時間やジョブの衝突などの⽅で、これは⾮常にテストしにくい
(機会があれば)頑張ります><