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

Golang始めました。

とりあえずvimATOMの設定と(便利そうなので)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

注意

  • ブロック内の変数のスコープはブロック内のみ(外からは参照できない)
  • ブロックの外の変数値を上書きするのは可能