sidekiq-unique-jobs 5.0.0 → 5.0.1
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/.codeclimate.yml +2 -31
- data/.editorconfig +2 -2
- data/.rubocop.yml +1 -1
- data/.travis.yml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +2 -6
- data/lib/sidekiq-unique-jobs.rb +5 -1
- data/lib/sidekiq/simulator.rb +3 -4
- data/lib/sidekiq_unique_jobs/cli.rb +27 -5
- data/lib/sidekiq_unique_jobs/lock/until_executed.rb +6 -14
- data/lib/sidekiq_unique_jobs/scripts.rb +8 -6
- data/lib/sidekiq_unique_jobs/scripts/acquire_lock.rb +45 -0
- data/lib/sidekiq_unique_jobs/scripts/release_lock.rb +47 -0
- data/lib/sidekiq_unique_jobs/testing.rb +1 -1
- data/lib/sidekiq_unique_jobs/unique_args.rb +3 -3
- data/lib/sidekiq_unique_jobs/unlockable.rb +4 -18
- data/lib/sidekiq_unique_jobs/util.rb +21 -2
- data/lib/sidekiq_unique_jobs/version.rb +1 -1
- data/rails_example/.env +1 -1
- data/rails_example/Gemfile +1 -0
- data/rails_example/Procfile +1 -1
- data/rails_example/app/workers/simple_worker.rb +1 -1
- data/rails_example/app/workers/slow_until_executing_worker.rb +1 -1
- data/rails_example/app/workers/spawn_simple_worker.rb +1 -1
- data/rails_example/app/workers/while_executing_worker.rb +12 -0
- data/rails_example/app/workers/without_argument_worker.rb +1 -1
- data/sidekiq-unique-jobs.gemspec +1 -0
- data/spec/jobs/another_unique_job.rb +1 -1
- data/spec/jobs/my_job.rb +1 -1
- data/spec/jobs/unique_job_with_filter_method.rb +1 -1
- data/spec/jobs/unique_on_all_queues_job.rb +1 -1
- data/spec/jobs/until_executed_job.rb +1 -1
- data/spec/jobs/while_executing_job.rb +1 -1
- data/spec/lib/sidekiq_unique_jobs/cli_spec.rb +183 -0
- data/spec/lib/sidekiq_unique_jobs/client/middleware_spec.rb +1 -9
- data/spec/lib/sidekiq_unique_jobs/lock/until_and_while_executing_spec.rb +0 -2
- data/spec/lib/sidekiq_unique_jobs/script_mock_spec.rb +1 -3
- data/spec/lib/sidekiq_unique_jobs/scripts/acquire_lock_spec.rb +50 -0
- data/spec/lib/sidekiq_unique_jobs/scripts/release_lock_spec.rb +41 -0
- data/spec/lib/sidekiq_unique_jobs/scripts_spec.rb +4 -8
- data/spec/lib/sidekiq_unique_jobs/server/middleware_spec.rb +0 -5
- data/spec/lib/sidekiq_unique_jobs/sidekiq_testing_enabled_spec.rb +0 -19
- data/spec/lib/sidekiq_unique_jobs/sidekiq_unique_ext_spec.rb +0 -5
- data/spec/lib/sidekiq_unique_jobs/unlockable_spec.rb +0 -1
- data/spec/lib/sidekiq_unique_jobs/util_spec.rb +54 -26
- data/spec/spec_helper.rb +40 -2
- data/spec/support/matchers/redis_matchers.rb +16 -6
- metadata +22 -2
data/rails_example/.env
CHANGED
data/rails_example/Gemfile
CHANGED
data/rails_example/Procfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
web: bundle exec rails server -p $PORT
|
2
|
-
worker: bundle exec sidekiq
|
2
|
+
worker: bundle exec sidekiq -C config/sidekiq.yml
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class WhileExecutingWorker
|
2
|
+
include Sidekiq::Worker
|
3
|
+
|
4
|
+
sidekiq_options unique: :while_executing
|
5
|
+
|
6
|
+
def perform(_one, _two)
|
7
|
+
Sidekiq::Logging.with_context(self.class.name) do
|
8
|
+
logger.debug { "#{__method__}(#{some_args})" }
|
9
|
+
end
|
10
|
+
sleep 10
|
11
|
+
end
|
12
|
+
end
|
data/sidekiq-unique-jobs.gemspec
CHANGED
@@ -23,5 +23,6 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.add_development_dependency 'timecop'
|
24
24
|
gem.add_development_dependency 'yard'
|
25
25
|
gem.add_development_dependency 'gem-release'
|
26
|
+
gem.add_development_dependency 'aruba'
|
26
27
|
gem.add_development_dependency 'codeclimate-test-reporter', '~> 1.0.0'
|
27
28
|
end
|
@@ -4,7 +4,7 @@ class AnotherUniqueJob
|
|
4
4
|
unique: :until_executed
|
5
5
|
|
6
6
|
sidekiq_retries_exhausted do |msg|
|
7
|
-
|
7
|
+
logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
|
8
8
|
end
|
9
9
|
|
10
10
|
def perform(*)
|
data/spec/jobs/my_job.rb
CHANGED
@@ -3,7 +3,7 @@ class MyJob
|
|
3
3
|
sidekiq_options queue: :working, retry: 1, backtrace: 10, unique: false
|
4
4
|
|
5
5
|
sidekiq_retries_exhausted do |msg|
|
6
|
-
|
6
|
+
logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
|
7
7
|
end
|
8
8
|
|
9
9
|
def perform(*)
|
@@ -4,7 +4,7 @@ class UniqueJobWithFilterMethod
|
|
4
4
|
unique: :while_executing, unique_args: :filtered_args
|
5
5
|
|
6
6
|
sidekiq_retries_exhausted do |msg|
|
7
|
-
|
7
|
+
logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
|
8
8
|
end
|
9
9
|
|
10
10
|
def perform(*)
|
@@ -4,7 +4,7 @@ class UniqueOnAllQueuesJob
|
|
4
4
|
unique: :until_executed, unique_on_all_queues: true
|
5
5
|
|
6
6
|
sidekiq_retries_exhausted do |msg|
|
7
|
-
|
7
|
+
logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
|
8
8
|
end
|
9
9
|
|
10
10
|
def perform(*)
|
@@ -4,7 +4,7 @@ class UntilExecutedJob
|
|
4
4
|
sidekiq_options unique: :until_executed
|
5
5
|
|
6
6
|
sidekiq_retries_exhausted do |msg|
|
7
|
-
|
7
|
+
logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
|
8
8
|
end
|
9
9
|
|
10
10
|
def perform(*)
|
@@ -3,7 +3,7 @@ class WhileExecutingJob
|
|
3
3
|
sidekiq_options queue: :working, retry: 1, backtrace: 10, unique: :while_executing
|
4
4
|
|
5
5
|
sidekiq_retries_exhausted do |msg|
|
6
|
-
|
6
|
+
logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
|
7
7
|
end
|
8
8
|
|
9
9
|
def perform(_)
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'thor/runner'
|
3
|
+
|
4
|
+
RSpec.describe SidekiqUniqueJobs::Cli, ruby_ver: '>= 2.4' do
|
5
|
+
describe '#help' do
|
6
|
+
let(:output) { capture(:stdout) { described_class.start(%w[help]) } }
|
7
|
+
let(:banner) do
|
8
|
+
<<~EOS.freeze
|
9
|
+
Commands:
|
10
|
+
jobs console # drop into a console with easy access to helper methods
|
11
|
+
jobs del PATTERN # deletes unique keys from redis by pattern
|
12
|
+
jobs expire # removes all expired unique keys from the hash in redis
|
13
|
+
jobs help [COMMAND] # Describe available commands or one specific command
|
14
|
+
jobs keys PATTERN # list all unique keys and their expiry time
|
15
|
+
EOS
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'displays help' do
|
19
|
+
expect(output).to include(banner)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#help del' do
|
23
|
+
let(:output) { capture(:stdout) { described_class.start(%w[help del]) } }
|
24
|
+
let(:banner) do
|
25
|
+
<<~EOS.freeze
|
26
|
+
Usage:
|
27
|
+
jobs del PATTERN
|
28
|
+
|
29
|
+
Options:
|
30
|
+
d, [--dry-run], [--no-dry-run] # set to false to perform deletion
|
31
|
+
c, [--count=N] # The max number of keys to return
|
32
|
+
# Default: 1000
|
33
|
+
|
34
|
+
deletes unique keys from redis by pattern
|
35
|
+
EOS
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'displays help about the `del` command' do
|
39
|
+
expect(output).to eq(banner)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#help expire' do
|
44
|
+
let(:output) { capture(:stdout) { described_class.start(%w[help expire]) } }
|
45
|
+
|
46
|
+
let(:banner) do
|
47
|
+
<<~EOS.freeze
|
48
|
+
Usage:
|
49
|
+
jobs expire
|
50
|
+
|
51
|
+
removes all expired unique keys from the hash in redis
|
52
|
+
EOS
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'displays help about the `expire` command' do
|
56
|
+
expect(output).to eq(banner)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#help keys' do
|
61
|
+
let(:output) { capture(:stdout) { described_class.start(%w[help keys]) } }
|
62
|
+
let(:banner) do
|
63
|
+
<<~EOS.freeze
|
64
|
+
Usage:
|
65
|
+
jobs keys PATTERN
|
66
|
+
|
67
|
+
Options:
|
68
|
+
c, [--count=N] # The max number of keys to return
|
69
|
+
# Default: 1000
|
70
|
+
|
71
|
+
list all unique keys and their expiry time
|
72
|
+
EOS
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'displays help about the `key` command' do
|
76
|
+
expect(output).to eq(banner)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
let(:pattern) { '*' }
|
82
|
+
let(:max_lock_time) { 1 }
|
83
|
+
let(:unique_key) { 'uniquejobs:abcdefab' }
|
84
|
+
let(:jid) { 'abcdefab' }
|
85
|
+
|
86
|
+
describe '.keys' do
|
87
|
+
let(:output) { capture(:stdout) { described_class.start(%w[keys * --count 1000]) } }
|
88
|
+
|
89
|
+
context 'when no keys exist' do
|
90
|
+
let(:expected) { "Found 0 keys matching '#{pattern}':\n" }
|
91
|
+
specify { expect(output).to eq(expected) }
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when a key exists' do
|
95
|
+
let(:keys) { ['defghayl', jid, 'poilkij'].sort }
|
96
|
+
before do
|
97
|
+
keys.each do |jid|
|
98
|
+
unique_key = "uniquejobs:#{jid}"
|
99
|
+
SidekiqUniqueJobs::Scripts::AcquireLock.execute(nil, unique_key, jid, 20)
|
100
|
+
expect(SidekiqUniqueJobs)
|
101
|
+
.to have_key(unique_key)
|
102
|
+
.for_seconds(20)
|
103
|
+
.with_value(jid)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
after { SidekiqUniqueJobs::Util.del('*', 1000, false) }
|
108
|
+
|
109
|
+
let(:expected) do
|
110
|
+
<<~EOS
|
111
|
+
Found 3 keys matching '#{pattern}':
|
112
|
+
uniquejobs:abcdefab uniquejobs:defghayl uniquejobs:poilkij
|
113
|
+
EOS
|
114
|
+
end
|
115
|
+
specify do
|
116
|
+
expect(output).to eq(expected)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '.del' do
|
122
|
+
let(:expected) do
|
123
|
+
<<~EOS
|
124
|
+
Deleted 1 keys matching '*'
|
125
|
+
EOS
|
126
|
+
end
|
127
|
+
|
128
|
+
before do
|
129
|
+
SidekiqUniqueJobs::Scripts::AcquireLock.execute(nil, unique_key, jid, max_lock_time)
|
130
|
+
expect(SidekiqUniqueJobs)
|
131
|
+
.to have_key(unique_key)
|
132
|
+
.for_seconds(1)
|
133
|
+
.with_value(jid)
|
134
|
+
end
|
135
|
+
|
136
|
+
specify do
|
137
|
+
output = capture(:stdout) { described_class.start(%w[del * --no-dry-run --count 1000]) }
|
138
|
+
expect(output).to eq(expected)
|
139
|
+
expect(SidekiqUniqueJobs).not_to have_key(unique_key)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '.expire' do
|
144
|
+
before do
|
145
|
+
SidekiqUniqueJobs::Scripts::AcquireLock.execute(nil, unique_key, jid, max_lock_time)
|
146
|
+
expect(SidekiqUniqueJobs)
|
147
|
+
.to have_key(unique_key)
|
148
|
+
.for_seconds(1)
|
149
|
+
.with_value(jid)
|
150
|
+
end
|
151
|
+
let(:expected) do
|
152
|
+
<<~EOS
|
153
|
+
Removed 1 left overs from redis.
|
154
|
+
uniquejobs:abcdefab
|
155
|
+
EOS
|
156
|
+
end
|
157
|
+
|
158
|
+
specify do
|
159
|
+
sleep 1
|
160
|
+
output = capture(:stdout) { described_class.start(%w[expire]) }
|
161
|
+
expect(output).to eq(expected)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '.console' do
|
166
|
+
let(:expected) do
|
167
|
+
<<~EOS
|
168
|
+
Use `keys '*', 1000 to display the first 1000 unique keys matching '*'
|
169
|
+
Use `del '*', 1000, true (default) to see how many keys would be deleted for the pattern '*'
|
170
|
+
Use `del '*', 1000, false to delete the first 1000 keys matching '*'
|
171
|
+
EOS
|
172
|
+
end
|
173
|
+
let(:output) { capture(:stdout) { described_class.start(%w[console]) } }
|
174
|
+
|
175
|
+
specify do
|
176
|
+
expect(Object).to receive(:include).with(SidekiqUniqueJobs::Util).and_return(true)
|
177
|
+
console = double(:console)
|
178
|
+
allow_any_instance_of(SidekiqUniqueJobs::Cli).to receive(:console_class).and_return(console)
|
179
|
+
allow(console).to receive(:start)
|
180
|
+
expect(output).to eq(expected)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -9,14 +9,6 @@ RSpec.describe SidekiqUniqueJobs::Client::Middleware do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe 'with real redis' do
|
12
|
-
before do
|
13
|
-
if defined?(Sidekiq::Extensions) && Sidekiq::Extensions.respond_to?(:enable_delay!)
|
14
|
-
Sidekiq::Extensions.enable_delay!
|
15
|
-
end
|
16
|
-
Sidekiq.redis = REDIS
|
17
|
-
Sidekiq.redis(&:flushdb)
|
18
|
-
end
|
19
|
-
|
20
12
|
describe 'when a job is already scheduled' do
|
21
13
|
it 'processes jobs properly' do
|
22
14
|
Sidekiq::Testing.disable! do
|
@@ -288,7 +280,7 @@ RSpec.describe SidekiqUniqueJobs::Client::Middleware do
|
|
288
280
|
end
|
289
281
|
|
290
282
|
it 'does not log duplicate payload when config turned off' do
|
291
|
-
expect(
|
283
|
+
expect(SidekiqUniqueJobs.logger).to_not receive(:warn).with(/^payload is not unique/)
|
292
284
|
|
293
285
|
UntilExecutedJob.sidekiq_options log_duplicate_payload: false
|
294
286
|
|
@@ -15,8 +15,6 @@ RSpec.describe SidekiqUniqueJobs::Lock::UntilAndWhileExecuting do
|
|
15
15
|
|
16
16
|
describe '#execute' do
|
17
17
|
before do
|
18
|
-
Sidekiq.redis(&:flushdb)
|
19
|
-
Sidekiq::Worker.clear_all
|
20
18
|
subject.lock(:client)
|
21
19
|
end
|
22
20
|
let(:runtime_lock) { SidekiqUniqueJobs::Lock::WhileExecuting.new(item, nil) }
|
@@ -14,7 +14,6 @@ RSpec.describe SidekiqUniqueJobs::ScriptMock, ruby_ver: '>= 2.4.1' do
|
|
14
14
|
ANOTHER_JID ||= 'anotherjid'.freeze
|
15
15
|
|
16
16
|
before do
|
17
|
-
Sidekiq.redis(&:flushdb)
|
18
17
|
SidekiqUniqueJobs.configure do |config|
|
19
18
|
config.redis_test_mode = :mock
|
20
19
|
end
|
@@ -36,7 +35,6 @@ RSpec.describe SidekiqUniqueJobs::ScriptMock, ruby_ver: '>= 2.4.1' do
|
|
36
35
|
SidekiqUniqueJobs.configure do |config|
|
37
36
|
config.redis_test_mode = :redis
|
38
37
|
end
|
39
|
-
Sidekiq.redis(&:flushdb)
|
40
38
|
end
|
41
39
|
|
42
40
|
subject { SidekiqUniqueJobs::Scripts }
|
@@ -54,7 +52,7 @@ RSpec.describe SidekiqUniqueJobs::ScriptMock, ruby_ver: '>= 2.4.1' do
|
|
54
52
|
specify { expect(lock_for).to eq(1) }
|
55
53
|
specify do
|
56
54
|
expect(lock_for(1)).to eq(1)
|
57
|
-
expect(
|
55
|
+
expect(SidekiqUniqueJobs)
|
58
56
|
.to have_key(UNIQUE_KEY)
|
59
57
|
.for_seconds(1)
|
60
58
|
.with_value('fuckit')
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe SidekiqUniqueJobs::Scripts::AcquireLock do
|
4
|
+
let(:redis_pool) { nil }
|
5
|
+
let(:jid) { 'abcdefab' }
|
6
|
+
let(:unique_key) { 'uniquejobs:123asdasd2134' }
|
7
|
+
let(:max_lock_time) { 1 }
|
8
|
+
|
9
|
+
describe '.execute' do
|
10
|
+
subject { instance_double(described_class) }
|
11
|
+
|
12
|
+
it 'delegates to instance' do
|
13
|
+
expect(described_class).to receive(:new)
|
14
|
+
.with(redis_pool, unique_key, jid, max_lock_time)
|
15
|
+
.and_return(subject)
|
16
|
+
expect(subject).to receive(:execute).and_return(true)
|
17
|
+
|
18
|
+
described_class.execute(redis_pool, unique_key, jid, max_lock_time)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#execute' do
|
23
|
+
context 'when job is unique' do
|
24
|
+
def execute(myjid = jid, key = unique_key, max_lock = max_lock_time)
|
25
|
+
described_class.execute(
|
26
|
+
redis_pool,
|
27
|
+
key,
|
28
|
+
myjid,
|
29
|
+
max_lock
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
specify { expect(execute(jid, unique_key, max_lock_time)).to eq(true) }
|
34
|
+
specify do
|
35
|
+
expect(execute(jid, unique_key, max_lock_time)).to eq(true)
|
36
|
+
expect(SidekiqUniqueJobs)
|
37
|
+
.to have_key(unique_key)
|
38
|
+
.for_seconds(max_lock_time)
|
39
|
+
.with_value('abcdefab')
|
40
|
+
sleep 0.5
|
41
|
+
expect(execute).to eq(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when a unique_key exists for another jid' do
|
45
|
+
before { expect(execute(jid, unique_key, 10)).to eq(true) }
|
46
|
+
specify { expect(execute('anotherjid', unique_key, 5)).to eq(false) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe SidekiqUniqueJobs::Scripts::ReleaseLock do
|
4
|
+
let(:redis_pool) { nil }
|
5
|
+
let(:jid) { 'abcdefab' }
|
6
|
+
let(:unique_key) { 'uniquejobs:123asdasd2134' }
|
7
|
+
let(:max_lock_time) { 1 }
|
8
|
+
|
9
|
+
describe '.execute' do
|
10
|
+
subject { instance_double(described_class) }
|
11
|
+
|
12
|
+
it 'delegates to instance' do
|
13
|
+
expect(described_class).to receive(:new)
|
14
|
+
.with(redis_pool, unique_key, jid)
|
15
|
+
.and_return(subject)
|
16
|
+
expect(subject).to receive(:execute).and_return(true)
|
17
|
+
|
18
|
+
described_class.execute(redis_pool, unique_key, jid)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#execute' do
|
23
|
+
context 'when exists' do
|
24
|
+
subject { described_class.execute(redis_pool, unique_key, jid) }
|
25
|
+
|
26
|
+
before do
|
27
|
+
SidekiqUniqueJobs::Scripts::AcquireLock.execute(redis_pool, unique_key, jid, max_lock_time)
|
28
|
+
end
|
29
|
+
|
30
|
+
specify do
|
31
|
+
expect(SidekiqUniqueJobs)
|
32
|
+
.to have_key(unique_key)
|
33
|
+
.for_seconds(max_lock_time)
|
34
|
+
.with_value(jid)
|
35
|
+
|
36
|
+
expect(subject).to eq(true)
|
37
|
+
expect(SidekiqUniqueJobs).not_to have_key(unique_key)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|