sidekiq-unique-jobs 3.0.13 → 3.0.14

Sign up to get free protection for your applications and to get access to all the features.

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