sidekiq-rescue 0.4.0 → 0.6.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 +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +12 -5
- data/lib/sidekiq/rescue/dsl.rb +17 -5
- data/lib/sidekiq/rescue/rspec/matchers.rb +17 -2
- data/lib/sidekiq/rescue/server_middleware.rb +8 -5
- data/lib/sidekiq/rescue/version.rb +1 -1
- metadata +7 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7e9209b0b3fcb19ae0068540e6a83aedf56928457fd53584e6ebf7397ef541a
|
4
|
+
data.tar.gz: debef2495e57e1452f19982777fb2238f75c811ab48a161f36858beff7f7a7e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96620e97aeabdfb9bc0ed8df29216e5017409c28e126e0e41d3941b3dde8548b5a4ecf0c1e54253f6280a8046291f21e2110ab3c514673f26cb1a557b895f754
|
7
|
+
data.tar.gz: a01212d26bdfb69c4dd3377a89fdeae379f8a016d8e98087b5c916528e0a54191bdff9b99d5e41330e81b7d971aea0da18fcde7058d1f727d525ac57e29d72b6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.6.0] - 2025-03-15
|
4
|
+
- Add support for Sidekiq 8.0, Ruby 3.4 [#6](https://github.com/moofkit/sidekiq-rescue/pull/6)
|
5
|
+
|
6
|
+
## [0.5.0] - 2024-10-17
|
7
|
+
- Add support for queue configuration [#5](https://github.com/moofkit/sidekiq-rescue/pull/5)
|
8
|
+
|
3
9
|
## [0.4.0] - 2024-06-03
|
4
10
|
- Add support for jitter configuration [#4](https://github.com/moofkit/sidekiq-rescue/pull/4)
|
5
11
|
- Changes the strategy for retry delay. Now it's calculated using the formula `delay + delay * jitter * rand`
|
@@ -34,7 +40,9 @@
|
|
34
40
|
- Add documentation
|
35
41
|
- Add CI
|
36
42
|
|
37
|
-
[Unreleased]: https://github.com/moofkit/sidekiq-rescue/compare/v0.
|
43
|
+
[Unreleased]: https://github.com/moofkit/sidekiq-rescue/compare/v0.6.0...HEAD
|
44
|
+
[0.6.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.6.0
|
45
|
+
[0.5.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.5.0
|
38
46
|
[0.4.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.4.0
|
39
47
|
[0.3.1]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.3.1
|
40
48
|
[0.3.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.3.0
|
data/README.md
CHANGED
@@ -16,14 +16,16 @@ class MyJob
|
|
16
16
|
|
17
17
|
sidekiq_rescue CustomAppException # defaults to 60 seconds delay and 10 retries
|
18
18
|
sidekiq_rescue AnotherCustomAppException, delay: ->(counter) { counter * 2 }
|
19
|
-
sidekiq_rescue CustomInfrastructureException, delay: 5.minutes
|
20
|
-
sidekiq_rescue ActiveRecord::Deadlocked, delay: 5.seconds, limit: 3
|
19
|
+
sidekiq_rescue CustomInfrastructureException, delay: 5.minutes.to_i
|
20
|
+
sidekiq_rescue ActiveRecord::Deadlocked, delay: 5.seconds.to_i, limit: 3
|
21
|
+
sidekiq_rescue TooManyRequestsError, queue: "slow"
|
21
22
|
sidekiq_rescue Net::OpenTimeout, Timeout::Error, limit: 10 # retries at most 10 times for Net::OpenTimeout and Timeout::Error combined
|
22
23
|
|
23
24
|
def perform(*args)
|
24
25
|
# Might raise CustomAppException, AnotherCustomAppException, or YetAnotherCustomAppException for something domain specific
|
25
26
|
# Might raise ActiveRecord::Deadlocked when a local db deadlock is detected
|
26
27
|
# Might raise Net::OpenTimeout or Timeout::Error when the remote service is down
|
28
|
+
# Might raise TooManyRequestsError when the rate limit is exceeded
|
27
29
|
end
|
28
30
|
end
|
29
31
|
```
|
@@ -74,7 +76,7 @@ end
|
|
74
76
|
|
75
77
|
## Configuration
|
76
78
|
|
77
|
-
You can configure the number of retries and the delay
|
79
|
+
You can configure the number of retries and the delay *in seconds* between retries:
|
78
80
|
|
79
81
|
```ruby
|
80
82
|
class MyJob
|
@@ -89,7 +91,12 @@ class MyJob
|
|
89
91
|
end
|
90
92
|
```
|
91
93
|
|
92
|
-
|
94
|
+
* `delay` - the delay between retries in seconds
|
95
|
+
* `limit` - the number of retries. The number of attempts includes the original job execution.
|
96
|
+
* `jitter` - represents the upper bound of possible wait time (expressed as a percentage) and defaults to 0.15 (15%)
|
97
|
+
* `queue` - the queue to which the job will be enqueued if it fails
|
98
|
+
|
99
|
+
The `delay` is not the exact time between retries, but a minimum delay. The actual delay calculates based on jitter and `delay` value. The formula is `delay + delay * jitter * rand` seconds. Randomization is used to avoid retry storms.
|
93
100
|
|
94
101
|
The default values are:
|
95
102
|
- `delay`: 60 seconds
|
@@ -261,7 +268,7 @@ If something doesn't work on one of these versions, it's a bug
|
|
261
268
|
|
262
269
|
## Supported Sidekiq versions
|
263
270
|
|
264
|
-
This gem supports Sidekiq
|
271
|
+
This gem supports Sidekiq 7.0+. It may work with older versions, but it's not tested.
|
265
272
|
|
266
273
|
If you need support for older versions, please open an issue
|
267
274
|
|
data/lib/sidekiq/rescue/dsl.rb
CHANGED
@@ -23,15 +23,19 @@ module Sidekiq
|
|
23
23
|
# @raise [ArgumentError] if delay is not an Integer or Float
|
24
24
|
# @raise [ArgumentError] if limit is not an Integer
|
25
25
|
# @raise [ArgumentError] if jitter is not an Integer or Float
|
26
|
+
# @raise [ArgumentError] if queue is not a String
|
26
27
|
# @example
|
27
28
|
# sidekiq_rescue NetworkError, delay: 60, limit: 10
|
28
29
|
def sidekiq_rescue(*errors, delay: Sidekiq::Rescue.config.delay, limit: Sidekiq::Rescue.config.limit,
|
29
|
-
jitter: Sidekiq::Rescue.config.jitter)
|
30
|
+
jitter: Sidekiq::Rescue.config.jitter, queue: nil)
|
30
31
|
unpacked_errors = validate_and_unpack_error_argument(errors)
|
31
32
|
validate_delay_argument(delay)
|
32
33
|
validate_limit_argument(limit)
|
33
34
|
validate_jitter_argument(jitter)
|
34
|
-
|
35
|
+
validate_queue_argument(queue)
|
36
|
+
assign_sidekiq_rescue_options(
|
37
|
+
errors: unpacked_errors, delay:, limit:, jitter:, queue:
|
38
|
+
)
|
35
39
|
end
|
36
40
|
|
37
41
|
# Find the error group and options for the given exception.
|
@@ -77,10 +81,18 @@ module Sidekiq
|
|
77
81
|
"jitter must be integer or float"
|
78
82
|
end
|
79
83
|
|
80
|
-
def
|
84
|
+
def validate_queue_argument(queue)
|
85
|
+
return if queue.nil? || queue.is_a?(String)
|
86
|
+
|
87
|
+
raise ArgumentError,
|
88
|
+
"queue must be a string"
|
89
|
+
end
|
90
|
+
|
91
|
+
def assign_sidekiq_rescue_options(errors:, delay:, limit:, jitter:, queue:)
|
81
92
|
self.sidekiq_rescue_options ||= {}
|
82
|
-
self.sidekiq_rescue_options = self.sidekiq_rescue_options.merge(errors => {
|
83
|
-
|
93
|
+
self.sidekiq_rescue_options = self.sidekiq_rescue_options.merge(errors => {
|
94
|
+
delay:, limit:, jitter:, queue:
|
95
|
+
}.compact)
|
84
96
|
end
|
85
97
|
end
|
86
98
|
end
|
@@ -15,6 +15,8 @@ module Sidekiq
|
|
15
15
|
str = "expected #{actual} to be rescueable with #{expected}"
|
16
16
|
str += " and delay #{@delay}" if @delay
|
17
17
|
str += " and limit #{@limit}" if @limit
|
18
|
+
str += " and jitter #{@jitter}" if @jitter
|
19
|
+
str += " and queue #{@queue}" if @queue
|
18
20
|
str
|
19
21
|
end
|
20
22
|
failure_message_when_negated { |actual| "expected #{actual} not to be rescueable with #{expected}" }
|
@@ -27,23 +29,36 @@ module Sidekiq
|
|
27
29
|
@limit = limit
|
28
30
|
end
|
29
31
|
|
32
|
+
chain :with_jitter do |jitter|
|
33
|
+
@jitter = jitter
|
34
|
+
end
|
35
|
+
|
36
|
+
chain :with_queue do |queue|
|
37
|
+
@queue = queue
|
38
|
+
end
|
39
|
+
|
30
40
|
match do |actual|
|
31
41
|
matched = actual.is_a?(Class) &&
|
32
42
|
actual.include?(Sidekiq::Rescue::Dsl) &&
|
33
43
|
actual.respond_to?(:sidekiq_rescue_options) &&
|
34
|
-
actual
|
44
|
+
actual.sidekiq_rescue_options.is_a?(Hash) &&
|
45
|
+
actual.sidekiq_rescue_options.keys.flatten.include?(expected)
|
35
46
|
|
36
47
|
return false unless matched
|
37
48
|
|
38
49
|
_error_group, options = actual.sidekiq_rescue_error_group_with_options_by(expected.new)
|
39
50
|
|
40
51
|
(@delay.nil? || options.fetch(:delay) == @delay) &&
|
41
|
-
(@limit.nil? || options.fetch(:limit) == @limit)
|
52
|
+
(@limit.nil? || options.fetch(:limit) == @limit) &&
|
53
|
+
(@jitter.nil? || options.fetch(:jitter) == @jitter) &&
|
54
|
+
(@queue.nil? || options.fetch(:queue) == @queue)
|
42
55
|
end
|
43
56
|
|
44
57
|
match_when_negated do |actual|
|
45
58
|
raise NotImplementedError, "it's confusing to use `not_to be_rescueable` with `with_delay`" if @delay
|
46
59
|
raise NotImplementedError, "it's confusing to use `not_to be_rescueable` with `with_limit`" if @limit
|
60
|
+
raise NotImplementedError, "it's confusing to use `not_to be_rescueable` with `with_jitter`" if @jitter
|
61
|
+
raise NotImplementedError, "it's confusing to use `not_to be_rescueable` with `with_queue`" if @queue
|
47
62
|
|
48
63
|
actual.is_a?(Class) &&
|
49
64
|
actual.include?(Sidekiq::Rescue::Dsl) &&
|
@@ -9,10 +9,10 @@ module Sidekiq
|
|
9
9
|
class ServerMiddleware
|
10
10
|
include Sidekiq::ServerMiddleware
|
11
11
|
|
12
|
-
def call(job_instance, job_payload, _queue, &
|
12
|
+
def call(job_instance, job_payload, _queue, &)
|
13
13
|
job_class = job_instance.class
|
14
14
|
if job_class.respond_to?(:sidekiq_rescue_options) && !job_class.sidekiq_rescue_options.nil?
|
15
|
-
sidekiq_rescue(job_payload, job_class, &
|
15
|
+
sidekiq_rescue(job_payload, job_class, &)
|
16
16
|
else
|
17
17
|
yield
|
18
18
|
end
|
@@ -31,13 +31,15 @@ module Sidekiq
|
|
31
31
|
|
32
32
|
def rescue_error(error, error_group, options, job_payload)
|
33
33
|
delay, limit, jitter = options.fetch_values(:delay, :limit, :jitter)
|
34
|
+
queue = options.fetch(:queue, job_payload["queue"])
|
35
|
+
|
34
36
|
rescue_counter = increment_rescue_counter_for(error_group, job_payload)
|
35
37
|
raise error if rescue_counter > limit
|
36
38
|
|
37
39
|
calculated_delay = calculate_delay(delay, rescue_counter, jitter)
|
38
40
|
log_reschedule_info(rescue_counter, error, calculated_delay)
|
39
|
-
reschedule_job(job_payload
|
40
|
-
error_group:
|
41
|
+
reschedule_job(job_payload:, delay: calculated_delay, rescue_counter:,
|
42
|
+
error_group:, queue:)
|
41
43
|
end
|
42
44
|
|
43
45
|
def increment_rescue_counter_for(error_group, job_payload)
|
@@ -63,10 +65,11 @@ module Sidekiq
|
|
63
65
|
"#{error.message}; rescheduling in #{delay} seconds")
|
64
66
|
end
|
65
67
|
|
66
|
-
def reschedule_job(job_payload:, delay:, rescue_counter:, error_group:)
|
68
|
+
def reschedule_job(job_payload:, delay:, rescue_counter:, error_group:, queue:)
|
67
69
|
payload = job_payload.dup
|
68
70
|
payload["at"] = Time.now.to_f + delay if delay.positive?
|
69
71
|
payload["sidekiq_rescue_exceptions_counter"] = { error_group.to_s => rescue_counter }
|
72
|
+
payload["queue"] = queue
|
70
73
|
Sidekiq::Client.push(payload)
|
71
74
|
end
|
72
75
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-rescue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitrii Ivliev
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-03-15 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: sidekiq
|
@@ -16,15 +15,14 @@ dependencies:
|
|
16
15
|
requirements:
|
17
16
|
- - ">="
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
18
|
+
version: '7.0'
|
20
19
|
type: :runtime
|
21
20
|
prerelease: false
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
23
22
|
requirements:
|
24
23
|
- - ">="
|
25
24
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
-
description:
|
25
|
+
version: '7.0'
|
28
26
|
email:
|
29
27
|
- mail@ivda.dev
|
30
28
|
executables: []
|
@@ -50,9 +48,8 @@ metadata:
|
|
50
48
|
homepage_uri: https://github.com/moofkit/sidekiq-rescue
|
51
49
|
source_code_uri: https://github.com/moofkit/sidekiq-rescue
|
52
50
|
changelog_uri: https://github.com/moofkit/sidekiq-rescue/blob/master/CHANGELOG.md
|
53
|
-
documentation_uri: https://rubydoc.info/gems/sidekiq-rescue/0.
|
51
|
+
documentation_uri: https://rubydoc.info/gems/sidekiq-rescue/0.6.0
|
54
52
|
rubygems_mfa_required: 'true'
|
55
|
-
post_install_message:
|
56
53
|
rdoc_options: []
|
57
54
|
require_paths:
|
58
55
|
- lib
|
@@ -60,15 +57,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
57
|
requirements:
|
61
58
|
- - ">="
|
62
59
|
- !ruby/object:Gem::Version
|
63
|
-
version: 2.
|
60
|
+
version: 3.2.0
|
64
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
62
|
requirements:
|
66
63
|
- - ">="
|
67
64
|
- !ruby/object:Gem::Version
|
68
65
|
version: '0'
|
69
66
|
requirements: []
|
70
|
-
rubygems_version: 3.
|
71
|
-
signing_key:
|
67
|
+
rubygems_version: 3.6.2
|
72
68
|
specification_version: 4
|
73
69
|
summary: Rescue Sidekiq jobs on expected error and reschedule them
|
74
70
|
test_files: []
|