Edited at

ActiveJobのperform_laterとperform_nowの違い


はじめに

ActiveJobのperformメソッドは、perform_laterperform_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_laterperform_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_laterperform_nowの違いを確認するために、簡単なサンプルプログラムを作ってみました。(Railsのバージョンは5.2.3)


app/jobs/sample_job.rb

class SampleJob < ApplicationJob

queue_as :default

def perform(msg)
puts "[JOB] start: #{msg}"
# sleepを入れて時間がかかる処理であることをシミュレート
sleep 3
puts "[JOB] end: #{msg}"
end
end



app/models/foo.rb

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_laterperform_nowの2通りの呼び出し方があります。

では、この2種類のメソッドはどう使い分けるのがよいでしょうか?

基本的なユースケースではperform_laterを使うことが大半だと思います。

なぜなら、重たい処理(=時間のかかる処理)を非同期的に実行することが、ActiveJobを利用する主な動機になるはずだからです。

ですので、perform_nowはあまり使われないと思いますが、以下のようなユースケースでは例外的にperform_nowが必要になるかもしれません。


  • 通常は非同期実行するが、ある特定の条件下においては同期実行したい

  • rails consoleなどで、とりあえずジョブの動作確認がしたい

少なくとも、ActiveJobを作ったのにperform_nowでしか呼びだしていないようなコードがあれば、設計か実装に何か間違いがあるのかもしれません。


まとめ

というわけで、この記事ではActiveJobのperform_laterperform_nowの違いについて調べてみました。

簡単にまとめると、



  • perform_laterは非同期実行(呼び出し側はジョブの完了を待たずに先へ進む)


  • perform_nowは同期実行(呼び出し側はジョブの完了を待ち続ける)

ということになります。