背景
DelayedJobを利用して定期実行しているタスクがあるのですが、処理時間が長いと全く同じタスクが並列して走ってしまうことがありました。
(cronを利用しないのはサーバーのリソースが枯れてしまわないように数を制限しているため)
既に実行されている(locked_atに時間が入っているもの)のhandlerから何のタスクが実行されているか分かれば後からキューイングされたジョブの実行をスキップする処理ができるなと思い調べたのですが変換方法が調べても出てこなかったので記事にしました。
handlerの取り出し
まずは結論から書きたいと思います。
handler = Delayed::Job.first
YAML.load_dj(handler)
上記のようにYAML#load_dj
を利用することでDelayed::Jobを使ってenqueueしたジョブのhandlerをRubyのオブジェクトに変換することができます。
できたオブジェクトはenqueuしたときのクラスなので、例のようにクラス変数のnameなどを取り出したい場合は元のクラスで参照できるようにattr_readerなどの設定をする必要があります。
class A
attr_reader :name
def initialize
@id = 'id'
@name = 'name'
end
def enqueue
Delayed::Job.enqueue~~~
end
end
A.new.enqueue
handler = Delayed::Job.first
task = YAML.load_dj(handler)
=> #<A ~~~~~>
task.name
=> name
load_djの実装
ちなみにこの取り出し方はDelayed::Jobでワーカーが取り出す際に利用しているものでこちらから確認できます。
def payload_object
@payload_object ||= YAML.load_dj(handler)
rescue TypeError, LoadError, NameError, ArgumentError, SyntaxError, Psych::SyntaxError => e
raise DeserializationError, "Job failed to load: #{e.message}. Handler: #{handler.inspect}"
end
また、YAMLモジュールをオーバライドしている箇所はこちらになります
module Psych
def self.load_dj(yaml)
result = parse(yaml)
result ? Delayed::PsychExt::ToRuby.create.accept(result) : result
end
end
最後に
もう少し実装を抽象化できればDelayed::Jobに実装しても良い機能なきもするので実装を考えてみたいと思います。
本記事が誰かの役に立てれ立てれば嬉しいです