AllIsHackedOff

Just a memo, just a progress

delayed_jobを使ってみた

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で使ってみる