freekiqs 4.0.0 → 4.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 059f622d989114ee072fabcef7902ca2a6a83e82
4
- data.tar.gz: 73388b860f4c623fa692799e8ac0531d2f2c5265
3
+ metadata.gz: 0a7ee677e8d100c7405dc91f9d4c341d7f7a8986
4
+ data.tar.gz: 0281092a960af50ca75ed3b03370bd7a86b1ae17
5
5
  SHA512:
6
- metadata.gz: 12f4de057c24bc156311010179d37ab080f2c23efab81f9ea0743c249e4eaa477d4d4cf328d289bc60325d9b7f7cc59967454038801129c984bc1ae4dfa1d523
7
- data.tar.gz: 4d6649df9cbee156a1dbcffd51823d07b0df17ed45ce7f10e76a5a228b5d0fdcf358e90478ac8ce3336c49ef1184d019378e2ae20ca8624539706fb8d6251a58
6
+ metadata.gz: 35984aad9ab4d0fec1d33f07469ba0f82ff83a5b3cdeaecb39f1bbdb42f56ec9566df45c8735970034f99294919790481983047141a89750118845d46d7a3ad9
7
+ data.tar.gz: d932c98cefbdcc672d00c2e85ae16a86b6b4b70cb719403a462b8981dacd22fca4f0cbaa7cf90735879cbd8cbc8efe3a5f221eaa9d8cd033962e0efd841dbaf4
data/README.md CHANGED
@@ -50,6 +50,42 @@ Example:
50
50
  end
51
51
  ```
52
52
 
53
+ An array of specific errors to be freekiq'd can be defined in the `freekiq_for` option.
54
+
55
+ Example:
56
+ ``` ruby
57
+ class MyWorker
58
+ include Sidekiq::Worker
59
+ sidekiq_options freekiqs: 1, freekiq_for: ['MyError']
60
+ #
61
+ def perform(param)
62
+ ...
63
+ end
64
+ end
65
+ ```
66
+ In this case, if MyWorker fails with a MyError it will get 1 freekiq.
67
+ All other errors thrown by this worker will get no freekiqs.
68
+
69
+
70
+ If a `freekiq_for` contains a class name as a constant, any exception is that class
71
+ *or a subclass* of that class will get freekiq'd.
72
+
73
+ Example:
74
+ ``` ruby
75
+ class SubMyError < MyError; end
76
+
77
+ class MyWorker
78
+ include Sidekiq::Worker
79
+ sidekiq_options freekiqs: 1, freekiq_for: [MyError]
80
+ #
81
+ def perform(param)
82
+ ...
83
+ end
84
+ end
85
+ ```
86
+ If MyWorker throws a SubMyError or MyError, it will get freekiq'd.
87
+
88
+
53
89
  ## Overview
54
90
 
55
91
  Up to the configured number of "freekiqs", catch exceptions thrown
data/freekiqs.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = 'freekiqs'
5
- gem.version = '4.0.0'
5
+ gem.version = '4.1.0'
6
6
  gem.authors = ['Rob Lewis']
7
7
  gem.email = ['rob@bookbub.com']
8
8
  gem.summary = 'Sidekiq middleware extending RetryJobs to allow silient errors.'
@@ -12,12 +12,13 @@ module Sidekiq
12
12
 
13
13
  def initialize(opts={})
14
14
  @default_freekiqs = opts[:freekiqs]
15
+ @default_freekiq_for = opts[:freekiq_for]
15
16
  end
16
17
 
17
18
  def call(worker, msg, queue)
18
19
  yield
19
20
  rescue => ex
20
- freekiqs = get_freekiqs_if_enabled(worker, msg)
21
+ freekiqs = get_freekiqs_if_enabled(worker, msg, ex)
21
22
  if freekiqs
22
23
  if msg['retry_count'].nil? || msg['retry_count'] < freekiqs-1
23
24
  begin
@@ -34,20 +35,49 @@ module Sidekiq
34
35
  raise ex
35
36
  end
36
37
 
37
- def get_freekiqs_if_enabled(worker, msg)
38
+ def get_freekiqs_if_enabled(worker, msg, ex)
38
39
  freekiqs = nil
39
40
  if msg['retry']
40
41
  if worker.class.get_sidekiq_options['freekiqs'] != false
42
+ errors = get_freekiq_errors(worker)
41
43
  if worker.class.get_sidekiq_options['freekiqs']
42
- freekiqs = worker.class.get_sidekiq_options['freekiqs'].to_i
44
+ freekiqs = get_freekiqs(worker.class.get_sidekiq_options['freekiqs'], ex, errors)
43
45
  elsif @default_freekiqs
44
- freekiqs = @default_freekiqs.to_i
46
+ freekiqs = get_freekiqs(@default_freekiqs, ex, errors)
45
47
  end
46
48
  end
47
49
  end
48
50
  freekiqs
49
51
  end
50
52
 
53
+ def get_freekiq_errors(worker)
54
+ if worker.class.get_sidekiq_options['freekiq_for']
55
+ worker.class.get_sidekiq_options['freekiq_for']
56
+ elsif @default_freekiq_for
57
+ @default_freekiq_for
58
+ end
59
+ end
60
+
61
+ def get_freekiqs(freekiqs, ex, errors)
62
+ if errors
63
+ if error_whitelisted?(ex, errors)
64
+ freekiqs.to_i
65
+ end
66
+ else
67
+ freekiqs.to_i
68
+ end
69
+ end
70
+
71
+ def error_whitelisted?(ex, errors)
72
+ errors.any? do |error|
73
+ if error.respond_to?(:name) && error.is_a?(Class)
74
+ ex.class == error || ex.class < error
75
+ else
76
+ ex.class.name == error
77
+ end
78
+ end
79
+ end
80
+
51
81
  def self.callback
52
82
  @@callback
53
83
  end
@@ -2,6 +2,52 @@ require 'spec_helper'
2
2
  require 'sidekiq/cli'
3
3
  require 'sidekiq/middleware/server/retry_jobs'
4
4
 
5
+ shared_examples_for 'it should have 2 freekiqs for an ArgumentError' do
6
+ it 'throws Freekiq exception for specified number of free kiqs' do
7
+ expect {
8
+ handler.invoke(worker, job, 'default') do
9
+ raise ArgumentError, 'overlooked'
10
+ end
11
+ }.to raise_error(Sidekiq::FreekiqException, 'overlooked')
12
+ expect(job['retry_count']).to eq(0)
13
+ expect(job['error_class']).to eq('Sidekiq::FreekiqException')
14
+ expect(job['error_message']).to eq('overlooked')
15
+ expect {
16
+ handler.invoke(worker, job, 'default') do
17
+ raise ArgumentError, 'overlooked'
18
+ end
19
+ }.to raise_error(Sidekiq::FreekiqException, 'overlooked')
20
+ expect(job['retry_count']).to eq(1)
21
+ expect(job['error_class']).to eq('Sidekiq::FreekiqException')
22
+ expect(job['error_message']).to eq('overlooked')
23
+ expect {
24
+ handler.invoke(worker, job, 'default') do
25
+ raise ArgumentError, 'not overlooked'
26
+ end
27
+ }.to raise_error(ArgumentError, 'not overlooked')
28
+ expect(job['retry_count']).to eq(2)
29
+ expect(job['error_class']).to eq('ArgumentError')
30
+ expect(job['error_message']).to eq('not overlooked')
31
+ expect(Sidekiq::RetrySet.new.size).to eq(3)
32
+ expect(Sidekiq::DeadSet.new.size).to eq(0)
33
+ end
34
+ end
35
+
36
+ shared_examples_for 'it should have 0 freekiqs for an ArgumentError' do
37
+ it 'raises the original error' do
38
+ expect {
39
+ handler.invoke(worker, job, 'default') do
40
+ raise ArgumentError, 'not overlooked'
41
+ end
42
+ }.to raise_error(ArgumentError, 'not overlooked')
43
+ expect(job['retry_count']).to eq(0)
44
+ expect(job['error_class']).to eq('ArgumentError')
45
+ expect(job['error_message']).to eq('not overlooked')
46
+ expect(Sidekiq::RetrySet.new.size).to eq(1)
47
+ expect(Sidekiq::DeadSet.new.size).to eq(0)
48
+ end
49
+ end
50
+
5
51
  describe Sidekiq::Middleware::Server::Freekiqs do
6
52
  class DummyWorkerPlain
7
53
  include Sidekiq::Worker
@@ -10,10 +56,6 @@ describe Sidekiq::Middleware::Server::Freekiqs do
10
56
  include Sidekiq::Worker
11
57
  sidekiq_options freekiqs: 2
12
58
  end
13
- class DummyWorkerWithFreekiqsDisabled
14
- include Sidekiq::Worker
15
- sidekiq_options freekiqs: false
16
- end
17
59
 
18
60
  def build_handler_chain(freekiq_options={}, retry_options={})
19
61
  Sidekiq::Middleware::Chain.new do |chain|
@@ -37,7 +79,6 @@ describe Sidekiq::Middleware::Server::Freekiqs do
37
79
 
38
80
  let(:worker_plain) { DummyWorkerPlain.new }
39
81
  let(:worker_with_freekiqs_enabled) { DummyWorkerWithFreekiqsEnabled.new }
40
- let(:worker_with_freekiqs_disabled) { DummyWorkerWithFreekiqsDisabled.new }
41
82
 
42
83
  it 'requires RetryJobs to update retry_count' do
43
84
  handler = Sidekiq::Middleware::Server::RetryJobs.new
@@ -57,42 +98,30 @@ describe Sidekiq::Middleware::Server::Freekiqs do
57
98
  expect(job['retry_count']).to eq(1)
58
99
  end
59
100
 
60
- it 'throws Freekiq exception for specified number of free kiqs' do
101
+ it 'should execute a defined callback' do
102
+ Sidekiq::Middleware::Server::Freekiqs::callback = ->(worker, msg, queue) do
103
+ return true
104
+ end
105
+
61
106
  handler = build_handler_chain
62
107
  worker = worker_with_freekiqs_enabled
63
108
  job = build_job
64
109
 
110
+ expect(Sidekiq::Middleware::Server::Freekiqs::callback).to receive(:call)
65
111
  expect {
66
112
  handler.invoke(worker, job, 'default') do
67
113
  raise ArgumentError, 'overlooked'
68
114
  end
69
115
  }.to raise_error(Sidekiq::FreekiqException, 'overlooked')
70
- expect(job['retry_count']).to eq(0)
71
- expect(job['error_class']).to eq('Sidekiq::FreekiqException')
72
- expect(job['error_message']).to eq('overlooked')
73
- expect {
74
- handler.invoke(worker, job, 'default') do
75
- raise ArgumentError, 'overlooked'
76
- end
77
- }.to raise_error(Sidekiq::FreekiqException, 'overlooked')
78
- expect(job['retry_count']).to eq(1)
79
- expect(job['error_class']).to eq('Sidekiq::FreekiqException')
80
- expect(job['error_message']).to eq('overlooked')
81
- expect {
82
- handler.invoke(worker, job, 'default') do
83
- raise ArgumentError, 'not overlooked'
84
- end
85
- }.to raise_error(ArgumentError, 'not overlooked')
86
- expect(job['retry_count']).to eq(2)
87
- expect(job['error_class']).to eq('ArgumentError')
88
- expect(job['error_message']).to eq('not overlooked')
89
- expect(Sidekiq::RetrySet.new.size).to eq(3)
90
- expect(Sidekiq::DeadSet.new.size).to eq(0)
91
116
  end
92
117
 
93
- it 'allows a freekiqs option in initializer' do
94
- handler = build_handler_chain(freekiqs: 1)
95
- worker = worker_plain
118
+ it 'should still raise FreekiqException if the callback fails' do
119
+ Sidekiq::Middleware::Server::Freekiqs::callback = ->(worker, msg, queue) do
120
+ raise 'callback error'
121
+ end
122
+
123
+ handler = build_handler_chain
124
+ worker = worker_with_freekiqs_enabled
96
125
  job = build_job
97
126
 
98
127
  expect {
@@ -100,101 +129,201 @@ describe Sidekiq::Middleware::Server::Freekiqs do
100
129
  raise ArgumentError, 'overlooked'
101
130
  end
102
131
  }.to raise_error(Sidekiq::FreekiqException, 'overlooked')
103
- expect(job['retry_count']).to eq(0)
104
- expect(job['error_class']).to eq('Sidekiq::FreekiqException')
105
- expect(job['error_message']).to eq('overlooked')
106
- expect {
107
- handler.invoke(worker, job, 'default') do
108
- raise ArgumentError, 'not overlooked'
132
+ end
133
+
134
+ describe 'with nothing explicitly enabled' do
135
+ it_behaves_like 'it should have 0 freekiqs for an ArgumentError' do
136
+ let (:handler) { build_handler_chain }
137
+ let (:job) { build_job }
138
+ let (:worker) do
139
+ Class.new do
140
+ include Sidekiq::Worker
141
+ end.new
109
142
  end
110
- }.to raise_error(ArgumentError, 'not overlooked')
111
- expect(job['retry_count']).to eq(1)
112
- expect(job['error_class']).to eq('ArgumentError')
113
- expect(job['error_message']).to eq('not overlooked')
114
- expect(Sidekiq::RetrySet.new.size).to eq(2)
115
- expect(Sidekiq::DeadSet.new.size).to eq(0)
143
+ end
116
144
  end
117
145
 
118
- it 'allows explicitly disabling freekiqs' do
119
- handler = build_handler_chain(freekiqs: 3)
120
- worker = worker_with_freekiqs_disabled
121
- job = build_job
146
+ describe 'with freekiqs explicitly disabled' do
147
+ it_behaves_like 'it should have 0 freekiqs for an ArgumentError' do
148
+ let (:handler) { build_handler_chain(freekiqs: 3) }
149
+ let (:job) { build_job }
150
+ let (:worker) do
151
+ Class.new do
152
+ include Sidekiq::Worker
153
+ sidekiq_options freekiqs: false
154
+ end.new
155
+ end
156
+ end
157
+ end
122
158
 
123
- expect {
124
- handler.invoke(worker, job, 'default') do
125
- raise ArgumentError, 'not overlooked'
159
+ describe 'with 2 freekiqs in the initializer' do
160
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
161
+ let (:handler) { build_handler_chain(freekiqs: 2) }
162
+ let (:job) { build_job }
163
+ let (:worker) do
164
+ Class.new do
165
+ include Sidekiq::Worker
166
+ end.new
126
167
  end
127
- }.to raise_error(ArgumentError, 'not overlooked')
128
- expect(job['retry_count']).to eq(0)
129
- expect(job['error_class']).to eq('ArgumentError')
130
- expect(job['error_message']).to eq('not overlooked')
131
- expect(Sidekiq::RetrySet.new.size).to eq(1)
132
- expect(Sidekiq::DeadSet.new.size).to eq(0)
168
+ end
133
169
  end
134
170
 
135
- it 'does nothing if not explicitly enabled' do
136
- handler = build_handler_chain
137
- worker = worker_plain
138
- job = build_job
171
+ describe 'with 2 freekiqs in the worker' do
172
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
173
+ let (:handler) { build_handler_chain }
174
+ let (:job) { build_job }
175
+ let (:worker) do
176
+ Class.new do
177
+ include Sidekiq::Worker
178
+ sidekiq_options freekiqs: 2
179
+ end.new
180
+ end
181
+ end
182
+ end
139
183
 
140
- expect {
141
- handler.invoke(worker, job, 'default') do
142
- raise ArgumentError, 'not overlooked'
184
+ describe 'with freekiq_for ArgumentError and 2 freekiqs in worker' do
185
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
186
+ let (:handler) { build_handler_chain }
187
+ let (:job) { build_job }
188
+ let (:worker) do
189
+ Class.new do
190
+ include Sidekiq::Worker
191
+ sidekiq_options freekiqs: 2, freekiq_for: [ArgumentError]
192
+ end.new
143
193
  end
144
- }.to raise_error(ArgumentError, 'not overlooked')
145
- expect(job['retry_count']).to eq(0)
146
- expect(job['error_class']).to eq('ArgumentError')
147
- expect(job['error_message']).to eq('not overlooked')
194
+ end
148
195
  end
149
196
 
150
- it 'does nothing if retries disabled' do
151
- handler = build_handler_chain
152
- worker = worker_with_freekiqs_enabled
153
- job = build_job('retry' => false)
197
+ describe 'with freekiq_for ArgumentError in worker and no freekiqs' do
198
+ it_behaves_like 'it should have 0 freekiqs for an ArgumentError' do
199
+ let (:handler) { build_handler_chain }
200
+ let (:job) { build_job }
201
+ let (:worker) do
202
+ Class.new do
203
+ include Sidekiq::Worker
204
+ class NonArgumentError < StandardError; end
205
+ sidekiq_options freekiqs: 2, freekiq_for: [NonArgumentError]
206
+ end.new
207
+ end
208
+ end
209
+ end
154
210
 
155
- expect {
156
- handler.invoke(worker, job, 'default') do
157
- raise ArgumentError, 'not overlooked'
211
+ describe 'with freekiq_for ArgumentError in worker and 2 freekiqs in initializer' do
212
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
213
+ let (:handler) { build_handler_chain(freekiqs: 2) }
214
+ let (:job) { build_job }
215
+ let (:worker) do
216
+ Class.new do
217
+ include Sidekiq::Worker
218
+ sidekiq_options freekiq_for: [ArgumentError]
219
+ end.new
158
220
  end
159
- }.to raise_error(ArgumentError, 'not overlooked')
160
- expect(job['retry_count']).to be_nil
161
- expect(job['error_class']).to be_nil
162
- expect(job['error_message']).to be_nil
163
- expect(job['error_message']).to be_nil
164
- expect(Sidekiq::RetrySet.new.size).to eq(0)
165
- expect(Sidekiq::DeadSet.new.size).to eq(0)
221
+ end
166
222
  end
167
223
 
168
- it 'should execute a defined callback' do
169
- Sidekiq::Middleware::Server::Freekiqs::callback = ->(worker, msg, queue) do
170
- return true
224
+ describe 'with freekiq_for ArgumentError in initializer and 2 freekiqs in initializer' do
225
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
226
+ let (:handler) { build_handler_chain(freekiqs: 2, freekiq_for: [ArgumentError]) }
227
+ let (:job) { build_job }
228
+ let (:worker) do
229
+ Class.new do
230
+ include Sidekiq::Worker
231
+ end.new
232
+ end
171
233
  end
234
+ end
172
235
 
173
- handler = build_handler_chain
174
- worker = worker_with_freekiqs_enabled
175
- job = build_job
236
+ describe 'with freekiq_for ArgumentError in initializer and 2 freekiqs in worker' do
237
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
238
+ let (:handler) { build_handler_chain(freekiq_for: [ArgumentError]) }
239
+ let (:job) { build_job }
240
+ let (:worker) do
241
+ Class.new do
242
+ include Sidekiq::Worker
243
+ sidekiq_options freekiqs: 2
244
+ end.new
245
+ end
246
+ end
247
+ end
176
248
 
177
- expect(Sidekiq::Middleware::Server::Freekiqs::callback).to receive(:call)
178
- expect {
179
- handler.invoke(worker, job, 'default') do
180
- raise ArgumentError, 'overlooked'
249
+ describe 'with freekiq_for NonArgumentError in worker and 2 freekiqs in worker' do
250
+ it_behaves_like 'it should have 0 freekiqs for an ArgumentError' do
251
+ let (:handler) { build_handler_chain }
252
+ let (:job) { build_job }
253
+ let (:worker) do
254
+ Class.new do
255
+ include Sidekiq::Worker
256
+ class NonArgumentError < StandardError; end
257
+ sidekiq_options freekiqs: 2, freekiq_for: [NonArgumentError]
258
+ end.new
181
259
  end
182
- }.to raise_error(Sidekiq::FreekiqException, 'overlooked')
260
+ end
183
261
  end
184
262
 
185
- it 'should still raise FreekiqException if the callback fails' do
186
- Sidekiq::Middleware::Server::Freekiqs::callback = ->(worker, msg, queue) do
187
- raise 'callback error'
263
+ describe 'with freekiq_for NonArgumentError in initializer and 2 freekiqs in worker' do
264
+ it_behaves_like 'it should have 0 freekiqs for an ArgumentError' do
265
+ let (:handler) { build_handler_chain(freekiq_for: [NonArgumentError]) }
266
+ let (:job) { build_job }
267
+ let (:worker) do
268
+ Class.new do
269
+ include Sidekiq::Worker
270
+ class NonArgumentError < StandardError; end
271
+ sidekiq_options freekiqs: 2
272
+ end.new
273
+ end
188
274
  end
275
+ end
189
276
 
190
- handler = build_handler_chain
191
- worker = worker_with_freekiqs_enabled
192
- job = build_job
277
+ describe 'with freekiq_for NonArgumentError in initializer and freekiq for ArgumentError in worker' do
278
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
279
+ let (:handler) { build_handler_chain(freekiq_for: [NonArgumentError]) }
280
+ let (:job) { build_job }
281
+ let (:worker) do
282
+ Class.new do
283
+ include Sidekiq::Worker
284
+ sidekiq_options freekiqs: 2, freekiq_for: [ArgumentError]
285
+ end.new
286
+ end
287
+ end
288
+ end
193
289
 
194
- expect {
195
- handler.invoke(worker, job, 'default') do
196
- raise ArgumentError, 'overlooked'
290
+ describe 'with freekiq_for ArgumentError in initializer and freekiq for NonArgumentError in worker' do
291
+ it_behaves_like 'it should have 0 freekiqs for an ArgumentError' do
292
+ let (:handler) { build_handler_chain(freekiq_for: [ArgumentError]) }
293
+ let (:job) { build_job }
294
+ let (:worker) do
295
+ Class.new do
296
+ include Sidekiq::Worker
297
+ class NonArgumentError < StandardError; end
298
+ sidekiq_options freekiqs: 2, freekiq_for: [NonArgumentError]
299
+ end.new
197
300
  end
198
- }.to raise_error(Sidekiq::FreekiqException, 'overlooked')
301
+ end
302
+ end
303
+
304
+ describe 'with freekiq_for ArgumentError as a string' do
305
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
306
+ let (:handler) { build_handler_chain }
307
+ let (:job) { build_job }
308
+ let (:worker) do
309
+ Class.new do
310
+ include Sidekiq::Worker
311
+ sidekiq_options freekiqs: 2, freekiq_for: ['ArgumentError']
312
+ end.new
313
+ end
314
+ end
315
+ end
316
+
317
+ describe 'with freekiq_for the super class of ArgumentError' do
318
+ it_behaves_like 'it should have 2 freekiqs for an ArgumentError' do
319
+ let (:handler) { build_handler_chain }
320
+ let (:job) { build_job }
321
+ let (:worker) do
322
+ Class.new do
323
+ include Sidekiq::Worker
324
+ sidekiq_options freekiqs: 2, freekiq_for: [ArgumentError.superclass]
325
+ end.new
326
+ end
327
+ end
199
328
  end
200
329
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freekiqs
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Lewis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-21 00:00:00.000000000 Z
11
+ date: 2016-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq