rhod 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +8 -0
- data/README.md +64 -168
- data/lib/rhod.rb +6 -16
- data/lib/rhod/backoffs.rb +15 -51
- data/lib/rhod/backoffs/backoff.rb +16 -0
- data/lib/rhod/backoffs/constant.rb +5 -0
- data/lib/rhod/backoffs/exponential.rb +5 -0
- data/lib/rhod/backoffs/logarithmic.rb +5 -0
- data/lib/rhod/backoffs/random.rb +6 -0
- data/lib/rhod/command.rb +8 -6
- data/lib/rhod/profile.rb +37 -0
- data/lib/rhod/version.rb +1 -1
- data/test/test_backoffs.rb +39 -15
- data/test/test_command.rb +5 -5
- data/test/test_profile.rb +30 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1d610e43d6cd0ea21bc4bf99bd68e4728f26cbd
|
4
|
+
data.tar.gz: ea6aba49b9e5df02cedf9988f6ba8915df2e1f99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b30aebc6f171b35d4dc581824d7e0a1466d308e4cc1f4927c5d791d3382ef3b75880cb7e1fa7a122daafd4df45b96c5d978d3ea2e6f377cd933c8cbd6142dc5
|
7
|
+
data.tar.gz: 2997412a10c2d10e3a3a2f2eca8df58387459f95fbe6a5da15f6473cb07383bdc5bc6c21ad62d83e0572154144cac0d324f0e0322843ac11ec09b0fc7f3573aa
|
data/Changes.md
ADDED
data/README.md
CHANGED
@@ -12,214 +12,107 @@ A Lightweight High Avalibility framework for Ruby, inspired by [Hystrix](https:/
|
|
12
12
|
|
13
13
|
Rhod helps you handle failures gracefully, even during a firefight. When your code has to interact with other services, it also means writing code to keep it running in the event of failure. Failures can include exceptions, timeouts, downed hosts, and any number of issues that are caused by events outside of your application.
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
- Fail Fast
|
18
|
-
- Retry N times before Fail
|
19
|
-
- Retry N times with progressive backoffs before Fail
|
20
|
-
- Fail Silent
|
21
|
-
- Fail w/ Fallback
|
22
|
-
- Primary / Secondary ("hot spare") switch over
|
23
|
-
|
24
|
-
## Installation
|
25
|
-
|
26
|
-
Rhod requires Ruby 1.9.2 or greater.
|
27
|
-
|
28
|
-
Add this line to your application's Gemfile:
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
gem 'rhod'
|
32
|
-
```
|
15
|
+
# Is it any good?
|
33
16
|
|
34
|
-
|
35
|
-
|
36
|
-
$ bundle
|
37
|
-
|
38
|
-
Or install it yourself as:
|
39
|
-
|
40
|
-
$ gem install rhod
|
41
|
-
|
42
|
-
## Configuration
|
43
|
-
|
44
|
-
To configure Rhod's defaults, change any of the keys in `Rhod.defaults`
|
45
|
-
|
46
|
-
```ruby
|
47
|
-
Rhod.defaults
|
48
|
-
=> {:retries=>0,
|
49
|
-
:backoffs=>#<Enumerator: ...>,
|
50
|
-
:fallback=>nil}
|
51
|
-
```
|
17
|
+
[Yes](https://news.ycombinator.com/item?id=3067434)
|
52
18
|
|
53
19
|
## Usage
|
54
20
|
|
55
21
|
Rhod has a very simple API. Design your application as you would normally, then enclose network accessing portions of your code with:
|
56
22
|
|
57
23
|
```ruby
|
58
|
-
Rhod.
|
24
|
+
Rhod.with_default do
|
59
25
|
...
|
60
26
|
end
|
61
27
|
```
|
62
28
|
|
63
|
-
This implements the
|
64
|
-
|
65
|
-
Example, open a remote reasource, fail immediately if it fails:
|
66
|
-
|
67
|
-
```ruby
|
68
|
-
require 'open-uri'
|
69
|
-
require 'rhod'
|
70
|
-
|
71
|
-
Rhod.execute { open("http://google.com").read }
|
72
|
-
```
|
73
|
-
|
74
|
-
## An Important note about arguments:
|
75
|
-
|
76
|
-
Do not reach into the outer scope when using Rhod, instead you can pass arguments into your block like so:
|
77
|
-
|
78
|
-
```ruby
|
79
|
-
address = "http://google.com"
|
80
|
-
|
81
|
-
Rhod.execute(address) do |url|
|
82
|
-
open(url).read
|
83
|
-
end
|
84
|
-
```
|
85
|
-
|
86
|
-
If you need to pass options to Rhod, pass them as the last argument:
|
87
|
-
|
88
|
-
```ruby
|
89
|
-
# Works the same as the above, with but with retires.
|
90
|
-
address = "http://google.com"
|
91
|
-
|
92
|
-
Rhod.execute(address, :retries => 5) do |url|
|
93
|
-
open(url).read
|
94
|
-
end
|
95
|
-
```
|
96
|
-
|
97
|
-
### Retries with and without backoffs
|
29
|
+
This implements the [Fail Fast](https://github.com/dinedal/rhod/wiki/Fail-Fast) scenario by default.
|
98
30
|
|
99
|
-
|
100
|
-
|
101
|
-
Code within a `Rhod::Command` block with reties in use must be _idempotent_, i.e., safe to run multiple times.
|
31
|
+
Rhod allows you to fully customize how your application reacts when it can't reach a service it needs. but by default it is configured for a 'fail fast' scenario. With some configuration, Rhod can support the following failure scenarios and variations on them:
|
102
32
|
|
103
|
-
|
33
|
+
- [Fail Fast](https://github.com/dinedal/rhod/wiki/Fail-Fast)
|
34
|
+
- [Retry N times before Fail](https://github.com/dinedal/rhod/wiki/Retry-N-times-before-Fail)
|
35
|
+
- [Retry N times with progressive backoffs before Fail](Retry-N-times-with-progressive-backoffs-before-Fail)
|
36
|
+
- [Fail Silent](https://github.com/dinedal/rhod/wiki/Fail-Silent)
|
37
|
+
- [Fail w/ Fallback](https://github.com/dinedal/rhod/wiki/Fail-with-Fallback)
|
38
|
+
- [Primary / Secondary ("hot spare") switch over](https://github.com/dinedal/rhod/wiki/Primary-Secondary-Switchover)
|
104
39
|
|
105
|
-
|
106
|
-
Rhod::Backoffs.default.take(5)
|
107
|
-
# [0.7570232465074598, 2.403267722339301, 3.444932048942182, 4.208673319629471, 4.811984719351674]
|
108
|
-
```
|
40
|
+
Check the [wiki](https://github.com/dinedal/rhod/wiki/) for more documentation.
|
109
41
|
|
110
|
-
|
42
|
+
## Upgrading from v0.0.x to v0.1.x
|
111
43
|
|
112
|
-
|
44
|
+
The only breaking API change is that backoffs have changed in their creation, dropping `Enumerator` in favor of a simple threadsafe class. Please switch any custom backoff code subclass `Rhod::Backoffs::Backoff`.
|
113
45
|
|
114
|
-
|
115
|
-
require 'open-uri'
|
116
|
-
require 'rhod'
|
46
|
+
## Installation
|
117
47
|
|
118
|
-
Rhod
|
119
|
-
```
|
48
|
+
Rhod requires Ruby 1.9.2 or greater.
|
120
49
|
|
121
|
-
|
50
|
+
Add this line to your application's Gemfile:
|
122
51
|
|
123
52
|
```ruby
|
124
|
-
|
125
|
-
require 'rhod'
|
126
|
-
|
127
|
-
Rhod.execute(:retries => 10, :backoffs => 0.2) do
|
128
|
-
open("http://google.com").read
|
129
|
-
end
|
53
|
+
gem 'rhod'
|
130
54
|
```
|
131
55
|
|
132
|
-
|
133
|
-
|
134
|
-
```ruby
|
135
|
-
require 'open-uri'
|
136
|
-
require 'rhod'
|
56
|
+
And then execute:
|
137
57
|
|
138
|
-
|
139
|
-
open("http://google.com").read
|
140
|
-
end
|
141
|
-
```
|
58
|
+
$ bundle
|
142
59
|
|
143
|
-
|
60
|
+
Or install it yourself as:
|
144
61
|
|
145
|
-
|
146
|
-
require 'open-uri'
|
147
|
-
require 'rhod'
|
62
|
+
$ gem install rhod
|
148
63
|
|
149
|
-
|
150
|
-
open("http://google.com").read
|
151
|
-
end
|
152
|
-
```
|
64
|
+
## Configuration
|
153
65
|
|
154
|
-
|
66
|
+
To configure Rhod's defaults, just overwrite the default profile with any changes you'd like to make.
|
155
67
|
|
156
68
|
```ruby
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
69
|
+
Rhod.create_profile(:default, retries: 10)
|
70
|
+
# => {:retries=>10,
|
71
|
+
# :backoffs=>#<Rhod::Backoffs::Logarithmic:0x007f89afaeb4c0 @state=1.3>,
|
72
|
+
# :fallback=>nil,
|
73
|
+
# :pool=>
|
74
|
+
# #<ConnectionPool:0x007f89afaeb470
|
75
|
+
# @available=
|
76
|
+
# #<ConnectionPool::TimedStack:0x007f89afaeb3d0
|
77
|
+
# @mutex=#<Mutex:0x007f89afaeb358>,
|
78
|
+
# @que=[nil],
|
79
|
+
# @resource=
|
80
|
+
# #<ConditionVariable:0x007f89afaeb330
|
81
|
+
# @waiters={},
|
82
|
+
# @waiters_mutex=#<Mutex:0x007f89afaeb2e0>>>,
|
83
|
+
# @key=:"current-70114667354600",
|
84
|
+
# @size=1,
|
85
|
+
# @timeout=0>,
|
86
|
+
# :exceptions=>[Exception, StandardError]}
|
163
87
|
```
|
164
88
|
|
165
|
-
|
166
|
-
### Fail Silent
|
167
|
-
|
168
|
-
In the event of a failure, Rhod falls back to a `fallback`. The most basic case is to fall back to a constant value.
|
169
|
-
|
170
|
-
Example, open a remote reasource, if it fails return them empty string.
|
89
|
+
Creating a new profile will copy from the default profile any unspecified options:
|
171
90
|
|
172
91
|
```ruby
|
173
|
-
|
174
|
-
|
92
|
+
Rhod.create_profile(:redis,
|
93
|
+
retries: 10,
|
94
|
+
backoffs: :^,
|
95
|
+
pool: ConnectionPool.new(size: 3, timeout: 10) { Redis.new },
|
96
|
+
exceptions: [Redis::BaseError])
|
175
97
|
|
176
|
-
Rhod.
|
177
|
-
|
178
|
-
|
98
|
+
Rhod.with_redis("1") {|r, a| r.set('test',a)}
|
99
|
+
# => "OK"
|
100
|
+
Rhod.with_redis {|r| r.get('test')}
|
101
|
+
# => "1"
|
179
102
|
```
|
180
103
|
|
181
|
-
|
182
|
-
|
183
|
-
If there is another network call that can be used to fetch the reasource, it's possible to use another `Rhod::Command` once a failure has occurred.
|
184
|
-
|
185
|
-
```ruby
|
186
|
-
require 'open-uri'
|
187
|
-
require 'rhod'
|
104
|
+
## Idempotence Caution
|
188
105
|
|
189
|
-
|
190
|
-
:fallback => -> {""} # couldn't get anything
|
191
|
-
) do
|
192
|
-
open("https://yahoo.com").read
|
193
|
-
end
|
106
|
+
Code within a `Rhod::Command` block with reties in use must be _idempotent_, i.e., safe to run multiple times.
|
194
107
|
|
195
|
-
|
196
|
-
open("http://google.com").read
|
197
|
-
end
|
198
|
-
```
|
108
|
+
## Passing arguments
|
199
109
|
|
200
|
-
|
110
|
+
Code within a `Rhod::Command` should avoid leaking memory and/or scope by having arguments passed to it:
|
201
111
|
|
202
|
-
|
112
|
+
### Good use of argument passing:
|
203
113
|
|
204
114
|
```ruby
|
205
|
-
|
206
|
-
require 'rhod'
|
207
|
-
|
208
|
-
class SearchEngineHTML
|
209
|
-
attr_accessor :secondary
|
210
|
-
|
211
|
-
def fetch
|
212
|
-
url = !@secondary ? "http://google.com" : "https://yahoo.com"
|
213
|
-
|
214
|
-
Rhod.execute(url, :fallback => Proc.new { @secondary = !@secondary; fetch }) do |url|
|
215
|
-
open(url).read
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
search_engine_html = SearchEngineHTML.new
|
221
|
-
|
222
|
-
search_engine_html.fetch
|
115
|
+
Rhod.with_default("http://google.com") {|url| open(url).read}
|
223
116
|
```
|
224
117
|
|
225
118
|
## Connection Pools
|
@@ -231,9 +124,12 @@ require 'rhod'
|
|
231
124
|
require 'redis'
|
232
125
|
require 'connection_pool'
|
233
126
|
|
234
|
-
Rhod.
|
127
|
+
Rhod.create_profile(:redis,
|
128
|
+
pool: ConnectionPool.new(size: 3, timeout: 5) { Redis.new }
|
129
|
+
)
|
235
130
|
|
236
|
-
Rhod.
|
131
|
+
Rhod.with_redis {|redis| redis.set("foo", "bar") }
|
132
|
+
# => "OK"
|
237
133
|
```
|
238
134
|
|
239
135
|
The connection is always the first argument passed into the block, the other arguments are passed in their original order after.
|
@@ -241,7 +137,7 @@ The connection is always the first argument passed into the block, the other arg
|
|
241
137
|
```ruby
|
242
138
|
key = "foo"
|
243
139
|
value = "bar"
|
244
|
-
Rhod.
|
140
|
+
Rhod.with_redis(key, value) {|redis, k, v| redis.set(k, v) }
|
245
141
|
```
|
246
142
|
|
247
143
|
## Contributing
|
data/lib/rhod.rb
CHANGED
@@ -1,27 +1,17 @@
|
|
1
|
+
require 'connection_pool'
|
1
2
|
require_relative "rhod/version"
|
2
3
|
require_relative "rhod/backoffs"
|
3
4
|
require_relative "rhod/command"
|
4
|
-
|
5
|
+
require_relative "rhod/profile"
|
5
6
|
|
6
7
|
module Rhod
|
8
|
+
|
7
9
|
def self.execute(*args, &block)
|
8
|
-
Rhod
|
10
|
+
Rhod.with_default(*args, &block)
|
9
11
|
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
attr_accessor :connection_pools
|
13
|
+
def self.create_profile(name, options={})
|
14
|
+
Rhod::Profile.new(name, options)
|
15
15
|
end
|
16
16
|
|
17
|
-
self.defaults = {
|
18
|
-
retries: 0,
|
19
|
-
backoffs: Rhod::Backoffs.default,
|
20
|
-
fallback: nil,
|
21
|
-
}
|
22
|
-
|
23
|
-
self.connection_pools = {
|
24
|
-
default: ConnectionPool.new(size: 1, timeout: 0) { nil }
|
25
|
-
}
|
26
|
-
|
27
17
|
end
|
data/lib/rhod/backoffs.rb
CHANGED
@@ -3,76 +3,40 @@ module Rhod::Backoffs
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
def backoff_sugar_to_enumerator(backoff)
|
6
|
-
if backoff.is_a?(
|
6
|
+
if backoff.is_a?(Rhod::Backoffs::Backoff)
|
7
7
|
backoff
|
8
8
|
elsif backoff.is_a?(Numeric)
|
9
|
-
|
9
|
+
Rhod::Backoffs::Constant.new(backoff)
|
10
10
|
elsif backoff.is_a?(Range)
|
11
|
-
|
11
|
+
Rhod::Backoffs::Random.new(backoff)
|
12
12
|
elsif backoff.is_a?(String)
|
13
13
|
n = (backoff[1..-1].to_f)
|
14
14
|
case backoff[0]
|
15
15
|
when "^"
|
16
|
-
|
16
|
+
Rhod::Backoffs::Exponential.new(n)
|
17
17
|
when "l"
|
18
|
-
|
18
|
+
Rhod::Backoffs::Logarithmic.new(n)
|
19
19
|
when "r"
|
20
20
|
min = backoff[1..-1].split("..")[0].to_f
|
21
21
|
max = backoff[1..-1].split("..")[1].to_f
|
22
|
-
|
22
|
+
Rhod::Backoffs::Random.new((min..max))
|
23
23
|
end
|
24
24
|
elsif backoff.is_a?(Symbol)
|
25
25
|
case backoff
|
26
26
|
when :^
|
27
|
-
|
27
|
+
Rhod::Backoffs::Exponential.new(0)
|
28
28
|
when :l
|
29
|
-
|
29
|
+
Rhod::Backoffs::Logarithmic.new(1.3)
|
30
30
|
when :r
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns a generator of a expoentially increasing series starting at n
|
37
|
-
def expoential_backoffs(n=1)
|
38
|
-
Enumerator.new do |yielder|
|
39
|
-
x = (n - 1)
|
40
|
-
loop do
|
41
|
-
x += 1
|
42
|
-
yielder << 2.0**x
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns a generator of a logarithmicly increasing series starting at n
|
48
|
-
def logarithmic_backoffs(n=0.3)
|
49
|
-
Enumerator.new do |yielder|
|
50
|
-
x = n
|
51
|
-
loop do
|
52
|
-
x += 1
|
53
|
-
yielder << Math.log2(x**2)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
alias default logarithmic_backoffs
|
58
|
-
|
59
|
-
# Always the same backoff
|
60
|
-
def constant_backoff(n)
|
61
|
-
Enumerator.new do |yielder|
|
62
|
-
loop do
|
63
|
-
yielder << n
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# Returns a generator of random numbers falling inside of a range
|
69
|
-
def random_backoffs(range=(0..10))
|
70
|
-
float_range = (range.min.to_f..range.max.to_f)
|
71
|
-
Enumerator.new do |yielder|
|
72
|
-
loop do
|
73
|
-
yielder << rand(float_range)
|
31
|
+
Rhod::Backoffs::Random.new(0..10)
|
74
32
|
end
|
75
33
|
end
|
76
34
|
end
|
77
35
|
|
78
36
|
end
|
37
|
+
|
38
|
+
require_relative 'backoffs/backoff.rb'
|
39
|
+
require_relative 'backoffs/constant.rb'
|
40
|
+
require_relative 'backoffs/exponential.rb'
|
41
|
+
require_relative 'backoffs/logarithmic.rb'
|
42
|
+
require_relative 'backoffs/random.rb'
|
data/lib/rhod/command.rb
CHANGED
@@ -10,17 +10,19 @@ class Rhod::Command
|
|
10
10
|
@request = block
|
11
11
|
|
12
12
|
@retries = opts[:retries]
|
13
|
-
@retries ||=
|
13
|
+
@retries ||= 0
|
14
14
|
@attempts = 0
|
15
15
|
|
16
16
|
@backoffs = Rhod::Backoffs.backoff_sugar_to_enumerator(opts[:backoffs])
|
17
|
-
@backoffs ||= Rhod.
|
17
|
+
@backoffs ||= Rhod::Backoffs::Logarithmic.new(1.3)
|
18
18
|
|
19
19
|
@fallback = opts[:fallback]
|
20
|
-
@fallback ||= Rhod.defaults[:fallback]
|
21
20
|
|
22
|
-
@pool =
|
23
|
-
@pool ||=
|
21
|
+
@pool = opts[:pool]
|
22
|
+
@pool ||= ConnectionPool.new(size: 1, timeout: 0) { nil }
|
23
|
+
|
24
|
+
@exceptions = opts[:exceptions]
|
25
|
+
@exceptions ||= EXCEPTIONS
|
24
26
|
end
|
25
27
|
|
26
28
|
### Class methods
|
@@ -40,7 +42,7 @@ class Rhod::Command
|
|
40
42
|
|
41
43
|
@request.call(*@args)
|
42
44
|
end
|
43
|
-
rescue
|
45
|
+
rescue *@exceptions
|
44
46
|
@attempts += 1
|
45
47
|
if @attempts <= @retries
|
46
48
|
sleep(@backoffs.next)
|
data/lib/rhod/profile.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
class Rhod::Profile < Hash
|
2
|
+
@@profiles = {}
|
3
|
+
|
4
|
+
def initialize(name, options={})
|
5
|
+
# When creating new profiles, copy from the global default, incase it was customized.
|
6
|
+
if @@profiles[:default]
|
7
|
+
default = @@profiles[:default].dup
|
8
|
+
else
|
9
|
+
default = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
default.each {|k,v| self[k] = v }
|
13
|
+
|
14
|
+
options.each {|k,v| self[k] = v }
|
15
|
+
|
16
|
+
# Syntax sugar: named .with_#{profile} methods on this class and the module
|
17
|
+
@@profiles[name] = self
|
18
|
+
|
19
|
+
self.class.__send__(:define_method, :"with_#{name}") do |*args, &block|
|
20
|
+
Rhod::Command.execute(*args, @@profiles[name], &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
Rhod.class.__send__(:define_method, :"with_#{name}") do |*args, &block|
|
24
|
+
Rhod::Command.execute(*args, @@profiles[name], &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Rhod::Profile.new(:default,
|
32
|
+
retries: 0,
|
33
|
+
backoffs: Rhod::Backoffs::Logarithmic.new(1.3),
|
34
|
+
fallback: nil,
|
35
|
+
pool: ConnectionPool.new(size: 1, timeout: 0) { nil },
|
36
|
+
exceptions: [Exception, StandardError],
|
37
|
+
)
|
data/lib/rhod/version.rb
CHANGED
data/test/test_backoffs.rb
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path(File.dirname(__FILE__) + '/helper')
|
|
4
4
|
describe Rhod::Backoffs do
|
5
5
|
describe "backoff_sugar_to_enumerator" do
|
6
6
|
it "returns enumerators as is" do
|
7
|
-
e = Rhod::Backoffs.
|
7
|
+
e = Rhod::Backoffs::Constant.new(0)
|
8
8
|
Rhod::Backoffs.backoff_sugar_to_enumerator(e).must_equal e
|
9
9
|
end
|
10
10
|
|
@@ -14,35 +14,59 @@ describe Rhod::Backoffs do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
it "generates expoential backoffs with '^' syntax" do
|
17
|
-
|
17
|
+
results = []
|
18
|
+
eb = Rhod::Backoffs.backoff_sugar_to_enumerator("^2.0")
|
19
|
+
|
20
|
+
3.times { results << eb.next }
|
21
|
+
|
22
|
+
results.must_equal [4.0, 8.0, 16.0]
|
18
23
|
end
|
19
24
|
|
20
25
|
it "generates logarithmic backoffs with 'l' syntax" do
|
21
|
-
|
22
|
-
|
26
|
+
results = []
|
27
|
+
lb = Rhod::Backoffs.backoff_sugar_to_enumerator("l2.0")
|
28
|
+
|
29
|
+
3.times { results << lb.next }
|
30
|
+
|
31
|
+
results.must_equal [2.0, 3.169925001442312, 4.0]
|
23
32
|
end
|
24
33
|
|
25
34
|
it "generates random backoffs with 'r' syntax" do
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
results = []
|
36
|
+
rb = Rhod::Backoffs.backoff_sugar_to_enumerator("r1..2")
|
37
|
+
|
38
|
+
10.times { results << rb.next }
|
39
|
+
|
40
|
+
results.min.floor.must_equal 1
|
41
|
+
results.max.ceil.must_equal 2
|
30
42
|
end
|
31
43
|
|
32
44
|
it "generates expoential backoffs with :^ syntax" do
|
33
|
-
|
45
|
+
results = []
|
46
|
+
eb = Rhod::Backoffs.backoff_sugar_to_enumerator(:^)
|
47
|
+
|
48
|
+
3.times { results << eb.next }
|
49
|
+
|
50
|
+
results.must_equal [1.0, 2.0, 4.0]
|
34
51
|
end
|
35
52
|
|
36
53
|
it "generates logarithmic backoffs with :l syntax" do
|
37
|
-
|
38
|
-
|
54
|
+
results = []
|
55
|
+
lb = Rhod::Backoffs.backoff_sugar_to_enumerator(:l)
|
56
|
+
|
57
|
+
3.times { results << lb.next }
|
58
|
+
|
59
|
+
results.must_equal [0.7570232465074598, 2.403267722339301, 3.444932048942182]
|
39
60
|
end
|
40
61
|
|
41
62
|
it "generates random backoffs with :r syntax" do
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
63
|
+
results = []
|
64
|
+
rb = Rhod::Backoffs.backoff_sugar_to_enumerator(:r)
|
65
|
+
|
66
|
+
100.times { results << rb.next }
|
67
|
+
|
68
|
+
results.min.floor.must_equal 0
|
69
|
+
results.max.ceil.must_equal 10
|
46
70
|
end
|
47
71
|
end
|
48
72
|
end
|
data/test/test_command.rb
CHANGED
@@ -34,7 +34,7 @@ describe Rhod::Command do
|
|
34
34
|
backoff = MiniTest::Mock.new
|
35
35
|
backoff.expect(:next, 0)
|
36
36
|
|
37
|
-
Rhod::Backoffs.stub(:
|
37
|
+
Rhod::Backoffs::Constant.stub(:new, backoff) do
|
38
38
|
begin
|
39
39
|
Rhod::Command.new(:retries => 1, :backoffs => 0) do
|
40
40
|
val += 1
|
@@ -75,14 +75,14 @@ describe Rhod::Command do
|
|
75
75
|
|
76
76
|
describe "with connection pools" do
|
77
77
|
it "uses the provided pool" do
|
78
|
-
|
78
|
+
pool = ConnectionPool.new(size: 1, timeout: 0) { :conn }
|
79
79
|
Rhod::Command.new {|a| a}.execute.must_equal nil
|
80
|
-
Rhod::Command.new(pool:
|
80
|
+
Rhod::Command.new(pool: pool) {|a| a}.execute.must_equal :conn
|
81
81
|
end
|
82
82
|
|
83
83
|
it "correctly handles arguements" do
|
84
|
-
|
85
|
-
Rhod::Command.new(1, pool:
|
84
|
+
pool = ConnectionPool.new(size: 1, timeout: 0) { :conn }
|
85
|
+
Rhod::Command.new(1, pool: pool) {|a, b| [a,b]}.execute.must_equal [:conn, 1]
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/helper')
|
3
|
+
|
4
|
+
describe Rhod::Profile do
|
5
|
+
describe "defaults" do
|
6
|
+
it "always has them" do
|
7
|
+
assert_instance_of Rhod::Profile, Rhod::Profile.class_variable_get(:@@profiles)[:default]
|
8
|
+
defined?(Rhod.with_default).must_equal "method"
|
9
|
+
defined?(Rhod::Profile.with_default).must_equal "method"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "self.new" do
|
14
|
+
it "copies missing attributes from the defaults" do
|
15
|
+
Rhod::Profile.new(:test1, retries: 55 )
|
16
|
+
Rhod::Profile.class_variable_get(:@@profiles)[:test1][:retries].must_equal 55
|
17
|
+
Rhod::Profile.class_variable_get(:@@profiles)[:test1][:exceptions].must_equal [Exception, StandardError]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "creates a new method on itself and the module for new profiles" do
|
21
|
+
defined?(Rhod.with_test2).must_equal nil
|
22
|
+
defined?(Rhod::Profile.with_test2).must_equal nil
|
23
|
+
|
24
|
+
Rhod::Profile.new(:test2)
|
25
|
+
|
26
|
+
defined?(Rhod.with_test2).must_equal "method"
|
27
|
+
defined?(Rhod::Profile.with_test2).must_equal "method"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rhod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Bergeron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -103,18 +103,26 @@ extra_rdoc_files: []
|
|
103
103
|
files:
|
104
104
|
- .gitignore
|
105
105
|
- .pryrc
|
106
|
+
- Changes.md
|
106
107
|
- Gemfile
|
107
108
|
- LICENSE.txt
|
108
109
|
- README.md
|
109
110
|
- Rakefile
|
110
111
|
- lib/rhod.rb
|
111
112
|
- lib/rhod/backoffs.rb
|
113
|
+
- lib/rhod/backoffs/backoff.rb
|
114
|
+
- lib/rhod/backoffs/constant.rb
|
115
|
+
- lib/rhod/backoffs/exponential.rb
|
116
|
+
- lib/rhod/backoffs/logarithmic.rb
|
117
|
+
- lib/rhod/backoffs/random.rb
|
112
118
|
- lib/rhod/command.rb
|
119
|
+
- lib/rhod/profile.rb
|
113
120
|
- lib/rhod/version.rb
|
114
121
|
- rhod.gemspec
|
115
122
|
- test/helper.rb
|
116
123
|
- test/test_backoffs.rb
|
117
124
|
- test/test_command.rb
|
125
|
+
- test/test_profile.rb
|
118
126
|
homepage: https://github.com/dinedal/rhod
|
119
127
|
licenses:
|
120
128
|
- MIT
|
@@ -143,3 +151,4 @@ test_files:
|
|
143
151
|
- test/helper.rb
|
144
152
|
- test/test_backoffs.rb
|
145
153
|
- test/test_command.rb
|
154
|
+
- test/test_profile.rb
|