sidekiq-rescue 0.3.0 → 0.4.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 +13 -2
- data/README.md +5 -3
- data/lib/sidekiq/rescue/config.rb +19 -1
- data/lib/sidekiq/rescue/dsl.rb +22 -7
- data/lib/sidekiq/rescue/rspec/matchers.rb +1 -1
- data/lib/sidekiq/rescue/server_middleware.rb +20 -18
- data/lib/sidekiq/rescue/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd1cc37dddad565e32a86226f22111be381fffb62f80253e36dbd563598531be
|
4
|
+
data.tar.gz: 050f58d316e722f243b7eb49a5396cb56aa0ba368d827099aa4ca2dafafe43a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34d0325a6547e9ff4087114fb20c424383a62a144bafe72ede1ed53badecdba38affa32f6b695a5e68ab02c100c96629dfce30bc766f656f5dc319659e8e9489
|
7
|
+
data.tar.gz: a6d66d595d8e75e18cadb0f4e7360e49604c36a86c2587b249cb3e98fb4d90b30564ecf1e88d574c97ec34e5673e7a6df12b4d36d45eede6f6529cab9c1423c5
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.0] - 2024-06-03
|
4
|
+
- Add support for jitter configuration [#4](https://github.com/moofkit/sidekiq-rescue/pull/4)
|
5
|
+
- Changes the strategy for retry delay. Now it's calculated using the formula `delay + delay * jitter * rand`
|
6
|
+
|
7
|
+
## [0.3.1] - 2024-05-30
|
8
|
+
|
9
|
+
- Fix bug with inheritance of DSL options
|
10
|
+
|
3
11
|
## [0.3.0] - 2024-05-30
|
4
12
|
|
5
13
|
- Fix issue with RSpec matcher when job is not rescueable
|
6
|
-
- Add support for multiple invocations of the DSL
|
14
|
+
- Add support for multiple invocations of the DSL
|
7
15
|
- Update documentation with new features
|
8
16
|
|
9
17
|
## [0.2.1] - 2024-02-27
|
@@ -26,7 +34,10 @@
|
|
26
34
|
- Add documentation
|
27
35
|
- Add CI
|
28
36
|
|
29
|
-
[Unreleased]: https://github.com/moofkit/sidekiq-rescue/compare/v0.
|
37
|
+
[Unreleased]: https://github.com/moofkit/sidekiq-rescue/compare/v0.4.0...HEAD
|
38
|
+
[0.4.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.4.0
|
39
|
+
[0.3.1]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.3.1
|
40
|
+
[0.3.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.3.0
|
30
41
|
[0.2.1]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.2.1
|
31
42
|
[0.2.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.2.0
|
32
43
|
[0.1.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.1.0
|
data/README.md
CHANGED
@@ -81,7 +81,7 @@ class MyJob
|
|
81
81
|
include Sidekiq::Job
|
82
82
|
include Sidekiq::Rescue::Dsl
|
83
83
|
|
84
|
-
sidekiq_rescue ExpectedError, delay: 60, limit: 5
|
84
|
+
sidekiq_rescue ExpectedError, delay: 60, limit: 5, jitter: 0.15
|
85
85
|
|
86
86
|
def perform(*)
|
87
87
|
# ...
|
@@ -89,18 +89,20 @@ class MyJob
|
|
89
89
|
end
|
90
90
|
```
|
91
91
|
|
92
|
-
The `delay` is not the exact time between retries, but a minimum delay. The actual delay calculates based on
|
92
|
+
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. The `jitter` represents the upper bound of possible wait time (expressed as a percentage) and defaults to 0.15 (15%).
|
93
93
|
|
94
94
|
The default values are:
|
95
95
|
- `delay`: 60 seconds
|
96
96
|
- `limit`: 5 retries
|
97
|
+
- `jitter`: 0.15
|
97
98
|
|
98
|
-
Delay and
|
99
|
+
Delay, limit and jitter can be configured globally:
|
99
100
|
|
100
101
|
```ruby
|
101
102
|
Sidekiq::Rescue.configure do |config|
|
102
103
|
config.delay = 65
|
103
104
|
config.limit = 10
|
105
|
+
config.jitter = 0.2
|
104
106
|
end
|
105
107
|
```
|
106
108
|
|
@@ -7,12 +7,14 @@ module Sidekiq
|
|
7
7
|
class Config
|
8
8
|
DEFAULTS = {
|
9
9
|
delay: 60,
|
10
|
-
limit: 10
|
10
|
+
limit: 10,
|
11
|
+
jitter: 0.15
|
11
12
|
}.freeze
|
12
13
|
|
13
14
|
def initialize
|
14
15
|
@delay = DEFAULTS[:delay]
|
15
16
|
@limit = DEFAULTS[:limit]
|
17
|
+
@jitter = DEFAULTS[:jitter]
|
16
18
|
@logger = Sidekiq.logger
|
17
19
|
end
|
18
20
|
|
@@ -45,6 +47,22 @@ module Sidekiq
|
|
45
47
|
@limit = limit
|
46
48
|
end
|
47
49
|
|
50
|
+
# The jitter for the delay.
|
51
|
+
# @return [Integer, Float]
|
52
|
+
attr_reader :jitter
|
53
|
+
|
54
|
+
# @param jitter [Integer, Float] The jitter for the delay.
|
55
|
+
# @return [void]
|
56
|
+
# @raise [ArgumentError] if jitter is not an Integer or Float
|
57
|
+
def jitter=(jitter)
|
58
|
+
case jitter
|
59
|
+
when Integer, Float
|
60
|
+
@jitter = jitter
|
61
|
+
else
|
62
|
+
raise ArgumentError, "jitter must be Integer or Float"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
48
66
|
# The logger instance.
|
49
67
|
# @return [Logger]
|
50
68
|
# @note The default logger is Sidekiq's logger.
|
data/lib/sidekiq/rescue/dsl.rb
CHANGED
@@ -22,17 +22,25 @@ module Sidekiq
|
|
22
22
|
# @raise [ArgumentError] if error is not an array of StandardError
|
23
23
|
# @raise [ArgumentError] if delay is not an Integer or Float
|
24
24
|
# @raise [ArgumentError] if limit is not an Integer
|
25
|
+
# @raise [ArgumentError] if jitter is not an Integer or Float
|
25
26
|
# @example
|
26
27
|
# sidekiq_rescue NetworkError, delay: 60, limit: 10
|
27
|
-
def sidekiq_rescue(*errors, delay: Sidekiq::Rescue.config.delay, limit: Sidekiq::Rescue.config.limit
|
28
|
+
def sidekiq_rescue(*errors, delay: Sidekiq::Rescue.config.delay, limit: Sidekiq::Rescue.config.limit,
|
29
|
+
jitter: Sidekiq::Rescue.config.jitter)
|
28
30
|
unpacked_errors = validate_and_unpack_error_argument(errors)
|
29
31
|
validate_delay_argument(delay)
|
30
32
|
validate_limit_argument(limit)
|
31
|
-
|
33
|
+
validate_jitter_argument(jitter)
|
34
|
+
assign_sidekiq_rescue_options(errors: unpacked_errors, delay: delay, limit: limit, jitter: jitter)
|
32
35
|
end
|
33
36
|
|
34
|
-
|
35
|
-
|
37
|
+
# Find the error group and options for the given exception.
|
38
|
+
# @param exception [StandardError] The exception to find the error group for.
|
39
|
+
# @return [Array<StandardError>, Hash] The error group and options.
|
40
|
+
def sidekiq_rescue_error_group_with_options_by(exception)
|
41
|
+
sidekiq_rescue_options.reverse_each.find do |error_group, _options|
|
42
|
+
Array(error_group).any? { |error_klass| exception.is_a?(error_klass) }
|
43
|
+
end
|
36
44
|
end
|
37
45
|
|
38
46
|
private
|
@@ -46,7 +54,6 @@ module Sidekiq
|
|
46
54
|
end
|
47
55
|
|
48
56
|
def validate_delay_argument(delay)
|
49
|
-
return if delay.nil?
|
50
57
|
return if delay.is_a?(Integer) || delay.is_a?(Float)
|
51
58
|
|
52
59
|
if delay.is_a?(Proc)
|
@@ -63,9 +70,17 @@ module Sidekiq
|
|
63
70
|
raise ArgumentError, "limit must be integer" if limit && !limit.is_a?(Integer)
|
64
71
|
end
|
65
72
|
|
66
|
-
def
|
73
|
+
def validate_jitter_argument(jitter)
|
74
|
+
return if jitter.is_a?(Integer) || jitter.is_a?(Float)
|
75
|
+
|
76
|
+
raise ArgumentError,
|
77
|
+
"jitter must be integer or float"
|
78
|
+
end
|
79
|
+
|
80
|
+
def assign_sidekiq_rescue_options(errors:, delay:, limit:, jitter:)
|
67
81
|
self.sidekiq_rescue_options ||= {}
|
68
|
-
self.sidekiq_rescue_options.merge
|
82
|
+
self.sidekiq_rescue_options = self.sidekiq_rescue_options.merge(errors => { delay: delay, limit: limit,
|
83
|
+
jitter: jitter })
|
69
84
|
end
|
70
85
|
end
|
71
86
|
end
|
@@ -35,7 +35,7 @@ module Sidekiq
|
|
35
35
|
|
36
36
|
return false unless matched
|
37
37
|
|
38
|
-
options = actual.
|
38
|
+
_error_group, options = actual.sidekiq_rescue_error_group_with_options_by(expected.new)
|
39
39
|
|
40
40
|
(@delay.nil? || options.fetch(:delay) == @delay) &&
|
41
41
|
(@limit.nil? || options.fetch(:limit) == @limit)
|
@@ -23,22 +23,20 @@ module Sidekiq
|
|
23
23
|
def sidekiq_rescue(job_payload, job_class)
|
24
24
|
yield
|
25
25
|
rescue StandardError => e
|
26
|
-
error_group, options = job_class.
|
27
|
-
Array(error_group).any? { |error| e.is_a?(error) }
|
28
|
-
end
|
26
|
+
error_group, options = job_class.sidekiq_rescue_error_group_with_options_by(e)
|
29
27
|
raise e unless error_group
|
30
28
|
|
31
29
|
rescue_error(e, error_group, options, job_payload)
|
32
30
|
end
|
33
31
|
|
34
32
|
def rescue_error(error, error_group, options, job_payload)
|
35
|
-
delay, limit = options.fetch_values(:delay, :limit)
|
33
|
+
delay, limit, jitter = options.fetch_values(:delay, :limit, :jitter)
|
36
34
|
rescue_counter = increment_rescue_counter_for(error_group, job_payload)
|
37
35
|
raise error if rescue_counter > limit
|
38
36
|
|
39
|
-
|
40
|
-
log_reschedule_info(rescue_counter, error,
|
41
|
-
reschedule_job(job_payload: job_payload,
|
37
|
+
calculated_delay = calculate_delay(delay, rescue_counter, jitter)
|
38
|
+
log_reschedule_info(rescue_counter, error, calculated_delay)
|
39
|
+
reschedule_job(job_payload: job_payload, delay: calculated_delay, rescue_counter: rescue_counter,
|
42
40
|
error_group: error_group)
|
43
41
|
end
|
44
42
|
|
@@ -48,23 +46,27 @@ module Sidekiq
|
|
48
46
|
rescue_counter
|
49
47
|
end
|
50
48
|
|
51
|
-
def
|
52
|
-
# NOTE: we use the retry counter to increase the jitter
|
53
|
-
# so that the jobs don't retry at the same time
|
54
|
-
# inspired by sidekiq https://github.com/sidekiq/sidekiq/blob/73c150d0430a8394cadb5cd49218895b113613a0/lib/sidekiq/job_retry.rb#L188
|
55
|
-
jitter = rand(10) * rescue_counter
|
49
|
+
def calculate_delay(delay, rescue_counter, jitter)
|
56
50
|
delay = delay.call(rescue_counter) if delay.is_a?(Proc)
|
57
|
-
|
51
|
+
jitter_delay = calculate_delay_jitter(jitter, delay)
|
52
|
+
delay + jitter_delay
|
53
|
+
end
|
54
|
+
|
55
|
+
def calculate_delay_jitter(jitter, delay)
|
56
|
+
return 0.0 if jitter.zero?
|
57
|
+
|
58
|
+
jitter * Kernel.rand * delay
|
58
59
|
end
|
59
60
|
|
60
|
-
def log_reschedule_info(rescue_counter, error,
|
61
|
+
def log_reschedule_info(rescue_counter, error, delay)
|
61
62
|
Sidekiq::Rescue.logger.info("[sidekiq_rescue] Job failed #{rescue_counter} times with error: " \
|
62
|
-
"#{error.message}; rescheduling
|
63
|
+
"#{error.message}; rescheduling in #{delay} seconds")
|
63
64
|
end
|
64
65
|
|
65
|
-
def reschedule_job(job_payload:,
|
66
|
-
payload = job_payload.
|
67
|
-
|
66
|
+
def reschedule_job(job_payload:, delay:, rescue_counter:, error_group:)
|
67
|
+
payload = job_payload.dup
|
68
|
+
payload["at"] = Time.now.to_f + delay if delay.positive?
|
69
|
+
payload["sidekiq_rescue_exceptions_counter"] = { error_group.to_s => rescue_counter }
|
68
70
|
Sidekiq::Client.push(payload)
|
69
71
|
end
|
70
72
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-rescue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitrii Ivliev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -50,7 +50,7 @@ metadata:
|
|
50
50
|
homepage_uri: https://github.com/moofkit/sidekiq-rescue
|
51
51
|
source_code_uri: https://github.com/moofkit/sidekiq-rescue
|
52
52
|
changelog_uri: https://github.com/moofkit/sidekiq-rescue/blob/master/CHANGELOG.md
|
53
|
-
documentation_uri: https://rubydoc.info/gems/sidekiq-rescue/0.
|
53
|
+
documentation_uri: https://rubydoc.info/gems/sidekiq-rescue/0.4.0
|
54
54
|
rubygems_mfa_required: 'true'
|
55
55
|
post_install_message:
|
56
56
|
rdoc_options: []
|