非同期処理のために DelayedJob を導入したメモ

メールの送信やランキング集計といった、時間がかかる(かもしれない)処理をバックグラウンドで実行したいので、手軽に導入できそうな DelayedJob を試してみた。

DelayedJob をインストール

Gemfile に

gem 'delayed_job_active_record'

を追加して

bundle

を実行。
delayed_job ではなく delayed_job_active_record なので注意。delayed_job だけだと、DelayedJob で ActiveRecord が使えなかった。

bundle exec rails g delayed_job:active_record

を実行するとマイグレーションファイルやスクリプトが生成されるので、あとは

bundle exec rake db:migrate

でテーブルを作成すればインストール完了。

DelayedJob はジョブキューにデータベースを使うから、ジョブキュー用にわざわざミドルウェアを導入しなくてもいい。この手軽さが一番の長所だと思う。

メール送信を非同期にしてみる

まずは定番から。

class ContactsController < ApplicationController
  ...

  def create
    @contact = Contact.new(params[:contact])
    
    if @contact.valid?
      # 非同期に問い合わせメールを送信
      ContactMailer.delay.contact(@contact)
    end

    respond_with @contact
  end
end

あれ?deliver メソッドは?って最初戸惑ったけど、DelayedJob 側で実行してくれるから無問題。

常に非同期で実行したいメソッドを定義してみる

ランキング集計とか。

class BookarmkCalculator
  def calculate_daily(date)
    Bookmark.where("created_at >= ? AND created_at < ?", date, date.tomorrow)
      .group("entry_id")
      .select("entry_id, count(id) as bookmark_count")
      .order("bookmark_count DESC, comci_id ASC")
  end

  # 常に非同期で実行させる
  handle_asynchronously :calculate_daily
end

handle_asynchronously に指定したメソッドは、常に非同期で実行される。毎回 delay を書かなくていい。

あとは

c = BookmarkCalculator.new
ranking = c.calculate_daily(Date.today)

こんな感じで呼び出せば、非同期に集計できる。

これを 1日1回、定期的に実行したいんだけど…DelayedJob は遅延実行だけで定期的な実行はできなかったorz

定期的にランキングを集計するには他の RubyGems を使わないといけないのか。