はじめに

Advent Calendar4日目ということで技術情報を書きますが、今回はRailsにおける非同期処理のバックエンドで使うadapterについて簡単に記載します。(RailsのActive Jobについて)

非同期処理とadapter

処理が重たくなる場合やメール送信を使う場合など非同期処理を使用しているかと思います。一旦処理を非同期処理実行用のキューに詰め込み、非同期処理の結果を後で通知することで、アクション時の処理が軽くなりエンドユーザにストレスを与えないようにします。
皆さんはdelayed_jobを使ったことがありますか?便利でしたよね?
今でも便利なのは変わらないのですが、よく使用されるadapter(有名なところ)としてはsidekiqやresqueという選択肢もあります。

adapterの違い

sidekiqとresqueの違いは以下の記事で記載されていますのでご参照ください。
https://qiita.com/zaru/items/8385fdddbd1be25fe370

スクリーンショット 2017-12-04 10.42.36.png
https://github.com/mperham/sidekiq#performance

delayed_jobがsidekiq、resqueと違う大きな点はRedisを使用しない点になります。delayed_jobはActiveRecordを使用していてキュー管理用にテーブルを作成しています。
delayed_jobを使う場合、

  • メリット・・・Redisを使用していない場合、インストールが不要。
  • デメリット・・・Redisの方が早い。アクセスが多く非同期処理を多用する場合、遅延が発生する。

といった特徴があります。またworkerを増やしてもActiveRecord部分でテーブルロックをするのでボトルネックになることがあります。

base.rb
def reserve(worker, max_run_time = Worker.max_run_time)
  # We get up to 5 jobs from the db. In case we cannot get exclusive access to a job we try the next.
  # this leads to a more even distribution of jobs across the worker processes
  find_available(worker.name, worker.read_ahead, max_run_time).detect do |job|
    job.lock_exclusively!(max_run_time, worker.name)
  end
end
def lock_exclusively!(max_run_time, worker)
  now = self.class.db_time_now
  affected_rows = if locked_by != worker
    # We don't own this job so we will update the locked_by name and the locked_at
    self.class.update_all(["locked_at = ?, locked_by = ?", now, worker], ["id = ? and (locked_at is null or locked_at < ?) and (run_at <= ?)", id, (now - max_run_time.to_i), now])
  else
    # We already own this job, this may happen if the job queue crashes.
    # Simply resume and update the locked_at
    self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])
  end
  if affected_rows == 1
    self.locked_at = now
    self.locked_by = worker
    self.locked_at_will_change!
    self.locked_by_will_change!
    return true
  else
    return false
  end
end

所感

Redisを使用していたり、アクセスが多くなって来た場合は、sidekiq、resqueをadapterに使用する感じかなと思います。
delayed_jobを使うのはダメというわけではないですが、KVSであるRedisを使用しているadapterを選択した方が良いかなというのと、実行時はロックする処理が走るので多用するのは控えた方が良いかと思います。アクセス数が増えたり、規模が大きくなってくるとパフォーマンスを意識してRedisを使用する機会が増えると思いますので、delayed_jobを使用するなら小規模アプリケーションになるかなと考えています。

補足

同じようなことがresqueのREADMEに書いてますね。。
Resque VS DelayedJob !!?
https://github.com/resque/resque#resque-vs-delayedjob