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で使ってみる
Golang始めました。
とりあえずvimとATOMの設定と(便利そうなので)pecoを導入したりした後にA Tour Of Goをひと通りやってみました。練習問題が結構骨があって勉強になります。これでコンパイル言語なのはかなり面白いと思ったり。肝心のGo routineとchannelについてはA tour Of Goだけでは良さが全くわからないので
Go弱の会でgolangデビューしてきた。 http://yosuke-furukawa.hatenablog.com/entry/2013/08/13/013725
Goroutineとchannelから始めるgo言語@初心者向けgolang勉強会 http://www.slideshare.net/takuyaueda967/goroutinechannelgogolang
Go言語によるwebアプリの作り方 http://www.slideshare.net/yasi_life/goweb-16448500
Haskellも便利ですよ...とか思いながら。
yield文について
yield何じゃらほい
Railsをかじり始めたときにerbテンプレートに書く<% yield %>などを見て何じゃらほいと思ったのは結構前のことだ。Rubyを書く機会はそれほど多くなかったので、ブロック渡せるのは便利だなー程度にしか感じていなかったのですが、最近若干調べる意欲がわいたのでメモ
参考 (Qiita)[Ruby基礎] ブロックとProcをちゃんと理解する
- コード
def block_foo puts "given block if block_given? # block_given => 1 (ブロックが引数として与えられたとき puts "foo1" yield puts "foo2" end block_foo do puts "block!!!" end
- 実行結果
block given foo1 block!!! foo2
このyieldはブロックの戻り値を返すとのこと。 yieldの呼び出し時に渡した引数がブロックの引数になる。
- コード
def foo yield "a", "b" end foo do |a, b| puts a puts b c = 333 puts c end
- 実行結果
a b 333
Procを使ってさらに便利に
- コード
people = %w(Taro Jiro Saburo) p1 = Proc.new { |name| puts name } people.each &p1 # オブジェクトをブロックにして渡す。
- 結果
Taro Jiro Saburo
注意
- ブロック内の変数のスコープはブロック内のみ(外からは参照できない)
- ブロックの外の変数値を上書きするのは可能