sidekiq-retries 0.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.
- data/.gitignore +17 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +27 -0
- data/README.md +36 -0
- data/Rakefile +6 -0
- data/lib/sidekiq/retries.rb +15 -0
- data/lib/sidekiq/retries/errors.rb +25 -0
- data/lib/sidekiq/retries/server/middleware.rb +73 -0
- data/lib/sidekiq/retries/version.rb +5 -0
- data/sidekiq-retries.gemspec +31 -0
- data/spec/lib/sidekiq/retries/server/middleware_spec.rb +52 -0
- metadata +208 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
License
|
2
|
+
-------
|
3
|
+
Copyright (c) 2013, GovDelivery, Inc.
|
4
|
+
|
5
|
+
All rights reserved.
|
6
|
+
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
9
|
+
* Redistributions of source code must retain the above copyright notice, this
|
10
|
+
list of conditions and the following disclaimer.
|
11
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
13
|
+
and/or other materials provided with the distribution.
|
14
|
+
* Neither the name of GovDelivery nor the names of its contributors may be used
|
15
|
+
to endorse or promote products derived from this software without specific
|
16
|
+
prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Sidekiq::Retries
|
2
|
+
|
3
|
+
This subclasses the stock Sidekiq retries middleware to give you some additional options to conditionally retry jobs
|
4
|
+
irrespective of whether retries are enabled for the job.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'sidekiq-retries'
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
class MyWorker
|
15
|
+
include Sidekiq::Worker
|
16
|
+
sidekiq_options retry: false # or retry: 25, or the default...
|
17
|
+
|
18
|
+
def perform
|
19
|
+
#force a retry even if retry: false using default retry options
|
20
|
+
raise Sidekiq::Retries::Retry.new(RuntimeError.new('whatever happened'))
|
21
|
+
|
22
|
+
#force a retry even if retry: false using a specific max_retries
|
23
|
+
raise Sidekiq::Retries::Retry.new(RuntimeError.new('whatever happened'), 10)
|
24
|
+
|
25
|
+
#if e.g. retries: true or retries: 10, skip it anyway
|
26
|
+
raise Sidekiq::Retries::Fail.new(RuntimeError.new('whatever happened'))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
## Contributing
|
31
|
+
|
32
|
+
1. Fork it
|
33
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
34
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
35
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
36
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "sidekiq/retries/version"
|
2
|
+
require 'sidekiq/retries/server/middleware'
|
3
|
+
require 'sidekiq/retries/errors'
|
4
|
+
|
5
|
+
Sidekiq.configure_server do |config|
|
6
|
+
require 'sidekiq/middleware/server/retry_jobs'
|
7
|
+
require 'sidekiq/retries/server/middleware'
|
8
|
+
|
9
|
+
stock_middleware = Sidekiq::Middleware::Server::RetryJobs
|
10
|
+
|
11
|
+
config.server_middleware do |chain|
|
12
|
+
chain.insert_before stock_middleware, Sidekiq::Retries::Server::Middleware
|
13
|
+
chain.remove stock_middleware
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module Retries
|
3
|
+
class RetryError < StandardError
|
4
|
+
attr_accessor :cause
|
5
|
+
|
6
|
+
def initialize(cause, msg=nil)
|
7
|
+
super(msg)
|
8
|
+
self.cause=cause
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Retry < RetryError
|
13
|
+
attr_accessor :max_retries
|
14
|
+
|
15
|
+
def initialize(cause, max_retries=nil, msg=nil)
|
16
|
+
super(cause, msg)
|
17
|
+
self.max_retries = max_retries
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Fail < RetryError
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
require 'sidekiq/middleware/server/retry_jobs'
|
3
|
+
|
4
|
+
module Sidekiq
|
5
|
+
module Retries
|
6
|
+
module Server
|
7
|
+
class Middleware < Sidekiq::Middleware::Server::RetryJobs
|
8
|
+
|
9
|
+
def call(worker, msg, queue)
|
10
|
+
yield
|
11
|
+
rescue Sidekiq::Shutdown
|
12
|
+
# ignore, will be pushed back onto queue during hard_shutdown
|
13
|
+
raise
|
14
|
+
rescue Sidekiq::Retries::Retry => e
|
15
|
+
# force a retry (for workers that have retries disabled)
|
16
|
+
msg['retry'] ||= e.max_retries
|
17
|
+
attempt_retry(worker, msg, queue, e.cause)
|
18
|
+
raise e.cause
|
19
|
+
rescue Sidekiq::Retries::Fail => e
|
20
|
+
# don't retry this message (for workers that retry by default)
|
21
|
+
raise e.cause
|
22
|
+
rescue Exception => e
|
23
|
+
attempt_retry(worker, msg, queue, e) if msg['retry']
|
24
|
+
raise e
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# This is the default Sidekiq 2.17.x retry logic
|
30
|
+
def attempt_retry(worker, msg, queue, e)
|
31
|
+
max_retry_attempts = retry_attempts_from(msg['retry'], @max_retries)
|
32
|
+
|
33
|
+
msg['queue'] = if msg['retry_queue']
|
34
|
+
msg['retry_queue']
|
35
|
+
else
|
36
|
+
queue
|
37
|
+
end
|
38
|
+
msg['error_message'] = e.message
|
39
|
+
msg['error_class'] = e.class.name
|
40
|
+
count = if msg['retry_count']
|
41
|
+
msg['retried_at'] = Time.now.utc
|
42
|
+
msg['retry_count'] += 1
|
43
|
+
else
|
44
|
+
msg['failed_at'] = Time.now.utc
|
45
|
+
msg['retry_count'] = 0
|
46
|
+
end
|
47
|
+
|
48
|
+
if msg['backtrace'] == true
|
49
|
+
msg['error_backtrace'] = e.backtrace
|
50
|
+
elsif msg['backtrace'] == false
|
51
|
+
# do nothing
|
52
|
+
elsif msg['backtrace'].to_i != 0
|
53
|
+
msg['error_backtrace'] = e.backtrace[0..msg['backtrace'].to_i]
|
54
|
+
end
|
55
|
+
|
56
|
+
if count < max_retry_attempts
|
57
|
+
delay = delay_for(worker, count)
|
58
|
+
logger.debug { "Failure! Retry #{count} in #{delay} seconds" }
|
59
|
+
retry_at = Time.now.to_f + delay
|
60
|
+
payload = Sidekiq.dump_json(msg)
|
61
|
+
Sidekiq.redis do |conn|
|
62
|
+
conn.zadd('retry', retry_at.to_s, payload)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
# Goodbye dear message, you (re)tried your best I'm sure.
|
66
|
+
retries_exhausted(worker, msg)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sidekiq/retries/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sidekiq-retries"
|
8
|
+
spec.version = Sidekiq::Retries::VERSION
|
9
|
+
spec.authors = ["Benjamin Ortega"]
|
10
|
+
spec.email = ["ben.ortega@gmail.com"]
|
11
|
+
spec.description = %q{Enhanced retry logic for Sidekiq workers}
|
12
|
+
spec.summary = %q{Enhanced retry logic for Sidekiq workers}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
|
24
|
+
spec.add_dependency 'sidekiq', '~>2.17'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
spec.add_development_dependency 'rspec'
|
27
|
+
spec.add_development_dependency 'rspec-mocks'
|
28
|
+
spec.add_development_dependency 'activesupport', '~> 3'
|
29
|
+
spec.add_development_dependency 'simplecov'
|
30
|
+
spec.add_development_dependency 'rspec-sidekiq'
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Retries
|
5
|
+
module Server
|
6
|
+
class RetryWorker
|
7
|
+
include Sidekiq::Worker
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Middleware do
|
11
|
+
let (:handler) { Sidekiq::Retries::Server::Middleware.new }
|
12
|
+
let (:queue) { 'default' }
|
13
|
+
let (:errmsg) { 'oh man what did you do' }
|
14
|
+
|
15
|
+
before do
|
16
|
+
Sidekiq::RetrySet.new.clear
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'a worker without retry' do
|
20
|
+
it 'should retry anyway if we raise a Sidekiq::Retries::Retry' do
|
21
|
+
args = {'class' => 'Bob',
|
22
|
+
'args' => [1],
|
23
|
+
'retry' => false}
|
24
|
+
expect {
|
25
|
+
handler.call(RetryWorker, args, queue) do
|
26
|
+
raise Sidekiq::Retries::Retry.new(RuntimeError.new(errmsg), 10)
|
27
|
+
end
|
28
|
+
}.to raise_error(RuntimeError, errmsg)
|
29
|
+
expect(Sidekiq::RetrySet.new.size).to eq(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'a worker with retry' do
|
35
|
+
it 'should not retry if we raise a Sidekiq::Retries::Fail' do
|
36
|
+
args = {'class' => 'Bob',
|
37
|
+
'args' => [1],
|
38
|
+
'retry' => 2}
|
39
|
+
expect {
|
40
|
+
handler.call(RetryWorker, args, queue) do
|
41
|
+
raise Sidekiq::Retries::Fail.new(RuntimeError.new(errmsg))
|
42
|
+
end
|
43
|
+
}.to raise_error(RuntimeError, errmsg)
|
44
|
+
|
45
|
+
expect(Sidekiq::RetrySet.new.size).to eq(0)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sidekiq-retries
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Benjamin Ortega
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-02-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: sidekiq
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.17'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.17'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec-mocks
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: activesupport
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '3'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: simplecov
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: rspec-sidekiq
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
description: Enhanced retry logic for Sidekiq workers
|
159
|
+
email:
|
160
|
+
- ben.ortega@gmail.com
|
161
|
+
executables: []
|
162
|
+
extensions: []
|
163
|
+
extra_rdoc_files: []
|
164
|
+
files:
|
165
|
+
- .gitignore
|
166
|
+
- Gemfile
|
167
|
+
- LICENSE.txt
|
168
|
+
- README.md
|
169
|
+
- Rakefile
|
170
|
+
- lib/sidekiq/retries.rb
|
171
|
+
- lib/sidekiq/retries/errors.rb
|
172
|
+
- lib/sidekiq/retries/server/middleware.rb
|
173
|
+
- lib/sidekiq/retries/version.rb
|
174
|
+
- sidekiq-retries.gemspec
|
175
|
+
- spec/lib/sidekiq/retries/server/middleware_spec.rb
|
176
|
+
homepage: ''
|
177
|
+
licenses:
|
178
|
+
- MIT
|
179
|
+
post_install_message:
|
180
|
+
rdoc_options: []
|
181
|
+
require_paths:
|
182
|
+
- lib
|
183
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
184
|
+
none: false
|
185
|
+
requirements:
|
186
|
+
- - ! '>='
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0'
|
189
|
+
segments:
|
190
|
+
- 0
|
191
|
+
hash: -4325984513531570110
|
192
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ! '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
segments:
|
199
|
+
- 0
|
200
|
+
hash: -4325984513531570110
|
201
|
+
requirements: []
|
202
|
+
rubyforge_project:
|
203
|
+
rubygems_version: 1.8.23
|
204
|
+
signing_key:
|
205
|
+
specification_version: 3
|
206
|
+
summary: Enhanced retry logic for Sidekiq workers
|
207
|
+
test_files:
|
208
|
+
- spec/lib/sidekiq/retries/server/middleware_spec.rb
|