sidekiq-rescue 0.1.0 → 0.2.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
  SHA256:
3
- metadata.gz: 8f87fcb8d7e00cd287d289735c0655237d16520c298e9c96b12bbd19f745848d
4
- data.tar.gz: 2be3ba005f0379b6f124974b0e464c0ac197f174952d2bb50c4be9ec4f6f979b
3
+ metadata.gz: d8bcb824ca2945f9cf090e5cb6adfd0a4bc77366461c068915008e5ea953db31
4
+ data.tar.gz: 97011a0a37cefea2f6500e7d96147b50059aa46289112de6ce09a5aa9773f963
5
5
  SHA512:
6
- metadata.gz: 0757e9ea80e869a0eb819e4147d2c65b1bb8e6763af382ae2cf5a8910172d93fafeeb96e1d9cc51af18619e26f7dae0e13b43b2b59cdc07d41ea2330b644bbce
7
- data.tar.gz: 3ab9dcffcbe32a7d26b89791b0c76b1b59d46132afd779ace5d02554f95e2bd418b33ecffb05c89ced58554a25e287d6c5a6a8db13988227c65ab47bbedbd2dd
6
+ metadata.gz: 1f192b50d1b21265ef6902ce095129c0287df13c6684fc4eeaa125a70bdfe722aec40a2d7cc74dd1bbbf937bdeca436e3870ebe00ef1d3b7d818433df6efda08
7
+ data.tar.gz: 79a2a91abcdb591ed762084676ec58732dfb4ce797fbd7335b42b8497933b1e383ec126ed64b16e5dabd5b7925d8c1b7bf0e931cd7cc71b90051bb6a23a0d045
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2024-02-03
4
+
5
+ - Rename `Sidekiq::Rescue::DSL` to `Sidekiq::Rescue::Dsl`
6
+ - Update the `delay` option to now accept a proc as an argument
7
+ - Update dsl to accept a list of errors
8
+
3
9
  ## [0.1.0] - 2024-01-20
4
10
 
5
11
  - Initial release
@@ -9,5 +15,6 @@
9
15
  - Add documentation
10
16
  - Add CI
11
17
 
12
- [Unreleased]: https://github.com/moofkit/sidekiq-rescue/compare/v0.1.0...HEAD
18
+ [Unreleased]: https://github.com/moofkit/sidekiq-rescue/compare/v0.2.0...HEAD
19
+ [0.2.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.2.0
13
20
  [0.1.0]: https://github.com/moofkit/sidekiq-rescue/releases/tag/v0.1.0
data/README.md CHANGED
@@ -37,7 +37,7 @@ end
37
37
  ```ruby
38
38
  class MyJob
39
39
  include Sidekiq::Job
40
- include Sidekiq::Rescue::DSL
40
+ include Sidekiq::Rescue::Dsl
41
41
 
42
42
  sidekiq_rescue ExpectedError
43
43
 
@@ -54,7 +54,7 @@ You can configure the number of retries and the delay (in seconds) between retri
54
54
  ```ruby
55
55
  class MyJob
56
56
  include Sidekiq::Job
57
- include Sidekiq::Rescue::DSL
57
+ include Sidekiq::Rescue::Dsl
58
58
 
59
59
  sidekiq_rescue ExpectedError, delay: 60, limit: 5
60
60
 
@@ -79,10 +79,86 @@ Sidekiq::Rescue.configure do |config|
79
79
  end
80
80
  ```
81
81
 
82
+ You can also configure a job to have the delay to be a proc:
83
+
84
+ ```ruby
85
+ sidekiq_rescue ExpectedError, delay: ->(counter) { counter * 60 }
86
+ ```
87
+
88
+ or globally:
89
+
90
+ ```ruby
91
+ Sidekiq::Rescue.configure do |config|
92
+ config.delay = ->(counter) { counter * 60 }
93
+ end
94
+ ```
95
+
82
96
  ## Use cases
83
97
 
84
98
  Sidekiq::Rescue is useful when you want to retry jobs that failed due to expected errors and not spam your exception tracker with these errors. For example, you may want to retry a job that failed due to a network error or a temporary outage of a third party service, rather than a bug in your code.
85
99
 
100
+ ## Examples
101
+
102
+ ### Retry a job that may failed due to a network error
103
+
104
+ ```ruby
105
+ class MyJob
106
+ include Sidekiq::Job
107
+ include Sidekiq::Rescue::Dsl
108
+
109
+ sidekiq_rescue Faraday::ConnectionFailed
110
+
111
+ def perform(*)
112
+ # ...
113
+ end
114
+ end
115
+ ```
116
+
117
+ ### Retry a job that may failed due to different errors
118
+
119
+ ```ruby
120
+ class MyJob
121
+ include Sidekiq::Job
122
+ include Sidekiq::Rescue::Dsl
123
+
124
+ sidekiq_rescue Faraday::ConnectionFailed, Faraday::TimeoutError
125
+
126
+ def perform(*)
127
+ # ...
128
+ end
129
+ end
130
+ ```
131
+
132
+ ### Retry a job that may failed due to different errors with custom delay
133
+
134
+ ```ruby
135
+ class MyJob
136
+ include Sidekiq::Job
137
+ include Sidekiq::Rescue::Dsl
138
+
139
+ sidekiq_rescue Faraday::ConnectionFailed, Faraday::TimeoutError, delay: 60
140
+
141
+ def perform(*)
142
+ # ...
143
+ end
144
+ end
145
+ ```
146
+
147
+ ### Retry a job that may failed due to different errors with custom delays and limits
148
+
149
+ ```ruby
150
+ class MyJob
151
+ include Sidekiq::Job
152
+ include Sidekiq::Rescue::Dsl
153
+
154
+ sidekiq_rescue Faraday::ConnectionFailed, Faraday::TimeoutError, delay: 60, limit: 5
155
+
156
+ def perform(*)
157
+ # ...
158
+ end
159
+ end
160
+ ```
161
+
86
162
  ## Motivation
87
163
 
88
164
  Sidekiq provides a retry mechanism for jobs that failed due to unexpected errors. However, it does not provide a way to retry jobs that failed due to expected errors. This gem aims to fill this gap.
@@ -24,9 +24,12 @@ module Sidekiq
24
24
  # @return [void]
25
25
  # @raise [ArgumentError] if delay is not an Integer or Float
26
26
  def delay=(delay)
27
- raise ArgumentError, "delay must be an Integer or Float" unless delay.is_a?(Integer) || delay.is_a?(Float)
28
-
29
- @delay = delay
27
+ case delay
28
+ when Integer, Float, Proc
29
+ @delay = delay
30
+ else
31
+ raise ArgumentError, "delay must be Integer, Float or Proc"
32
+ end
30
33
  end
31
34
 
32
35
  # The maximum number of retries.
@@ -2,20 +2,20 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Rescue
5
- # This module is included into the job class to provide the DSL for
5
+ # This module is included into the job class to provide the Dsl for
6
6
  # configuring rescue options.
7
- module DSL
7
+ module Dsl
8
8
  def self.included(base)
9
9
  base.extend(ClassMethods)
10
10
  base.sidekiq_class_attribute(:sidekiq_rescue_options)
11
11
  end
12
12
 
13
- # Module containing the DSL methods
13
+ # Module containing the Dsl methods
14
14
  module ClassMethods
15
15
  # Configure rescue options for the job.
16
16
  # @param error [StandardError] The error class to rescue.
17
17
  # @param error [Array<StandardError>] The error classes to rescue.
18
- # @param delay [Integer] The delay in seconds before retrying the job.
18
+ # @param delay [Integer, Float, Proc] The delay in seconds before retrying the job.
19
19
  # @param limit [Integer] The maximum number of retries.
20
20
  # @return [void]
21
21
  # @raise [ArgumentError] if error is not a StandardError
@@ -24,8 +24,8 @@ module Sidekiq
24
24
  # @raise [ArgumentError] if limit is not an Integer
25
25
  # @example
26
26
  # sidekiq_rescue NetworkError, delay: 60, limit: 10
27
- def sidekiq_rescue(error, delay: nil, limit: nil)
28
- validate_error_argument(error)
27
+ def sidekiq_rescue(*error, delay: nil, limit: nil)
28
+ error = validate_and_unpack_error_argument(error)
29
29
  validate_delay_argument(delay)
30
30
  validate_limit_argument(limit)
31
31
 
@@ -38,23 +38,26 @@ module Sidekiq
38
38
 
39
39
  private
40
40
 
41
- def validate_error_argument(error)
42
- error_arg_valid = if error.is_a?(Array)
43
- error.all? { |e| e < StandardError }
44
- else
45
- error < StandardError
46
- end
47
- return if error_arg_valid
41
+ def validate_and_unpack_error_argument(error)
42
+ error_arg_valid = error.any? && error.flatten.all? { |e| e < StandardError } if error.is_a?(Array)
43
+ return error.flatten if error_arg_valid
48
44
 
49
45
  raise ArgumentError,
50
- "error must be an ancestor of StandardError or an array of ancestors of StandardError"
46
+ "error must be an ancestor of StandardError"
51
47
  end
52
48
 
53
49
  def validate_delay_argument(delay)
54
- return unless delay && !delay.is_a?(Integer) && !delay.is_a?(Float)
50
+ return if delay.nil?
51
+ return if delay.is_a?(Integer) || delay.is_a?(Float)
52
+
53
+ if delay.is_a?(Proc)
54
+ raise ArgumentError, "delay proc must accept counter as argument" if delay.arity.zero?
55
+
56
+ return
57
+ end
55
58
 
56
59
  raise ArgumentError,
57
- "delay must be integer or float"
60
+ "delay must be integer, float or proc"
58
61
  end
59
62
 
60
63
  def validate_limit_argument(limit)
@@ -62,5 +65,9 @@ module Sidekiq
62
65
  end
63
66
  end
64
67
  end
68
+ # Alias for Dsl; TODO: remove in 1.0.0
69
+ # @deprecated
70
+ # @see Dsl
71
+ DSL = Dsl
65
72
  end
66
73
  end
@@ -24,18 +24,35 @@ module Sidekiq
24
24
  def sidekiq_rescue(job_payload, delay:, limit:, error:, **)
25
25
  yield
26
26
  rescue *error => e
27
+ rescue_counter = increment_rescue_counter(job_payload)
28
+ raise e if rescue_counter > limit
29
+
30
+ reschedule_at = calculate_reschedule_time(delay, rescue_counter)
31
+ log_reschedule_info(rescue_counter, e, reschedule_at)
32
+ reschedule_job(job_payload, reschedule_at, rescue_counter)
33
+ end
34
+
35
+ def increment_rescue_counter(job_payload)
27
36
  rescue_counter = job_payload["sidekiq_rescue_counter"].to_i
28
37
  rescue_counter += 1
29
- raise e if rescue_counter > limit
38
+ rescue_counter
39
+ end
30
40
 
41
+ def calculate_reschedule_time(delay, rescue_counter)
31
42
  # NOTE: we use the retry counter to increase the jitter
32
43
  # so that the jobs don't retry at the same time
33
44
  # inspired by sidekiq https://github.com/sidekiq/sidekiq/blob/73c150d0430a8394cadb5cd49218895b113613a0/lib/sidekiq/job_retry.rb#L188
34
45
  jitter = rand(10) * rescue_counter
35
- reschedule_at = Time.now.to_f + delay + jitter
46
+ delay = delay.call(rescue_counter) if delay.is_a?(Proc)
47
+ Time.now.to_f + delay + jitter
48
+ end
49
+
50
+ def log_reschedule_info(rescue_counter, error, reschedule_at)
51
+ Sidekiq::Rescue.logger.info("[sidekiq_rescue] Job failed #{rescue_counter} times with error: " \
52
+ "#{error.message}; rescheduling at #{reschedule_at}")
53
+ end
36
54
 
37
- Sidekiq::Rescue.logger.info("[sidekiq_rescue] Job failed #{rescue_counter} times with error:" \
38
- "#{e.message}; rescheduling at #{reschedule_at}")
55
+ def reschedule_job(job_payload, reschedule_at, rescue_counter)
39
56
  Sidekiq::Client.push(job_payload.merge("at" => reschedule_at, "sidekiq_rescue_counter" => rescue_counter))
40
57
  end
41
58
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Rescue
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
@@ -4,13 +4,13 @@ module Sidekiq
4
4
  # Sidekiq::Rescue is a Sidekiq plugin which allows you to easily handle
5
5
  # exceptions thrown by your jobs.
6
6
  #
7
- # To use Sidekiq::Rescue, you need to include Sidekiq::Rescue::DSL module
7
+ # To use Sidekiq::Rescue, you need to include Sidekiq::Rescue::Dsl module
8
8
  # in your job class and use the sidekiq_rescue class method to define
9
9
  # exception handlers.
10
10
  #
11
11
  # class MyJob
12
12
  # include Sidekiq::Job
13
- # include Sidekiq::Rescue::DSL
13
+ # include Sidekiq::Rescue::Dsl
14
14
  #
15
15
  # sidekiq_rescue NetworkError, delay: 60, limit: 10
16
16
  #
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.1.0
4
+ version: 0.2.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-01-21 00:00:00.000000000 Z
11
+ date: 2024-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -49,6 +49,7 @@ metadata:
49
49
  homepage_uri: https://github.com/moofkit/sidekiq-rescue
50
50
  source_code_uri: https://github.com/moofkit/sidekiq-rescue
51
51
  changelog_uri: https://github.com/moofkit/sidekiq-rescue/blob/master/CHANGELOG.md
52
+ documentation_uri: https://rubydoc.info/gems/sidekiq-rescue/0.2.0
52
53
  rubygems_mfa_required: 'true'
53
54
  post_install_message:
54
55
  rdoc_options: []