freekiqs 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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