Delayed Job (DJ)
最終更新日 2024年04月27日(土)
Table of Contents
Heroku では Delayed Job 2.1 以降を使用できます。
データベースで生成される負荷が増加するため、ほとんどのアプリケーションでは Delayed Job を使用しないことをお勧めします。代わりに、Sidekiq などの Redis ベースのキューライブラリをお勧めします。
(DJ と略されることもある) Delayed Job を使用すると、Heroku 上の Rails アプリケーションにバックグラウンドタスクを簡単に追加できます。Resque や、他の多くの一般的なバックグラウンドキューイングライブラリも使用できます。 Delayed Job では、バックグラウンドジョブを処理するためのキューとしてデータベースを使用します。アプリのデータベース負荷が高い場合、Delayed Job の使用はアプリのバックグラウンドキューライブラリとして不適切な可能性があります。Delayed Job の使用を開始するには、アプリケーションを設定してから、アプリでワーカープロセスを実行する必要があります。
Delayed Job のセットアップ
PostgreSQL で Delayed Job を使用するには、まず Gemfile
に gem を追加する必要があります。
gem 'delayed_job_active_record'
bundle install
を実行してから、ジョブのキューイングのためのテーブルを作成する必要があります。
$ rails generate delayed_job:active_record
次に、データベースを移行します。
$ rake db:migrate
さらに、ジョブキューに入れられたジョブを処理するよう、アプリケーションに指示する必要があります。これは、次の内容を Procfile
に追加することによって実行できます。
worker: rake jobs:work
この段階で、アプリケーションをローカルで起動すると、ジョブキューの処理が開始されます。
$ heroku local
ジョブのキューイング
システムで Delayed Job をセットアップしたら、次はジョブをキューに入れます。これを行う方法は複数あり、すべて delayed_job のドキュメントで説明されています。
Delayed Job は、delay
メソッドを ActiveRecord
オブジェクトに追加して、そのメソッドがバックグラウンドで実行されるようにします。たとえば、次のようなブログ投稿モデルがあるとします。
class Post < ActiveRecord::Base
def send_to_twitter!
Twitter.update("#{self.title} #{self.url}")
end
end
この場合、次のようにして send_to_twitter!
メソッドを直接呼び出すことができます。
Post.find(9).send_to_twitter!
あるいは、メソッドをキューの後方に追加する delay
メソッドを使用して、メソッドの実行を遅らせることができます。
Post.find(9).delay.send_to_twitter!
メールのキューイング
メールの送信は即時的でないため、送信されるのを待つことをユーザーに要求してもほとんど意味がありません。メーラー上で直接、delay
メソッドを使用できます。たとえば、UserMailer
というメーラーがあり、登録メールを送信しようとしている場合、次のようになります。
UserMailer.send_registration_mail(email, name).deliver
メーラーで delay
メソッドを使用すると、代わりにバックグラウンドタスクでメールを送信できます。
UserMailer.delay.send_registration_mail(email, name)
すべてのメールをバックグラウンドで送信するのが良い方法とみなされています。
デプロイ
コードがローカルで問題なく動作したら、Heroku にデプロイします。
$ git push heroku master
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 315 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
-----> Heroku receiving push
-----> Ruby/Rails app detected
-----> Installing dependencies using Bundler version 1.2.0.pre
Running: bundle install --without development:test --path vendor/bundle --binstubs bin/ --deployment
Using rake (0.9.2.2)
...
Using sass (3.1.19)
Using sass-rails (3.2.5)
Using twitter-bootstrap-rails (2.0.1)
Using uglifier (1.2.5)
Using wicked (0.1.6)
Your bundle is complete! It was installed into ./vendor/bundle
Cleaning up the bundler cache.
-----> Writing config/database.yml to read from DATABASE_URL
-----> Preparing app for Rails asset pipeline
Please see this article for troubleshooting help:
http://devcenter.heroku.com/articles/rails31_heroku_cedar#troubleshooting
-----> Rails plugin injection
Injecting rails_log_stdout
Injecting rails3_serve_static_assets
-----> Discovering process types
Procfile declares types -> web
Default types for Ruby/Rails -> console, rake, worker
-----> Compiled slug size is 15.1MB
-----> Launching... done, v9
http://furious-robot-218.herokuapp.com deployed to Heroku
To git@heroku.com:furious-robot-218
47ae11e..0d13ad1 master -> master
データベースを移行することを忘れないでください。
$ heroku run rake db:migrate
Heroku でワーカープロセスを実行している必要があります。これは、実行中に次のようにして確認できます。
$ heroku ps
ワーカーが見当たらない場合、heroku コマンドを使用してプロセスをスケールアップすることが必要な場合があります。
$ heroku ps:scale worker=1
デバッグ
キューの最後の要素を確認するには、コンソールから次のコマンドを実行します。
$ heroku run rails console
>> Delayed::Job.last
=> #<Delayed::Job id: 8, priority: 0, attempts: 0, handler: "--- !ruby/obj ...
ワーカープロセスが出現してジョブを消費するようになるまで、ジョブは実行されません。ワーカープロセスが起動および実行されていることを確認してください。
$ heroku ps
Process State Command
-------- --------- -------------------------------
web.1 up for 1h thin start -p $PORT
worker.1 up for 1h rake jobs:work
ワーカープロセスの出力を表示するには、-p
フラグでログをフィルタリングします。
$ heroku logs -p worker -t
2012-04-26T20:25:37+00:00 app[worker.1]: ...
さらに分離させるために手動でワーカープロセスを起動することができます。
$ heroku run rake jobs:work
*** Starting job worker host:silver pid:4227
1 jobs processed at 7.0823 j/s, 0 failed ...
ジョブが失敗した場合、ジョブの last_error
フィールドが生成されるので、このフィールドを使用してジョブが失敗した理由に関する情報を取得できます。
job = Delayed::Job.where("last_error is not null").last
puts job.last_error
"{OAuthException: Error validating access token: ...