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 +4 -4
- data/.travis.yml +2 -1
- data/CHANGELOG.md +5 -0
- data/README.md +24 -0
- data/gemfiles/sidekiq_2.17.gemfile +1 -1
- data/lib/sidekiq-unique-jobs.rb +2 -1
- data/lib/sidekiq_unique_jobs/config.rb +2 -1
- data/lib/sidekiq_unique_jobs/middleware/client/strategies/unique.rb +31 -15
- data/lib/sidekiq_unique_jobs/middleware/server/unique_jobs.rb +6 -6
- data/lib/sidekiq_unique_jobs/version.rb +1 -1
- data/spec/lib/client_spec.rb +1 -1
- data/spec/lib/middleware/server/unique_jobs_spec.rb +35 -4
- data/spec/support/after_yield_worker.rb +13 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47d33808dbdda5aed24c7559888c87e9314feda9
|
4
|
+
data.tar.gz: 7e0698a1ddb3ac9ecff610ec1837e7cbeef6f844
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31028d7ac6a86361d3e32e4946223d0803fa71111e8b4e080947e3a9774a5935e57f5fe9661e63fdbc864ff214c8590548a5e12fce4058f3fad8b68358b2efd0
|
7
|
+
data.tar.gz: f0a3f8aed7f244ca06067c2753f508e3349dbcd65cce0f7acfdad57d29fbcfafa310b5ff50315a6b3258de39c7e37ffa72dc742924cb7e229f1775492ce6266c
|
data/.travis.yml
CHANGED
@@ -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
|
data/CHANGELOG.md
CHANGED
@@ -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
|
data/lib/sidekiq-unique-jobs.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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?
|
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)
|
data/spec/lib/client_spec.rb
CHANGED
@@ -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.
|
24
|
-
|
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.
|
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(
|
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.
|
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-
|
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
|