diff --git a/lib/resque/integration/unique.rb b/lib/resque/integration/unique.rb index 9e87600..542ccdc 100644 --- a/lib/resque/integration/unique.rb +++ b/lib/resque/integration/unique.rb @@ -3,6 +3,7 @@ require 'digest/sha1' require 'active_support/core_ext/module/aliasing' +require 'active_support/core_ext/hash' require 'resque/plugins/lock' require 'resque/plugins/progress' @@ -66,6 +67,8 @@ def lock_on(&block) # LockID should be independent from MetaID # @api private def lock(meta_id, *args) + args = [*args[0..-2], args.last.with_indifferent_access] if args.last.is_a?(Hash) + "lock:#{name}-#{Digest::SHA1.hexdigest(obj_to_string(lock_on[*args]))}" end diff --git a/resque-integration.gemspec b/resque-integration.gemspec index fc8e621..d7dfe39 100644 --- a/resque-integration.gemspec +++ b/resque-integration.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency 'railties', '>= 3.0.0' gem.add_runtime_dependency 'activerecord', '>= 3.0.0' gem.add_runtime_dependency 'actionpack', '>= 3.0.0' + gem.add_runtime_dependency 'activesupport', '>= 3.0.0' gem.add_runtime_dependency 'resque-lock', '~> 1.1.0' gem.add_runtime_dependency 'resque-meta', '>= 2.0.0' gem.add_runtime_dependency 'resque-progress', '~> 1.0.1' diff --git a/spec/resque/integration_spec.rb b/spec/resque/integration_spec.rb index f749d54..905197b 100644 --- a/spec/resque/integration_spec.rb +++ b/spec/resque/integration_spec.rb @@ -21,42 +21,92 @@ describe 'enqueue' do context 'when job is uniq' do + class DummyService + def self.call + # no-op + end + end + class UniqueJob include Resque::Integration queue :test unique + + def self.execute(id, params) + DummyService.call + end end it 'enqueues only one job' do - UniqueJob.enqueue(param: 'one') + UniqueJob.enqueue(1, param: 'one') Timecop.travel(10.hours.since) do - UniqueJob.enqueue(param: 'one') + UniqueJob.enqueue(1, param: 'one') expect(Resque.peek(:test, 0, 100).size).to eq(1) end end it 'enqueues two jobs with differ args' do - UniqueJob.enqueue(param: 'one') + UniqueJob.enqueue(1, param: 'one') Timecop.travel(10.hours.since) do - UniqueJob.enqueue(param: 'two') + UniqueJob.enqueue(1, param: 'two') expect(Resque.peek(:test, 0, 100).size).to eq(2) end end it 'enqueues two jobs after expire lock timeout' do - UniqueJob.enqueue(param: 'one') + UniqueJob.enqueue(1, param: 'one') Timecop.travel(4.days.since) do - UniqueJob.enqueue(param: 'one') + UniqueJob.enqueue(1, param: 'one') expect(Resque.peek(:test, 0, 100).size).to eq(2) end end + + describe 'unlock' do + class UniqueJobWithBlock + include Resque::Integration + + queue :test_with_block + unique { |id, params| [id, params[:one], params[:two]] } + + def self.execute(id, params) + DummyService.call + end + end + + around do |example| + inline = Resque.inline + Resque.inline = true + + example.run + + Resque.inline = inline + end + + it 'unlocks uniq job with args and without block' do + expect(DummyService).to receive(:call).twice + + UniqueJob.enqueue(1, one: 1, two: 2) + UniqueJob.enqueue(1, one: 1, two: 2) + + expect(UniqueJob.locked?(1, one: 1, two: 2)).to eq(false) + end + + it 'unlocks uniq job with args and block' do + expect(DummyService).to receive(:call).twice + + UniqueJobWithBlock.enqueue(1, one: 1, two: 2) + UniqueJobWithBlock.enqueue(1, one: 1, two: 2) + + expect(UniqueJobWithBlock.locked?(1, one: 1, two: 2)).to eq(false) + end + end end end end