sneakers_exponential_retry 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +37 -18
- data/lib/sneakers_exponential_retry.rb +83 -80
- data/lib/sneakers_exponential_retry/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 383c16547be12323af1e50d4a68895cf6b9b0468
|
4
|
+
data.tar.gz: 4791aa18b3aa580856516226dc6bf5f440e281d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f33ce2504df5dc2bf284dbb38aa45a9605f5146e95915439ecb91f4eaac3315303da7ef6692e6cd7ad13bdc28963569bae827f295a63f3c99269eb5f818c7ab0
|
7
|
+
data.tar.gz: d8597d129e0a9bb472dec754f6181f1a13956204444dc3c7df3b0cf58941fbb404a349cc5b76b947a1680ebc1058a7474d345b8d31d9d4b5ad441adbec7bdbd3
|
data/README.md
CHANGED
@@ -2,22 +2,6 @@
|
|
2
2
|
|
3
3
|
Exponential Retry Handler for [Sneakers](https://github.com/jondot/sneakers) that just works.
|
4
4
|
|
5
|
-
## Installation
|
6
|
-
|
7
|
-
Add this line to your application's Gemfile:
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
gem 'sneakers_exponential_retry'
|
11
|
-
```
|
12
|
-
|
13
|
-
And then execute:
|
14
|
-
|
15
|
-
$ bundle
|
16
|
-
|
17
|
-
Or install it yourself as:
|
18
|
-
|
19
|
-
$ gem install sneakers_exponential_retry
|
20
|
-
|
21
5
|
## Usage
|
22
6
|
|
23
7
|
Configure your Sneakers by the following:
|
@@ -25,7 +9,7 @@ Configure your Sneakers by the following:
|
|
25
9
|
```ruby
|
26
10
|
require 'sneakers_exponential_retry'
|
27
11
|
|
28
|
-
Sneakers.configure :handler => SneakersExponentialRetry,
|
12
|
+
Sneakers.configure :handler => SneakersExponentialRetry::Handler,
|
29
13
|
:handler_options => {
|
30
14
|
:max_retry_count => 3,
|
31
15
|
:logger => Sneakers.logger
|
@@ -41,9 +25,44 @@ Sneakers.configure :handler => SneakersExponentialRetry,
|
|
41
25
|
- `max_retry_count`: (optional) Max retry count, default to 14
|
42
26
|
- `logger`: (optional) logger instance, default to nil, which would not log anything related to retrying.
|
43
27
|
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
Add this line to your application's Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'sneakers_exponential_retry'
|
35
|
+
```
|
36
|
+
|
37
|
+
And then execute:
|
38
|
+
|
39
|
+
$ bundle
|
40
|
+
|
41
|
+
Or install it yourself as:
|
42
|
+
|
43
|
+
$ gem install sneakers_exponential_retry
|
44
|
+
|
45
|
+
|
44
46
|
## How it works:
|
45
47
|
|
46
|
-
|
48
|
+
(`SneakersExponentialRetry` is inspired by [this blogpost](https://gagnechris.wordpress.com/2015/09/19/easy-retries-with-rabbitmq/))
|
49
|
+
|
50
|
+
`SneakersExponentialRetry` handles the behavior of retrying failed jobs exponentially by:
|
51
|
+
|
52
|
+
1. On initializing:
|
53
|
+
- Create a retry exchange, which is named as `#{queue_name}-retry-ex`
|
54
|
+
- Create a retry queue, which is named as `#{queue_name}-retry-queue`
|
55
|
+
- Set the `x-dead-letter-exchange` of retry queue to the original job exchange
|
56
|
+
- Bind the retry queue to the retry exchange
|
57
|
+
|
58
|
+
2. Whenever a job fails, `SneakersExponentialRetry` would:
|
59
|
+
- if retry count <= `max_retry_count`
|
60
|
+
- publish the job to retry exchange, with an exponential expiration timeout
|
61
|
+
- the retry exchange would push the job into our retry queue
|
62
|
+
- after the timeout, the job would be published back to the `dead-letter-exchange` of the retry queue, which is our original exchange, so that the job would be retried
|
63
|
+
|
64
|
+
- if retry count > `max_retry_count`
|
65
|
+
- reject the job
|
47
66
|
|
48
67
|
## Testing:
|
49
68
|
|
@@ -1,97 +1,100 @@
|
|
1
1
|
require "sneakers_exponential_retry/version"
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
.
|
15
|
-
|
16
|
-
|
17
|
-
def calculate_max_retry_count opts
|
18
|
-
handler_options = opts[:handler_options] || {}
|
19
|
-
( handler_options[:max_retry_count] || DEFAULT_MAX_RETRY_COUNT ).to_i
|
20
|
-
end
|
21
|
-
private :calculate_max_retry_count
|
22
|
-
|
23
|
-
def create_retry_exchange queue_name
|
24
|
-
@channel.exchange("#{queue_name}-retry-ex",
|
25
|
-
:type => 'topic',
|
26
|
-
:durable => true)
|
27
|
-
end
|
28
|
-
private :create_retry_exchange
|
29
|
-
|
30
|
-
def create_retry_queue queue_name, exchange_name
|
31
|
-
@channel.queue(
|
32
|
-
"#{queue_name}-retry-queue",
|
33
|
-
durable: true,
|
34
|
-
arguments: {
|
35
|
-
:'x-dead-letter-exchange' => exchange_name
|
36
|
-
}
|
37
|
-
)
|
38
|
-
end
|
39
|
-
private :create_retry_queue
|
3
|
+
module SneakersExponentialRetry
|
4
|
+
class Handler
|
5
|
+
MINUTE = 60 * 1000
|
6
|
+
DEFAULT_MAX_RETRY_COUNT = 14
|
7
|
+
|
8
|
+
def initialize(channel, queue, opts)
|
9
|
+
@channel = channel
|
10
|
+
@max_retry_count = calculate_max_retry_count(opts)
|
11
|
+
@retry_exchange = create_retry_exchange(queue.name)
|
12
|
+
@logger = opts[:handler_options] ? opts[:handler_options][:logger] : nil
|
13
|
+
|
14
|
+
create_retry_queue(queue.name, opts[:exchange])
|
15
|
+
.bind(@retry_exchange, :routing_key => '#')
|
16
|
+
end
|
40
17
|
|
41
|
-
|
42
|
-
|
43
|
-
|
18
|
+
def calculate_max_retry_count opts
|
19
|
+
handler_options = opts[:handler_options] || {}
|
20
|
+
( handler_options[:max_retry_count] || DEFAULT_MAX_RETRY_COUNT ).to_i
|
21
|
+
end
|
22
|
+
private :calculate_max_retry_count
|
44
23
|
|
45
|
-
|
46
|
-
|
47
|
-
|
24
|
+
def create_retry_exchange queue_name
|
25
|
+
@channel.exchange("#{queue_name}-retry-ex",
|
26
|
+
:type => 'topic',
|
27
|
+
:durable => true)
|
28
|
+
end
|
29
|
+
private :create_retry_exchange
|
30
|
+
|
31
|
+
def create_retry_queue queue_name, exchange_name
|
32
|
+
@channel.queue(
|
33
|
+
"#{queue_name}-retry-queue",
|
34
|
+
durable: true,
|
35
|
+
arguments: {
|
36
|
+
:'x-dead-letter-exchange' => exchange_name
|
37
|
+
}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
private :create_retry_queue
|
48
41
|
|
49
|
-
|
50
|
-
|
51
|
-
|
42
|
+
def acknowledge(hdr, props, msg)
|
43
|
+
@channel.acknowledge(hdr.delivery_tag, false)
|
44
|
+
end
|
52
45
|
|
53
|
-
|
54
|
-
|
55
|
-
if retry_count >= @max_retry_count
|
56
|
-
reject(hdr, props, msg)
|
57
|
-
return
|
46
|
+
def reject(hdr, props, msg, requeue=false)
|
47
|
+
@channel.reject(hdr.delivery_tag, requeue)
|
58
48
|
end
|
59
49
|
|
50
|
+
def error(hdr, props, msg, err)
|
51
|
+
handle_failing_message(hdr, props, msg, err)
|
52
|
+
end
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
54
|
+
def handle_failing_message hdr, props, msg, error
|
55
|
+
retry_count = get_retry_count(props[:headers])
|
56
|
+
if retry_count >= @max_retry_count
|
57
|
+
reject(hdr, props, msg)
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
@retry_exchange.publish(msg,
|
63
|
+
:headers => {
|
64
|
+
'retry-count' => retry_count + 1
|
65
|
+
},
|
66
|
+
:routing_key => hdr.routing_key,
|
67
|
+
:expiration => expiration_time(retry_count))
|
68
|
+
log_retry(retry_count)
|
69
|
+
acknowledge(hdr, props, msg)
|
70
|
+
end
|
71
|
+
private :handle_failing_message
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
73
|
+
def log_retry count
|
74
|
+
return unless @logger
|
75
|
+
@logger.info do
|
76
|
+
"retry_count: #{count + 1}"
|
77
|
+
end
|
76
78
|
end
|
77
|
-
|
78
|
-
private :log_retry
|
79
|
+
private :log_retry
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
def get_retry_count headers
|
82
|
+
return 0 unless headers
|
83
|
+
headers['retry-count'].to_i
|
84
|
+
end
|
85
|
+
private :get_retry_count
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
87
|
+
def expiration_time retry_count
|
88
|
+
(2 ** retry_count) * MINUTE
|
89
|
+
end
|
90
|
+
private :expiration_time
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
92
|
+
def timeout(hdr, props, msg)
|
93
|
+
reject(hdr, props, msg)
|
94
|
+
end
|
94
95
|
|
95
|
-
|
96
|
+
def noop(hdr, props, msg)
|
97
|
+
end
|
96
98
|
end
|
97
99
|
end
|
100
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sneakers_exponential_retry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yang-Hsing Lin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|