diff --git a/lib/delayed_job/threaded_worker.rb b/lib/delayed_job/threaded_worker.rb index 3f514daaf7b..a6ab1d6c7b9 100644 --- a/lib/delayed_job/threaded_worker.rb +++ b/lib/delayed_job/threaded_worker.rb @@ -1,5 +1,18 @@ module Delayed class ThreadedWorker < Delayed::Worker + # Delayed::Plugin callbacks register against the base Delayed::Worker.lifecycle, + # but setup_lifecycle rebuilds the lifecycle on the class it is called on. On this + # subclass it would reset a different lifecycle than the one plugins append to, + # so every ThreadedWorker.new would leak another callback into the base lifecycle. + # Delegate to the base class so each instantiation rebuilds the same lifecycle. + def self.setup_lifecycle + Delayed::Worker.setup_lifecycle + end + + def self.lifecycle + Delayed::Worker.lifecycle + end + def initialize(options={}) super @num_threads = options[:num_threads] diff --git a/spec/unit/lib/delayed_job/threaded_worker_spec.rb b/spec/unit/lib/delayed_job/threaded_worker_spec.rb index f425a1b38a3..285bc5ecfbf 100644 --- a/spec/unit/lib/delayed_job/threaded_worker_spec.rb +++ b/spec/unit/lib/delayed_job/threaded_worker_spec.rb @@ -22,6 +22,16 @@ worker = Delayed::ThreadedWorker.new({ num_threads: 2 }) expect(worker.instance_variable_get(:@grace_period_seconds)).to eq(30) end + + it 'does not accumulate enqueue lifecycle callbacks across instantiations' do + before_enqueue_callbacks = lambda do + Delayed::Worker.lifecycle.instance_variable_get(:@callbacks)[:enqueue].instance_variable_get(:@before).size + end + + baseline = before_enqueue_callbacks.call + 5.times { Delayed::ThreadedWorker.new(options) } + expect(before_enqueue_callbacks.call).to eq(baseline) + end end describe '#start' do