はじめに
ActiveJobのperformメソッドは、perform_later
とperform_now
の2通りで呼び出すことができます(以下のコード例はRailsのAPIドキュメントから引用しました)。
class ProcessPhotoJob < ActiveJob::Base
def perform(photo)
photo.watermark!('Rails')
photo.rotate!(90.degrees)
photo.resize_to_fit!(300, 300)
photo.upload!
end
end
# perform_laterで呼び出す
ProcessPhotoJob.perform_later(photo)
# perform_nowで呼び出す
ProcessPhotoJob.perform_now(photo)
では、perform_later
とperform_now
は具体的にどのような違いがあるのでしょうか?
ネットを検索してみたところ、意外と明確に説明してくれている資料がなかったので、独自にまとめてみることにします。
perform_laterとperform_nowの違いは何か
APIドキュメントには以下のような説明があります。
- (
perform_later
は)ジョブをキューに入れ、キューが空き次第ジョブを実行する。(To enqueue a job to be performed as soon as the queuing system is free) - (
perform_now
は)キューに入ることなく即座に実行される。(A job can also be processed immediately without sending to the queue)
もう少し簡単にいうと、perform_later
は非同期的に実行され、perform_now
は同期的に実行されます。
具体的にどういう挙動の違いが現れるのか、次の項で確認していきます。
簡単なサンプルで比較してみる
perform_later
とperform_now
の違いを確認するために、簡単なサンプルプログラムを作ってみました。(Railsのバージョンは5.2.3)
class SampleJob < ApplicationJob
queue_as :default
def perform(msg)
puts "[JOB] start: #{msg}"
# sleepを入れて時間がかかる処理であることをシミュレート
sleep 3
puts "[JOB] end: #{msg}"
end
end
class Foo
# 非同期的にSampleJobを呼び出すメソッド
def execute_later
puts '[RUNNER] execute_later start'
SampleJob.perform_later('hello')
puts '[RUNNER] execute_later end'
end
# 同期的にSampleJobを呼び出すメソッド
def execute_now
puts '[RUNNER] execute_now start'
SampleJob.perform_now('nihao')
puts '[RUNNER] execute_now end'
end
end
perform_laterを呼び出した場合
perform_later
を呼び出した場合は次のような実行結果になります。
> foo = Foo.new
> foo.execute_later
[RUNNER] execute_later start
Enqueued SampleJob (Job ID: e6859c49-4ec4-41d2-820f-eef63615cc51) to Async(default) with arguments: "hello"
Performing SampleJob (Job ID: e6859c49-4ec4-41d2-820f-eef63615cc51) from Async(default) with arguments: "hello"
[RUNNER] execute_later end
[JOB] start: hello
[JOB] end: hello
Performed SampleJob (Job ID: e6859c49-4ec4-41d2-820f-eef63615cc51) from Async(default) in 3005.48ms
ご覧のとおり、Foo#execute_later
の実行が完了("[RUNNER] execute_later end"を表示)したあとに、SampleJob#perform
が実行されていることがわかります(非同期実行)。
そのため、Foo#execute_later
の呼び出し自体はすぐに完了します。
perform_nowを呼び出した場合
一方、perform_now
を呼び出した場合は次のような実行結果になります。
> foo = Foo.new
> foo.execute_now
[RUNNER] execute_now start
Performing SampleJob (Job ID: 9cd85c4c-e515-4d70-aeb7-ac915d7e745b) from Async(default) with arguments: "nihao"
[JOB] start: nihao
[JOB] end: nihao
Performed SampleJob (Job ID: 9cd85c4c-e515-4d70-aeb7-ac915d7e745b) from Async(default) in 3001.38ms
[RUNNER] execute_now end
こちらはFoo#execute_now
内でSampleJob#perform
が即座に実行され、SampleJob#perform
の処理が完了してから、"[RUNNER] execute_now end"が出力されています(同期実行)。
SampleJob#perform
は3秒間スリープするようになっているため、Foo#execute_now
の実行時間も3秒以上かかることになります。
どう使い分けるのがよいか
ここまで説明した通り、ActiveJobではperform_later
とperform_now
の2通りの呼び出し方があります。
では、この2種類のメソッドはどう使い分けるのがよいでしょうか?
基本的なユースケースではperform_later
を使うことが大半だと思います。
なぜなら、重たい処理(=時間のかかる処理)を非同期的に実行することが、ActiveJobを利用する主な動機になるはずだからです。
ですので、perform_now
はあまり使われないと思いますが、以下のようなユースケースでは例外的にperform_now
が必要になるかもしれません。
- 通常は非同期実行するが、ある特定の条件下においては同期実行したい
- rails consoleなどで、とりあえずジョブの動作確認がしたい
少なくとも、ActiveJobを作ったのにperform_now
でしか呼びだしていないようなコードがあれば、設計か実装に何か間違いがあるのかもしれません。
まとめ
というわけで、この記事ではActiveJobのperform_later
とperform_now
の違いについて調べてみました。
簡単にまとめると、
-
perform_later
は非同期実行(呼び出し側はジョブの完了を待たずに先へ進む) -
perform_now
は同期実行(呼び出し側はジョブの完了を待ち続ける)
ということになります。