👨💼
RailsのActiveJobで特定のジョブを連続実行させない方法
前提
- ActiveJob を使います
- アダプタは sidekiq
- バージョンは Ruby3.1, Rails7.0.1 です
やりたいこと
- ジョブを登録する時に○分待つように指定する
- ジョブ登録時に同じジョブが存在したら、前に登録したジョブは実行しない
方法
1.Sidekiq APIを使って実装
- Sidekiq APIを使って、ジョブ登録前にスケジュールに入っているジョブを取得し、同じジョブの場合は削除します。
-
Sidekiq::ScheduledSet.new
でスケジュールされているジョブを全て取得するので、大規模サービスなどジョブ数が多い場合はパフォーマンスが懸念されます。
class SampleJob < ApplicationJob
queue_as :default
def perform
puts 'hello'
end
end
require 'sidekiq/api'
class Sample
def run
delete_scheduled_job
SampleJob.set(wait: 5.minutes).perform_later
end
private
def delete_scheduled_job
jobs = Sidekiq::ScheduledSet.new
jobs.select { |job|
job.args[0]["job_class"] == "SampleJob" && job.args[0]["queue_name"] == "default"
}.map(&:delete)
end
end
2.Rails.cacheを使って実装
- Rails.cacheを使います。ジョブ登録後にjob_idをキャッシュして、ジョブが実行される時にキャッシュされているjob_idと自身のjob_idが一致するか確認し、一致した場合はジョブを実行します
- キャッシュが揮発する可能性を考慮して、キャッシュが見つからない場合もジョブを実行します
- サンプルではredisを使用しています
- 1の方法はsidekiqの機能を使いましたが、こちらはアダプタには依存しません
require 'sidekiq/api'
class Sample
def run
job = SampleJob.set(wait: 5.minutes).perform_later
Rails.cache.write('cached_job_id', job.job_id)
end
end
class SampleJob < ApplicationJob
queue_as :default
def perform
cached_job_id = Rails.cache.write('cached_job_id')
return if cached_job_id.present? && cached_job_id != self.job_id
puts 'hello'
Rails.cache.delete('cached_job_id') if cached_job_id.present?
end
end
Discussion