retriable 2.0.0.beta5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +14 -7
- data/.travis.yml +10 -3
- data/CHANGELOG.md +5 -1
- data/Gemfile +12 -0
- data/Guardfile +2 -8
- data/README.md +27 -25
- data/lib/retriable.rb +15 -15
- data/lib/retriable/config.rb +1 -1
- data/lib/retriable/exponential_backoff.rb +9 -9
- data/lib/retriable/version.rb +1 -1
- data/retriable.gemspec +21 -22
- data/spec/exponential_backoff_spec.rb +60 -14
- data/spec/retriable_spec.rb +119 -93
- data/spec/spec_helper.rb +16 -0
- data/wercker.yml +2 -2
- metadata +22 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b752911d256dced83d90ff384285f521001fd21
|
4
|
+
data.tar.gz: bc8abdb30075bfe7948bff5e2a7ddec0cdeb62ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b564835a63aa4d7e890cc0417d55132a8124070b92747074d9c5795daf28d2cc896cdc887e1da31b6176ee4061a83c9513b47dc15171f1332c1b0f40d4f59752
|
7
|
+
data.tar.gz: 7560dac587dfc6556602117c3f698ce2a7e52ef00f465e9a1dc871e7faf8a764d220a6ef8a34fb16dd2a45c278c9965043664cec4d300bf6a508bbd4d121480a
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- 2.0.0
|
4
|
-
- 2.1.
|
5
|
-
-
|
4
|
+
- 2.1.5
|
5
|
+
- 2.2.0
|
6
6
|
- rbx
|
7
|
+
- ruby-head
|
8
|
+
- jruby-head
|
7
9
|
|
8
10
|
matrix:
|
9
11
|
allow_failures:
|
10
|
-
- rvm:
|
12
|
+
- rvm: ruby-head
|
13
|
+
- rvm: jruby-head
|
14
|
+
|
15
|
+
addons:
|
16
|
+
code_climate:
|
17
|
+
repo_token: 20a1139ef1830b4f813a10a03d90e8aa179b5226f75e75c5a949b25756ebf558
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
## HEAD
|
2
|
+
|
3
|
+
* Require ruby 2.0.0 minimum in gemspec.
|
4
|
+
|
1
5
|
## 2.0.0.beta5
|
2
|
-
* Change
|
6
|
+
* Change `:max_tries` back to `:tries`.
|
3
7
|
|
4
8
|
## 2.0.0.beta4
|
5
9
|
* Change #retry back to #retriable. Didn't like the idea of defining a method that is also a reserved word.
|
data/Gemfile
CHANGED
@@ -2,3 +2,15 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in retriable.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem "ruby_gntp"
|
8
|
+
gem "minitest-focus"
|
9
|
+
gem "codeclimate-test-reporter", require: false
|
10
|
+
gem "simplecov", require: false
|
11
|
+
end
|
12
|
+
|
13
|
+
group :development, :test do
|
14
|
+
gem "pry"
|
15
|
+
gem "awesome_print"
|
16
|
+
end
|
data/Guardfile
CHANGED
@@ -2,13 +2,7 @@
|
|
2
2
|
# More info at https://github.com/guard/guard#readme
|
3
3
|
|
4
4
|
guard :minitest do
|
5
|
-
# with Minitest::Unit
|
6
|
-
watch(%r{^test/(.*)\/?test_(.*)\.rb})
|
7
|
-
watch(%r{^lib/retriable/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}/test_#{m[2]}.rb" }
|
8
|
-
watch(%r{^test/test_helper\.rb}) { 'test' }
|
9
|
-
|
10
|
-
# with Minitest::Spec
|
11
5
|
watch(%r{^spec/(.*)_spec\.rb})
|
12
|
-
watch(%r{^lib/retriable/(.+)\.rb})
|
13
|
-
watch(%r{^spec/spec_helper\.rb})
|
6
|
+
watch(%r{^lib/retriable/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch(%r{^spec/spec_helper\.rb}) { 'spec' }
|
14
8
|
end
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
#Retriable
|
2
2
|
|
3
3
|
[![Build Status](https://secure.travis-ci.org/kamui/retriable.png)](http://travis-ci.org/kamui/retriable)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/kamui/retriable/badges/gpa.svg)](https://codeclimate.com/github/kamui/retriable)
|
5
|
+
[![Test Coverage](https://codeclimate.com/github/kamui/retriable/badges/coverage.svg)](https://codeclimate.com/github/kamui/retriable)
|
4
6
|
|
5
7
|
Retriable is an simple DSL to retry failed code blocks with randomized [exponential backoff](http://en.wikipedia.org/wiki/Exponential_backoff) time intervals. This is especially useful when interacting external api/services or file system calls.
|
6
8
|
|
@@ -23,31 +25,31 @@ gem install retriable
|
|
23
25
|
In your ruby script:
|
24
26
|
|
25
27
|
```ruby
|
26
|
-
require 'retriable'
|
28
|
+
require 'retriable'
|
27
29
|
```
|
28
30
|
|
29
31
|
In your Gemfile:
|
30
32
|
|
31
33
|
```ruby
|
32
|
-
gem 'retriable'
|
34
|
+
gem 'retriable', '~> 2.0'
|
33
35
|
```
|
34
36
|
|
35
37
|
## Usage
|
36
38
|
|
37
|
-
Code in a `Retriable.retriable` block will be retried if an exception is raised. By default, Retriable will rescue any exception inherited from `StandardError`, make 3 retry
|
39
|
+
Code in a `Retriable.retriable` block will be retried if an exception is raised. By default, Retriable will rescue any exception inherited from `StandardError`, make 3 retry tries before raising the last exception, and also use randomized exponential backoff to calculate each succeeding try interval. The default interval table with 10 tries looks like this (in seconds):
|
38
40
|
|
39
|
-
| request# | retry interval | randomized interval
|
40
|
-
| -------- | -------------- |
|
41
|
-
| 1 | 0.5 |
|
42
|
-
| 2 | 0.75 |
|
43
|
-
| 3 | 1.125 |
|
44
|
-
| 4 | 1.
|
45
|
-
| 5 | 2.
|
46
|
-
| 6 | 3.
|
47
|
-
| 7 | 5.
|
48
|
-
| 8 |
|
49
|
-
| 9 |
|
50
|
-
| 10 |
|
41
|
+
| request# | retry interval | randomized interval |
|
42
|
+
| -------- | -------------- | ------------------------------- |
|
43
|
+
| 1 | 0.5 | [0.25, 0.75] |
|
44
|
+
| 2 | 0.75 | [0.375, 1.125] |
|
45
|
+
| 3 | 1.125 | [0.5625, 1.6875] |
|
46
|
+
| 4 | 1.6875 | [0.84375, 2.53125] |
|
47
|
+
| 5 | 2.53125 | [1.265625, 3.796875] |
|
48
|
+
| 6 | 3.796875 | [1.8984375, 5.6953125] |
|
49
|
+
| 7 | 5.6953125 | [2.84765625, 8.54296875] |
|
50
|
+
| 8 | 8.54296875 | [4.271484375, 12.814453125] |
|
51
|
+
| 9 | 12.814453125 | [6.4072265625, 19.2216796875] |
|
52
|
+
| 10 | 19.2216796875 | stop |
|
51
53
|
|
52
54
|
```ruby
|
53
55
|
require 'retriable'
|
@@ -66,11 +68,11 @@ end
|
|
66
68
|
|
67
69
|
Here are the available options:
|
68
70
|
|
69
|
-
`tries` (default: 3) - Number of
|
71
|
+
`tries` (default: 3) - Number of tries to make at running your code block.
|
70
72
|
|
71
|
-
`base_interval` (default: 0.5) - The initial interval in seconds between
|
73
|
+
`base_interval` (default: 0.5) - The initial interval in seconds between tries.
|
72
74
|
|
73
|
-
`max_interval` (default: 60) - The maximum interval in seconds that any
|
75
|
+
`max_interval` (default: 60) - The maximum interval in seconds that any try can reach.
|
74
76
|
|
75
77
|
`rand_factor` (default: 0.25) - The percent range above and below the next interval is randomized between. The calculation is calculated like this:
|
76
78
|
|
@@ -86,9 +88,9 @@ randomized_interval = retry_interval * (random value in range [1 - randomization
|
|
86
88
|
|
87
89
|
`timeout` (default: 0) - Number of seconds to allow the code block to run before raising a Timeout::Error
|
88
90
|
|
89
|
-
`on` (default: [StandardError]) - An `Array` of exceptions to rescue for each
|
91
|
+
`on` (default: [StandardError]) - An `Array` of exceptions to rescue for each try, a `Hash` where the keys are `Exception` classes and the values can be a single `Regexp` pattern or a list of patterns, or a single `Exception` type.
|
90
92
|
|
91
|
-
`on_retry` - (default: nil) - Proc to call after each
|
93
|
+
`on_retry` - (default: nil) - Proc to call after each try is rescued.
|
92
94
|
|
93
95
|
### Config
|
94
96
|
|
@@ -103,7 +105,7 @@ end
|
|
103
105
|
|
104
106
|
### Examples
|
105
107
|
|
106
|
-
`Retriable.retriable` accepts custom arguments. This example will only retry on a `Timeout::Error`, retry 3 times and sleep for a full second before each
|
108
|
+
`Retriable.retriable` accepts custom arguments. This example will only retry on a `Timeout::Error`, retry 3 times and sleep for a full second before each try.
|
107
109
|
|
108
110
|
```ruby
|
109
111
|
Retriable.retriable on: Timeout::Error, tries: 3, base_interval: 1 do
|
@@ -131,7 +133,7 @@ Retriable.retriable on: {
|
|
131
133
|
end
|
132
134
|
```
|
133
135
|
|
134
|
-
You can also specify a timeout if you want the code block to only make an
|
136
|
+
You can also specify a timeout if you want the code block to only make an try for X amount of seconds. This timeout is per try.
|
135
137
|
|
136
138
|
```ruby
|
137
139
|
Retriable.retriable timeout: 60 do
|
@@ -177,11 +179,11 @@ end
|
|
177
179
|
|
178
180
|
### Callbacks
|
179
181
|
|
180
|
-
`#retriable` also provides a callback called `:on_retry` that will run after an exception is rescued. This callback provides the `exception` that was raised in the current
|
182
|
+
`#retriable` also provides a callback called `:on_retry` that will run after an exception is rescued. This callback provides the `exception` that was raised in the current try, the `try_number`, the `elapsed_time` for all tries so far, and the time in seconds of the `next_interval`. As these are specified in a `Proc`, unnecessary variables can be left out of the parameter list.
|
181
183
|
|
182
184
|
```ruby
|
183
|
-
do_this_on_each_retry = Proc.new do |exception,
|
184
|
-
log "#{exception.class}: '#{exception.message}' - #{
|
185
|
+
do_this_on_each_retry = Proc.new do |exception, try, elapsed_time, next_interval|
|
186
|
+
log "#{exception.class}: '#{exception.message}' - #{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try."}
|
185
187
|
end
|
186
188
|
|
187
189
|
Retriable.retriable on_retry: do_this_on_each_retry do
|
data/lib/retriable.rb
CHANGED
@@ -17,16 +17,16 @@ module Retriable
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def retriable(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
tries: config.tries,
|
21
|
+
base_interval: config.base_interval,
|
22
|
+
max_interval: config.max_interval,
|
23
|
+
rand_factor: config.rand_factor,
|
24
|
+
multiplier: config.multiplier,
|
25
|
+
max_elapsed_time: config.max_elapsed_time,
|
26
|
+
intervals: config.intervals,
|
27
|
+
timeout: config.timeout,
|
28
|
+
on: config.on,
|
29
|
+
on_retry: config.on_retry
|
30
30
|
)
|
31
31
|
|
32
32
|
start_time = Time.now
|
@@ -46,12 +46,12 @@ module Retriable
|
|
46
46
|
|
47
47
|
exception_list = on.kind_of?(Hash) ? on.keys : on
|
48
48
|
|
49
|
-
intervals.each.with_index(1) do |interval,
|
49
|
+
intervals.each.with_index(1) do |interval, try|
|
50
50
|
begin
|
51
51
|
if timeout
|
52
|
-
Timeout::timeout(timeout) { return yield(
|
52
|
+
Timeout::timeout(timeout) { return yield(try) }
|
53
53
|
else
|
54
|
-
return yield(
|
54
|
+
return yield(try)
|
55
55
|
end
|
56
56
|
rescue *[*exception_list] => exception
|
57
57
|
if on.kind_of?(Hash)
|
@@ -66,8 +66,8 @@ module Retriable
|
|
66
66
|
raise unless message_match
|
67
67
|
end
|
68
68
|
|
69
|
-
on_retry.call(exception,
|
70
|
-
raise if
|
69
|
+
on_retry.call(exception, try, elapsed_time.call, interval) if on_retry
|
70
|
+
raise if try >= tries || (elapsed_time.call + interval) > max_elapsed_time
|
71
71
|
sleep interval if config.sleep_disabled != true
|
72
72
|
end
|
73
73
|
end
|
data/lib/retriable/config.rb
CHANGED
@@ -7,11 +7,11 @@ module Retriable
|
|
7
7
|
attr_accessor :rand_factor
|
8
8
|
|
9
9
|
def initialize(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
tries: Retriable.config.tries,
|
11
|
+
base_interval: Retriable.config.base_interval,
|
12
|
+
multiplier: Retriable.config.multiplier,
|
13
|
+
max_interval: Retriable.config.max_interval,
|
14
|
+
rand_factor: Retriable.config.rand_factor
|
15
15
|
)
|
16
16
|
|
17
17
|
@tries = tries
|
@@ -31,13 +31,13 @@ module Retriable
|
|
31
31
|
intervals.map { |i| randomize(i) }
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
private
|
35
35
|
def randomize(interval)
|
36
36
|
return interval if rand_factor == 0
|
37
37
|
delta = rand_factor * interval * 1.0
|
38
|
-
|
39
|
-
|
40
|
-
rand(
|
38
|
+
min = interval - delta
|
39
|
+
max = interval + delta
|
40
|
+
rand(min..max)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
data/lib/retriable/version.rb
CHANGED
data/retriable.gemspec
CHANGED
@@ -1,31 +1,30 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
require "retriable/version"
|
5
5
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
s.summary = %q{Retriable is an simple DSL to retry failed code blocks with randomized exponential backoff}
|
14
|
-
s.description = %q{Retriable is an simple DSL to retry failed code blocks with randomized exponential backoff. This is especially useful when interacting external api/services or file system calls.
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "retriable"
|
8
|
+
spec.version = Retriable::VERSION
|
9
|
+
spec.authors = ["Jack Chu"]
|
10
|
+
spec.email = ["jack@jackchu.com"]
|
11
|
+
spec.summary = %q{Retriable is an simple DSL to retry failed code blocks with randomized exponential backoff}
|
12
|
+
spec.description = %q{Retriable is an simple DSL to retry failed code blocks with randomized exponential backoff. This is especially useful when interacting external api/services or file system calls.
|
15
13
|
}
|
16
|
-
|
14
|
+
spec.homepage = %q{http://github.com/kamui/retriable}
|
15
|
+
spec.license = "MIT"
|
17
16
|
|
18
|
-
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
19
21
|
|
20
|
-
|
21
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
-
s.require_paths = ["lib"]
|
22
|
+
spec.required_ruby_version = '>= 2.0.0'
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
|
27
|
+
spec.add_development_dependency "minitest", "~> 5.4"
|
28
|
+
spec.add_development_dependency "guard"
|
29
|
+
spec.add_development_dependency "guard-minitest"
|
31
30
|
end
|
@@ -5,6 +5,10 @@ describe Retriable::ExponentialBackoff do
|
|
5
5
|
Retriable::ExponentialBackoff
|
6
6
|
end
|
7
7
|
|
8
|
+
before do
|
9
|
+
srand 0
|
10
|
+
end
|
11
|
+
|
8
12
|
it "tries defaults to 3" do
|
9
13
|
subject.new.tries.must_equal 3
|
10
14
|
end
|
@@ -21,29 +25,71 @@ describe Retriable::ExponentialBackoff do
|
|
21
25
|
subject.new.multiplier.must_equal 1.5
|
22
26
|
end
|
23
27
|
|
24
|
-
it "generates randomized intervals" do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
it "generates 10 randomized intervals" do
|
29
|
+
subject.new(tries: 9).intervals.must_equal([
|
30
|
+
0.5244067512211441,
|
31
|
+
0.9113920238761231,
|
32
|
+
1.2406087918999114,
|
33
|
+
1.7632403621664823,
|
34
|
+
2.338001204738311,
|
35
|
+
4.350816718580626,
|
36
|
+
5.339852157217869,
|
37
|
+
11.889873261212443,
|
38
|
+
18.756037881636484
|
39
|
+
])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "generates defined number of intervals" do
|
43
|
+
subject.new(tries: 5).intervals.size.must_equal 5
|
44
|
+
end
|
45
|
+
|
46
|
+
it "generates intervals with a defined base interval" do
|
47
|
+
subject.new(base_interval: 1).intervals.must_equal([
|
48
|
+
1.0488135024422882,
|
49
|
+
1.8227840477522461,
|
50
|
+
2.4812175837998227
|
51
|
+
])
|
52
|
+
end
|
53
|
+
|
54
|
+
it "generates intervals with a defined multiplier" do
|
55
|
+
subject.new(multiplier: 1).intervals.must_equal([
|
56
|
+
0.5244067512211441,
|
57
|
+
0.607594682584082,
|
58
|
+
0.5513816852888495
|
59
|
+
])
|
60
|
+
end
|
61
|
+
|
62
|
+
it "generates intervals with a defined max interval" do
|
63
|
+
subject.new(max_interval: 1.0, rand_factor: 0.0).intervals.must_equal([
|
64
|
+
0.5,
|
65
|
+
0.75,
|
66
|
+
1.0
|
67
|
+
])
|
68
|
+
end
|
69
|
+
|
70
|
+
it "generates intervals with a defined rand_factor" do
|
71
|
+
subject.new(rand_factor: 0.2).intervals.must_equal([
|
72
|
+
0.5097627004884576,
|
73
|
+
0.8145568095504492,
|
74
|
+
1.1712435167599646
|
75
|
+
])
|
35
76
|
end
|
36
77
|
|
37
|
-
it "generates
|
78
|
+
it "generates 10 non-randomized intervals" do
|
38
79
|
subject.new(
|
39
|
-
tries:
|
80
|
+
tries: 10,
|
40
81
|
rand_factor: 0.0
|
41
82
|
).intervals.must_equal([
|
42
83
|
0.5,
|
43
84
|
0.75,
|
44
85
|
1.125,
|
45
86
|
1.6875,
|
46
|
-
2.53125
|
87
|
+
2.53125,
|
88
|
+
3.796875,
|
89
|
+
5.6953125,
|
90
|
+
8.54296875,
|
91
|
+
12.814453125,
|
92
|
+
19.2216796875
|
47
93
|
])
|
48
94
|
end
|
49
95
|
end
|
data/spec/retriable_spec.rb
CHANGED
@@ -7,6 +7,10 @@ describe Retriable do
|
|
7
7
|
Retriable
|
8
8
|
end
|
9
9
|
|
10
|
+
before do
|
11
|
+
srand 0
|
12
|
+
end
|
13
|
+
|
10
14
|
describe "with sleep disabled" do
|
11
15
|
before do
|
12
16
|
Retriable.configure do |c|
|
@@ -14,110 +18,128 @@ describe Retriable do
|
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
17
|
-
it "stops at first
|
18
|
-
|
21
|
+
it "stops at first try if the block does not raise an exception" do
|
22
|
+
tries = 0
|
19
23
|
subject.retriable do
|
20
|
-
|
24
|
+
tries += 1
|
21
25
|
end
|
22
26
|
|
23
|
-
|
27
|
+
tries.must_equal 1
|
24
28
|
end
|
25
29
|
|
26
30
|
it "raises a LocalJumpError if #retriable is not given a block" do
|
27
31
|
-> do
|
28
|
-
subject.retriable on:
|
32
|
+
subject.retriable on: StandardError
|
29
33
|
end.must_raise LocalJumpError
|
30
34
|
|
31
35
|
-> do
|
32
|
-
subject.retriable on:
|
36
|
+
subject.retriable on: StandardError, timeout: 2
|
33
37
|
end.must_raise LocalJumpError
|
34
38
|
end
|
35
39
|
|
36
|
-
|
37
|
-
|
38
|
-
@attempts = 0
|
40
|
+
it "makes 3 tries when retrying block of code raising StandardError with no arguments" do
|
41
|
+
tries = 0
|
39
42
|
|
43
|
+
-> do
|
40
44
|
subject.retriable do
|
41
|
-
|
42
|
-
raise
|
45
|
+
tries += 1
|
46
|
+
raise StandardError.new
|
43
47
|
end
|
44
|
-
end
|
48
|
+
end.must_raise StandardError
|
45
49
|
|
46
|
-
|
47
|
-
|
48
|
-
|
50
|
+
tries.must_equal 3
|
51
|
+
end
|
52
|
+
|
53
|
+
it "makes only 1 try when exception raised is not ancestor of StandardError" do
|
54
|
+
tries = 0
|
55
|
+
|
56
|
+
-> do
|
57
|
+
subject.retriable do
|
58
|
+
tries += 1
|
59
|
+
raise TestError.new
|
60
|
+
end
|
61
|
+
end.must_raise TestError
|
62
|
+
|
63
|
+
tries.must_equal 1
|
49
64
|
end
|
50
65
|
|
51
|
-
it "#retriable
|
66
|
+
it "#retriable with custom exception tries 3 times and re-raises the exception" do
|
67
|
+
tries = 0
|
52
68
|
-> do
|
53
69
|
subject.retriable on: TestError do
|
70
|
+
tries += 1
|
54
71
|
raise TestError.new
|
55
72
|
end
|
56
73
|
end.must_raise TestError
|
74
|
+
|
75
|
+
tries.must_equal 3
|
57
76
|
end
|
58
77
|
|
59
78
|
it "#retriable tries 10 times" do
|
60
|
-
|
79
|
+
tries = 0
|
61
80
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
81
|
+
-> do
|
82
|
+
subject.retriable(
|
83
|
+
tries: 10
|
84
|
+
) do
|
85
|
+
tries += 1
|
86
|
+
raise StandardError.new
|
87
|
+
end
|
88
|
+
end.must_raise StandardError
|
68
89
|
|
69
|
-
|
90
|
+
tries.must_equal 10
|
70
91
|
end
|
71
92
|
|
72
93
|
it "#retriable will timeout after 1 second" do
|
73
94
|
-> do
|
74
95
|
subject.retriable timeout: 1 do
|
75
|
-
sleep
|
96
|
+
sleep 1.1
|
76
97
|
end
|
77
98
|
end.must_raise Timeout::Error
|
78
99
|
end
|
79
100
|
|
80
|
-
it "applies a randomized exponential backoff to each
|
81
|
-
@
|
82
|
-
@time_table =
|
101
|
+
it "applies a randomized exponential backoff to each try" do
|
102
|
+
@tries = 0
|
103
|
+
@time_table = []
|
83
104
|
|
84
|
-
handler = ->(exception,
|
105
|
+
handler = ->(exception, try, elapsed_time, next_interval) do
|
85
106
|
exception.class.must_equal ArgumentError
|
86
|
-
@time_table
|
107
|
+
@time_table << next_interval
|
87
108
|
end
|
88
109
|
|
89
110
|
-> do
|
90
111
|
Retriable.retriable(
|
91
112
|
on: [EOFError, ArgumentError],
|
92
113
|
on_retry: handler,
|
93
|
-
rand_factor: 0.0,
|
94
114
|
tries: 9
|
95
115
|
) do
|
96
|
-
@
|
116
|
+
@tries += 1
|
97
117
|
raise ArgumentError.new
|
98
118
|
end
|
99
119
|
end.must_raise ArgumentError
|
100
120
|
|
101
|
-
@time_table
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
121
|
+
@time_table.must_equal([
|
122
|
+
0.5244067512211441,
|
123
|
+
0.9113920238761231,
|
124
|
+
1.2406087918999114,
|
125
|
+
1.7632403621664823,
|
126
|
+
2.338001204738311,
|
127
|
+
4.350816718580626,
|
128
|
+
5.339852157217869,
|
129
|
+
11.889873261212443,
|
130
|
+
18.756037881636484
|
131
|
+
])
|
110
132
|
end
|
111
133
|
|
112
134
|
describe "retries with an on_#retriable handler, 6 max retries, and a 0.0 rand_factor" do
|
113
135
|
before do
|
114
136
|
tries = 6
|
115
|
-
@
|
137
|
+
@tries = 0
|
116
138
|
@time_table = {}
|
117
139
|
|
118
|
-
handler = ->(exception,
|
140
|
+
handler = ->(exception, try, elapsed_time, next_interval) do
|
119
141
|
exception.class.must_equal ArgumentError
|
120
|
-
@time_table[
|
142
|
+
@time_table[try] = next_interval
|
121
143
|
end
|
122
144
|
|
123
145
|
Retriable.retriable(
|
@@ -126,16 +148,16 @@ describe Retriable do
|
|
126
148
|
rand_factor: 0.0,
|
127
149
|
tries: tries
|
128
150
|
) do
|
129
|
-
@
|
130
|
-
raise ArgumentError.new if @
|
151
|
+
@tries += 1
|
152
|
+
raise ArgumentError.new if @tries < tries
|
131
153
|
end
|
132
154
|
end
|
133
155
|
|
134
|
-
it "makes 6
|
135
|
-
@
|
156
|
+
it "makes 6 tries" do
|
157
|
+
@tries.must_equal 6
|
136
158
|
end
|
137
159
|
|
138
|
-
it "applies a non-randomized exponential backoff to each
|
160
|
+
it "applies a non-randomized exponential backoff to each try" do
|
139
161
|
@time_table.must_equal({
|
140
162
|
1 => 0.5,
|
141
163
|
2 => 0.75,
|
@@ -147,24 +169,25 @@ describe Retriable do
|
|
147
169
|
end
|
148
170
|
|
149
171
|
it "#retriable has a max interval of 1.5 seconds" do
|
150
|
-
tries =
|
151
|
-
attempts = 0
|
172
|
+
tries = 0
|
152
173
|
time_table = {}
|
153
174
|
|
154
|
-
handler = ->(exception,
|
155
|
-
time_table[
|
175
|
+
handler = ->(exception, try, elapsed_time, next_interval) do
|
176
|
+
time_table[try] = next_interval
|
156
177
|
end
|
157
178
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
179
|
+
-> do
|
180
|
+
subject.retriable(
|
181
|
+
on: StandardError,
|
182
|
+
on_retry: handler,
|
183
|
+
rand_factor: 0.0,
|
184
|
+
tries: 5,
|
185
|
+
max_interval: 1.5
|
186
|
+
) do
|
187
|
+
tries += 1
|
188
|
+
raise StandardError.new
|
189
|
+
end
|
190
|
+
end.must_raise StandardError
|
168
191
|
|
169
192
|
time_table.must_equal({
|
170
193
|
1 => 0.5,
|
@@ -175,7 +198,7 @@ describe Retriable do
|
|
175
198
|
})
|
176
199
|
end
|
177
200
|
|
178
|
-
it "#retriable with defined intervals" do
|
201
|
+
it "#retriable with custom defined intervals" do
|
179
202
|
intervals = [
|
180
203
|
0.5,
|
181
204
|
0.75,
|
@@ -185,19 +208,18 @@ describe Retriable do
|
|
185
208
|
]
|
186
209
|
time_table = {}
|
187
210
|
|
188
|
-
handler = ->(exception,
|
189
|
-
time_table[
|
211
|
+
handler = ->(exception, try, elapsed_time, next_interval) do
|
212
|
+
time_table[try] = next_interval
|
190
213
|
end
|
191
214
|
|
192
215
|
-> do
|
193
216
|
subject.retriable(
|
194
|
-
on: EOFError,
|
195
217
|
on_retry: handler,
|
196
218
|
intervals: intervals
|
197
219
|
) do
|
198
|
-
raise
|
220
|
+
raise StandardError.new
|
199
221
|
end
|
200
|
-
end.must_raise
|
222
|
+
end.must_raise StandardError
|
201
223
|
|
202
224
|
time_table.must_equal({
|
203
225
|
1 => 0.5,
|
@@ -219,22 +241,22 @@ describe Retriable do
|
|
219
241
|
end
|
220
242
|
|
221
243
|
it "#retriable with a hash exception list where the values are exception message patterns" do
|
222
|
-
|
223
|
-
|
224
|
-
handler = ->(exception,
|
225
|
-
|
244
|
+
tries = 0
|
245
|
+
exceptions = []
|
246
|
+
handler = ->(exception, try, elapsed_time, next_interval) do
|
247
|
+
exceptions[try] = exception
|
226
248
|
end
|
227
249
|
|
228
250
|
e = -> do
|
229
|
-
subject.retriable tries: 4, on: {
|
230
|
-
|
231
|
-
case
|
251
|
+
subject.retriable tries: 4, on: { StandardError => nil, TestError => [/foo/, /bar/] }, on_retry: handler do
|
252
|
+
tries += 1
|
253
|
+
case tries
|
232
254
|
when 1
|
233
255
|
raise TestError.new('foo')
|
234
256
|
when 2
|
235
257
|
raise TestError.new('bar')
|
236
258
|
when 3
|
237
|
-
raise
|
259
|
+
raise StandardError.new
|
238
260
|
else
|
239
261
|
raise TestError.new('crash')
|
240
262
|
end
|
@@ -242,11 +264,11 @@ describe Retriable do
|
|
242
264
|
end.must_raise TestError
|
243
265
|
|
244
266
|
e.message.must_equal "crash"
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
267
|
+
exceptions[1].class.must_equal TestError
|
268
|
+
exceptions[1].message.must_equal "foo"
|
269
|
+
exceptions[2].class.must_equal TestError
|
270
|
+
exceptions[2].message.must_equal "bar"
|
271
|
+
exceptions[3].class.must_equal StandardError
|
250
272
|
end
|
251
273
|
|
252
274
|
it "#retriable can be called in the global scope" do
|
@@ -258,12 +280,16 @@ describe Retriable do
|
|
258
280
|
|
259
281
|
require_relative "../lib/retriable/core_ext/kernel"
|
260
282
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
283
|
+
tries = 0
|
284
|
+
|
285
|
+
-> do
|
286
|
+
retriable do
|
287
|
+
tries += 1
|
288
|
+
raise StandardError.new
|
289
|
+
end
|
290
|
+
end.must_raise StandardError
|
291
|
+
|
292
|
+
tries.must_equal 3
|
267
293
|
end
|
268
294
|
end
|
269
295
|
|
@@ -274,11 +300,11 @@ describe Retriable do
|
|
274
300
|
|
275
301
|
subject.config.sleep_disabled.must_equal false
|
276
302
|
|
277
|
-
|
303
|
+
tries = 0
|
278
304
|
time_table = {}
|
279
305
|
|
280
|
-
handler = ->(exception,
|
281
|
-
time_table[
|
306
|
+
handler = ->(exception, try, elapsed_time, next_interval) do
|
307
|
+
time_table[try] = elapsed_time
|
282
308
|
end
|
283
309
|
|
284
310
|
-> do
|
@@ -289,11 +315,11 @@ describe Retriable do
|
|
289
315
|
max_elapsed_time: 2.0,
|
290
316
|
on_retry: handler
|
291
317
|
) do
|
292
|
-
|
318
|
+
tries += 1
|
293
319
|
raise EOFError.new
|
294
320
|
end
|
295
321
|
end.must_raise EOFError
|
296
322
|
|
297
|
-
|
323
|
+
tries.must_equal 2
|
298
324
|
end
|
299
325
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,21 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
require "simplecov"
|
3
|
+
|
4
|
+
CodeClimate::TestReporter.configure do |config|
|
5
|
+
config.logger.level = Logger::WARN
|
6
|
+
end
|
7
|
+
|
8
|
+
SimpleCov.start do
|
9
|
+
formatter SimpleCov::Formatter::MultiFormatter[
|
10
|
+
SimpleCov::Formatter::HTMLFormatter,
|
11
|
+
CodeClimate::TestReporter::Formatter
|
12
|
+
]
|
13
|
+
add_filter 'spec/'
|
14
|
+
end
|
15
|
+
|
1
16
|
require "minitest/autorun"
|
2
17
|
require "minitest/focus"
|
18
|
+
require "awesome_print"
|
3
19
|
require "pry"
|
4
20
|
|
5
21
|
require_relative "../lib/retriable"
|
data/wercker.yml
CHANGED
@@ -6,8 +6,8 @@ build:
|
|
6
6
|
# http://devcenter.wercker.com/articles/languages/ruby.html
|
7
7
|
steps:
|
8
8
|
# Uncomment this to force RVM to use a specific Ruby version
|
9
|
-
|
10
|
-
|
9
|
+
- rvm-use:
|
10
|
+
version: 2.1.5
|
11
11
|
|
12
12
|
# A step that executes `bundle install` command
|
13
13
|
- bundle-install
|
metadata
CHANGED
@@ -1,71 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: retriable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Chu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: minitest
|
14
|
+
name: bundler
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
|
-
- - "
|
17
|
+
- - "~>"
|
32
18
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
19
|
+
version: '1.7'
|
34
20
|
type: :development
|
35
21
|
prerelease: false
|
36
22
|
version_requirements: !ruby/object:Gem::Requirement
|
37
23
|
requirements:
|
38
|
-
- - "
|
24
|
+
- - "~>"
|
39
25
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
26
|
+
version: '1.7'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
28
|
+
name: rake
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
|
-
- - "
|
31
|
+
- - "~>"
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
33
|
+
version: '10.0'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
|
-
- - "
|
38
|
+
- - "~>"
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
40
|
+
version: '10.0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
42
|
+
name: minitest
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
|
-
- - "
|
45
|
+
- - "~>"
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
47
|
+
version: '5.4'
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
|
-
- - "
|
52
|
+
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
54
|
+
version: '5.4'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: guard
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -133,15 +119,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
133
119
|
requirements:
|
134
120
|
- - ">="
|
135
121
|
- !ruby/object:Gem::Version
|
136
|
-
version:
|
122
|
+
version: 2.0.0
|
137
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
124
|
requirements:
|
139
|
-
- - "
|
125
|
+
- - ">="
|
140
126
|
- !ruby/object:Gem::Version
|
141
|
-
version:
|
127
|
+
version: '0'
|
142
128
|
requirements: []
|
143
|
-
rubyforge_project:
|
144
|
-
rubygems_version: 2.
|
129
|
+
rubyforge_project:
|
130
|
+
rubygems_version: 2.4.5
|
145
131
|
signing_key:
|
146
132
|
specification_version: 4
|
147
133
|
summary: Retriable is an simple DSL to retry failed code blocks with randomized exponential
|