delayed_jobを使ってみた
delayed_jobを使ってみた
環境
% rails -v Rails 4.1.8
installation
gem 'delayed_job_active_record'
Rails 4.2以上だとcobfig/application.rbに設定が必要だとか
仕組み(1)
The Active Record backend requires a jobs table. You can create that table by running the following command:
Redisを使うResqueでもいいかなと思ったんですけど、Redisはメモリのデータを乗せ過ぎだというイメージがあるので、スペックの低いサーバ使っている時に問題が起こると嫌だなと思い、Deleyed-jobを選択 テーブルを作るようだ。
rails generate delayed_job:active_record rake db:migrate
rails generateコマンドの詳細が判らないが、delayed_jobテーブルを作るのだろうか
% rails db sqlite> .tables books schema_migrations book_loves delayed_jobs
テーブルが作られている。個々にテーブルをQueueとしてつかうのだろう
sqlite> .schema delayed_jobs CREATE TABLE "delayed_jobs" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "priority" integer DEFAULT 0 NOT NULL, "attempts" integer DEFAULT 0 NOT NULL, "handler" text NOT NULL, "last_error" text, "run_at" datetime, "locked_at" datetime, "failed_at" datetime, "locked_by" varchar(255), "queue" varchar(255), "created_at" datetime, "updated_at" datetime ); CREATE INDEX "delayed_jobs_priority" ON "delayed_jobs" ("priority", "run_at");
スキーマはこんな感じ
使用例(1)
適当に本を登録するサービスを考えることにする。
book title:strinng isbn:string book_love book_id:integer love_count:integer
こんな感じ
登録した本をlove(いいね)できるとし、 いいね!の処理は別にリクエストが来たタイミングでする必要がないとする。
enqueue
love機能だけ書くのが面倒だったので、本の登録時にloveをカウントアップする。此のカウントアップ処理をQueueに積むことにする
def create @book = Book.new(book_params) bookLove.book_id = @book.id bookLove = BookLove.find_or_create_by(book_id: @book.id) loveCount = bookLove.love_count.nil? ? 0 : bookLove.love_count bookLove.love_count = loveCount + 1 bookLove.delay.save
ちょっと久々のRailsなのでだいぶアレな書き方ですが...
bookLove.delay.save
これでbookLoveオブジェクトに対するsaveがQueueにのります
実際にDBを見てみると
sqlite> select * from delayed_jobs; 1| 0| 0| --- !ruby/object:Delayed::PerformableMethod object: !ruby/object:BookLofe attributes: id: 1 book_id: love_count: 2 created_at: 2015-02-22 05:48:10.537041000 Z updated_at: 2015-02-22 05:56:12.796938000 Z method_name: :save args: [] | |2015-02-22 06:00:21.006075| | | | | 2015-02-22 06:00:21.006277|2015-02-22 06:00:21.006277
handlerの所にそれらしき文字列が格納されているのがわかる。 enqueueはこれで終わり。 どういう場合に他のカラムを使うかは考えたい。 (公式READMEに書いてありますね(Gory Details))
dequeue
開発環境でのテスト
% bundle exec rake jobs:work [Worker] Starting job worker [Worker] Job BookLofe#save (id=1) RUNNING [Worker] Job BookLofe#save (id=1) COMPLETED after 0.0058 [Worker] 1 jobs processed at 17.1456 j/s, 0 failed
動いた。dequeueされたっぽい
sqlite> select * from delayed_jobs; sqlite>
テーブルからいなくなっている
enqueue dequeueはされる(結果save!の結果が少しおかしいが..)
結果がおかしい問題
save だけdelayすると結果がおかしくなった(find_or_create_byで作られたままupdateされなかった)ので、修正する
class BookLove < ActiveRecord::Base def count_up(count) currentCount = self.love_count.nil? ? 0 : self.love_count self.love_count = currentCount + count self.save end end
適当にメソッド生やす
book_love.delayu.count_up(1)
これでenqueueするようにしたら正常にカウントアップされた を実行
daemonで処理
gem "delayed_job" gem "delayed_job_active_record" gem "daemons"
bin/delayed_job start
% ps aux | grep delay masashisalvador 5433 0.0 0.0 2432784 588 s000 R+ 7:58PM 0:00.00 grep delay masashisalvador 5429 0.0 0.7 2531216 57736 ?? S 7:58PM 0:00.21 delayed_job
ワーカ増やしたり
README参照
その他(参考)
Jobが失敗したらアラートとか欲しいですね DelayedJobでジョブの状態を取得する 【Rails 4】delayed_jobを使う
Rails 4.2.にはActiveJobがあるらしいので気になる。 Rails4.2のActiveJobをBackburnerで使ってみる