sidekiq-unique-jobs 3.0.13 → 3.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sidekiq-unique-jobs might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b074cf18e3e7d0878e69527a8716e0c962cf9279
4
- data.tar.gz: b960227a096e842d45446eb2e44d916b638451a5
3
+ metadata.gz: 47d33808dbdda5aed24c7559888c87e9314feda9
4
+ data.tar.gz: 7e0698a1ddb3ac9ecff610ec1837e7cbeef6f844
5
5
  SHA512:
6
- metadata.gz: aaaf71ae3c669a2cb77390e1d4c4a9fd12ef0cb1909f187cc9435ff858b6c2967a2e5f64f15c2b129f2776b9c1dd112bc1d44bdb0da114cdcd38aa0a8adf7b50
7
- data.tar.gz: 985a07f514bbc295c1ef2191eacde190246c0d6a7368f1db82046e77a487a7bc1207edfda5c485d4775a9d9d0592ef1e97c937384ff8979c069a732179e10e27
6
+ metadata.gz: 31028d7ac6a86361d3e32e4946223d0803fa71111e8b4e080947e3a9774a5935e57f5fe9661e63fdbc864ff214c8590548a5e12fce4058f3fad8b68358b2efd0
7
+ data.tar.gz: f0a3f8aed7f244ca06067c2753f508e3349dbcd65cce0f7acfdad57d29fbcfafa310b5ff50315a6b3258de39c7e37ffa72dc742924cb7e229f1775492ce6266c
@@ -8,11 +8,12 @@ rvm:
8
8
  - 2.1.2
9
9
  - 2.2.2
10
10
  gemfile:
11
+ - gemfiles/sidekiq_develop.gemfile
12
+ - gemfiles/sidekiq_2.17.gemfile
11
13
  - gemfiles/sidekiq_3.0.gemfile
12
14
  - gemfiles/sidekiq_3.1.gemfile
13
15
  - gemfiles/sidekiq_3.2.gemfile
14
16
  - gemfiles/sidekiq_3.3.gemfile
15
- - gemfiles/sidekiq_develop.gemfile
16
17
  branches:
17
18
  only:
18
19
  - master
@@ -1,3 +1,8 @@
1
+ ## v3.0.14
2
+ - Improve uniqueness check performance thanks @mpherham
3
+ - Remove locks in sidekiq fake testing mode
4
+ - Do not unlock jobs when sidekiq is shutting down
5
+
1
6
  ## v3.0.13
2
7
  - Improved testing capabilities (testing uniqueness should not work better)
3
8
  - Configurable logging of duplicate payloads
data/README.md CHANGED
@@ -43,6 +43,19 @@ sidekiq_options unique: true, unique_job_expiration: 120 * 60 # 2 hours
43
43
 
44
44
  Requiring the gem in your gemfile should be sufficient to enable unique jobs.
45
45
 
46
+ ### Usage with ActiveJob
47
+
48
+ ```ruby
49
+ Sidekiq.default_worker_options = {
50
+ 'unique' => true,
51
+ 'unique_args' => proc do |args|
52
+ [args.first.except('job_id')]
53
+ end
54
+ }
55
+ SidekiqUniqueJobs.config.unique_args_enabled = true
56
+ ```
57
+
58
+
46
59
  ### Finer Control over Uniqueness
47
60
 
48
61
  Sometimes it is desired to have a finer control over which arguments are used in determining uniqueness of the job, and others may be _transient_. For this use-case, you need to set `SidekiqUniqueJobs.config.unique_args_enabled` to true in an initializer, and then defined either `unique_args` method, or a ruby proc.
@@ -96,6 +109,17 @@ class UniqueJobWithFilterMethod
96
109
  end
97
110
  ```
98
111
 
112
+ ### Unique Storage Method
113
+
114
+ Starting from sidekiq-unique-jobs 3.0.14 we will use the `set` method in a way that has been available since redis 2.6.12. If you are on an older redis version you will have to change a config value like below.
115
+
116
+ ```ruby
117
+ SidekiqUniqueJobs.config.unique_storage_method = :old
118
+ ```
119
+
120
+ That should allow you to keep using redis in the old fashion way. See #89 for mor information.
121
+
122
+
99
123
  ### Logging
100
124
 
101
125
  To see logging in sidekiq when duplicate payload has been filtered out you can enable on a per worker basis using the sidekiq options. The default value is false
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "http://rubygems.org"
4
4
 
5
- gem "appraisal", "~> 1.0.0"
5
+ gem "appraisal", "~> 2.0.0"
6
6
  gem "pry", :platform => :mri
7
7
  gem "sidekiq", "~> 2.17.0"
8
8
 
@@ -16,7 +16,8 @@ module SidekiqUniqueJobs
16
16
  unique_prefix: 'sidekiq_unique',
17
17
  unique_args_enabled: false,
18
18
  default_expiration: 30 * 60,
19
- default_unlock_order: :after_yield
19
+ default_unlock_order: :after_yield,
20
+ unique_storage_method: :new
20
21
  )
21
22
  end
22
23
 
@@ -4,7 +4,8 @@ module SidekiqUniqueJobs
4
4
  :unique_prefix,
5
5
  :unique_args_enabled,
6
6
  :default_expiration,
7
- :default_unlock_order
7
+ :default_unlock_order,
8
+ :unique_storage_method
8
9
  ]
9
10
 
10
11
  class << self
@@ -17,7 +17,9 @@ module SidekiqUniqueJobs
17
17
  end
18
18
 
19
19
  def self.review(worker_class, item, queue, redis_pool = nil, log_duplicate_payload = false)
20
- new(worker_class, item, queue, redis_pool, log_duplicate_payload).review { yield }
20
+ new(worker_class, item, queue, redis_pool, log_duplicate_payload).review do
21
+ yield
22
+ end
21
23
  end
22
24
 
23
25
  def initialize(worker_class, item, queue, redis_pool = nil, log_duplicate_payload = false)
@@ -31,12 +33,12 @@ module SidekiqUniqueJobs
31
33
 
32
34
  def review
33
35
  item['unique_hash'] = payload_hash
36
+
34
37
  unless unique_for_connection?
35
- if @log_duplicate_payload
36
- Sidekiq.logger.warn "payload is not unique #{item}"
37
- end
38
+ Sidekiq.logger.warn "payload is not unique #{item}" if @log_duplicate_payload
38
39
  return
39
40
  end
41
+
40
42
  yield
41
43
  end
42
44
 
@@ -44,23 +46,24 @@ module SidekiqUniqueJobs
44
46
 
45
47
  attr_reader :item, :worker_class, :redis_pool, :queue, :log_duplicate_payload
46
48
 
47
- # rubocop:disable MethodLength
48
49
  def unique_for_connection?
49
- unique = false
50
+ send("#{storage_method}_unique_for?")
51
+ end
52
+
53
+ def storage_method
54
+ SidekiqUniqueJobs.config.unique_storage_method
55
+ end
56
+
57
+ def old_unique_for?
58
+ unique = nil
50
59
  connection do |conn|
51
60
  conn.watch(payload_hash)
52
-
53
- if conn.get(payload_hash).to_i == 1 ||
54
- (conn.get(payload_hash).to_i == 2 && item['at'])
61
+ pid = conn.get(payload_hash).to_i
62
+ if pid == 1 || (pid == 2 && item['at'])
55
63
  # if the job is already queued, or is already scheduled and
56
64
  # we're trying to schedule again, abort
57
65
  conn.unwatch
58
66
  else
59
- # if the job was previously scheduled and is now being queued,
60
- # or we've never seen it before
61
- expires_at = unique_job_expiration || SidekiqUniqueJobs.config.default_expiration
62
- expires_at = ((Time.at(item['at']) - Time.now.utc) + expires_at).to_i if item['at']
63
-
64
67
  unique = conn.multi do
65
68
  # set value of 2 for scheduled jobs, 1 for queued jobs.
66
69
  conn.setex(payload_hash, expires_at, item['at'] ? 2 : 1)
@@ -69,7 +72,20 @@ module SidekiqUniqueJobs
69
72
  end
70
73
  unique
71
74
  end
72
- # rubocop:enable MethodLength
75
+
76
+ def new_unique_for?
77
+ connection do |conn|
78
+ return conn.set(payload_hash, item['at'] ? 2 : 1, nx: true, ex: expires_at)
79
+ end
80
+ end
81
+
82
+ def expires_at
83
+ # if the job was previously scheduled and is now being queued,
84
+ # or we've never seen it before
85
+ ex = unique_job_expiration || SidekiqUniqueJobs.config.default_expiration
86
+ ex = ((Time.at(item['at']) - Time.now.utc) + ex).to_i if item['at']
87
+ ex
88
+ end
73
89
 
74
90
  def connection(&block)
75
91
  SidekiqUniqueJobs::Connectors.connection(redis_pool, &block)
@@ -8,17 +8,17 @@ module SidekiqUniqueJobs
8
8
  attr_reader :unlock_order, :redis_pool
9
9
 
10
10
  def call(worker, item, _queue, redis_pool = nil)
11
+ operative = true
11
12
  @redis_pool = redis_pool
12
-
13
13
  decide_unlock_order(worker.class)
14
14
  lock_key = payload_hash(item)
15
- unlocked = before_yield? ? unlock(lock_key).inspect : 0
16
-
15
+ unlock(lock_key) if before_yield?
17
16
  yield
17
+ rescue Sidekiq::Shutdown
18
+ operative = false
19
+ raise
18
20
  ensure
19
- if after_yield? || !defined? unlocked || unlocked != 1
20
- unlock(lock_key)
21
- end
21
+ unlock(lock_key) if after_yield? && operative
22
22
  end
23
23
 
24
24
  def decide_unlock_order(klass)
@@ -1,3 +1,3 @@
1
1
  module SidekiqUniqueJobs
2
- VERSION = '3.0.13'
2
+ VERSION = '3.0.14'
3
3
  end
@@ -57,7 +57,7 @@ describe 'Client' do
57
57
 
58
58
  it 'enqueues previously scheduled job' do
59
59
  QueueWorker.sidekiq_options unique: true
60
- QueueWorker.perform_in(60 * 60, 1, 2)
60
+ QueueWorker.perform_in(60 * 60, [1, 2])
61
61
 
62
62
  # time passes and the job is pulled off the schedule:
63
63
  Sidekiq::Client.push('class' => QueueWorker, 'queue' => 'customqueue', 'args' => [1, 2])
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'sidekiq/cli'
2
3
 
3
4
  module SidekiqUniqueJobs
4
5
  module Middleware
@@ -20,8 +21,10 @@ module SidekiqUniqueJobs
20
21
  end
21
22
 
22
23
  it 'returns true when unique_unlock_order has been set' do
23
- UniqueWorker.sidekiq_options unique_unlock_order: :before_yield
24
- expect(subject.unlock_order_configured?(UniqueWorker))
24
+ test_worker_class = UniqueWorker.dup
25
+ test_worker_class.sidekiq_options unique_unlock_order: :before_yield
26
+
27
+ expect(subject.unlock_order_configured?(test_worker_class))
25
28
  .to eq(true)
26
29
  end
27
30
  end
@@ -29,9 +32,11 @@ module SidekiqUniqueJobs
29
32
  describe '#decide_unlock_order' do
30
33
  context 'when worker has specified unique_unlock_order' do
31
34
  it 'changes unlock_order to the configured value' do
32
- UniqueWorker.sidekiq_options unique_unlock_order: :before_yield
35
+ test_worker_class = UniqueWorker.dup
36
+ test_worker_class.sidekiq_options unique_unlock_order: :before_yield
37
+
33
38
  expect do
34
- subject.decide_unlock_order(UniqueWorker)
39
+ subject.decide_unlock_order(test_worker_class)
35
40
  end.to change { subject.unlock_order }.to :before_yield
36
41
  end
37
42
  end
@@ -39,6 +44,7 @@ module SidekiqUniqueJobs
39
44
  context "when worker hasn't specified unique_unlock_order" do
40
45
  it 'falls back to configured default_unlock_order' do
41
46
  SidekiqUniqueJobs.config.default_unlock_order = :before_yield
47
+
42
48
  expect do
43
49
  subject.decide_unlock_order(UniqueWorker)
44
50
  end.to change { subject.unlock_order }.to :before_yield
@@ -75,6 +81,31 @@ module SidekiqUniqueJobs
75
81
  expect(subject.default_unlock_order).to eq(:after_yield)
76
82
  end
77
83
  end
84
+
85
+ describe '#call' do
86
+ context 'unlock' do
87
+ let(:uj) { SidekiqUniqueJobs::Middleware::Server::UniqueJobs.new }
88
+ let(:items) { [AfterYieldWorker.new, { 'class' => 'testClass' }, 'test'] }
89
+
90
+ it 'should unlock after yield when call succeeds' do
91
+ expect(uj).to receive(:unlock)
92
+
93
+ uj.call(*items) { true }
94
+ end
95
+
96
+ it 'should unlock after yield when call errors' do
97
+ expect(uj).to receive(:unlock)
98
+
99
+ expect { uj.call(*items) { fail } }.to raise_error(RuntimeError)
100
+ end
101
+
102
+ it 'should not unlock after yield on shutdown, but still raise error' do
103
+ expect(uj).to_not receive(:unlock)
104
+
105
+ expect { uj.call(*items) { fail Sidekiq::Shutdown } }.to raise_error(Sidekiq::Shutdown)
106
+ end
107
+ end
108
+ end
78
109
  end
79
110
  end
80
111
  end
@@ -0,0 +1,13 @@
1
+ class AfterYieldWorker
2
+ include Sidekiq::Worker
3
+ sidekiq_options queue: :working, retry: 1, backtrace: 10, unique_unlock_order: :after_yield
4
+ sidekiq_options unique: false
5
+
6
+ sidekiq_retries_exhausted do |msg|
7
+ Sidekiq.logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
8
+ end
9
+
10
+ def perform(*)
11
+ # NO-OP
12
+ end
13
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-unique-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.13
4
+ version: 3.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-05 00:00:00.000000000 Z
11
+ date: 2015-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -170,6 +170,7 @@ files:
170
170
  - spec/lib/sidekiq_unique_ext_spec.rb
171
171
  - spec/lib/unlock_order_spec.rb
172
172
  - spec/spec_helper.rb
173
+ - spec/support/after_yield_worker.rb
173
174
  - spec/support/another_unique_worker.rb
174
175
  - spec/support/my_worker.rb
175
176
  - spec/support/sidekiq_meta.rb