rhod 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +68 -0
- data/lib/rhod.rb +18 -0
- data/lib/rhod/backoffs.rb +20 -2
- data/lib/rhod/command.rb +12 -3
- data/lib/rhod/version.rb +1 -1
- data/rhod.gemspec +5 -3
- data/test/test_backoffs.rb +14 -0
- data/test/test_command.rb +14 -0
- metadata +28 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99aa65709753429dc68b3979f9397bf2502e13ad
|
4
|
+
data.tar.gz: cd2856de21b568e00fa7bb7b097125ff722704b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2245e308b5b535497b54e24681d7f512ca906b3dd1936897568b2022715063b66ed972ccb33cf0f68efdd81df5ec970533f0e67845aa01cbebd4e11762de4217
|
7
|
+
data.tar.gz: f8b786cde49e380879e0339513d9470f8a9c222e62e3f21eea915a3f821931b43e1b4a22752f64a9fdc8d4de8c76483b961f5fcc6a5326f58bf12f8480301df4
|
data/README.md
CHANGED
@@ -39,6 +39,17 @@ Or install it yourself as:
|
|
39
39
|
|
40
40
|
$ gem install rhod
|
41
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
|
+
```
|
52
|
+
|
42
53
|
## Usage
|
43
54
|
|
44
55
|
Rhod has a very simple API. Design your application as you would normally, then enclose network accessing portions of your code with:
|
@@ -60,6 +71,29 @@ require 'rhod'
|
|
60
71
|
Rhod.execute { open("http://google.com").read }
|
61
72
|
```
|
62
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
|
+
|
63
97
|
### Retries with and without backoffs
|
64
98
|
|
65
99
|
#### Idempotence Caution
|
@@ -117,6 +151,18 @@ Rhod.execute(:retries => 10, :backoffs => 0) do
|
|
117
151
|
end
|
118
152
|
```
|
119
153
|
|
154
|
+
Example, open a remote reasource, fail once it has failed 10 times, with a random wait between 1 and 5 seconds on each attempt
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
require 'open-uri'
|
158
|
+
require 'rhod'
|
159
|
+
|
160
|
+
Rhod.execute(:retries => 10, :backoffs => 'r1..5') do
|
161
|
+
open("http://google.com").read
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
|
120
166
|
### Fail Silent
|
121
167
|
|
122
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.
|
@@ -176,6 +222,28 @@ search_engine_html = SearchEngineHTML.new
|
|
176
222
|
search_engine_html.fetch
|
177
223
|
```
|
178
224
|
|
225
|
+
## Connection Pools
|
226
|
+
|
227
|
+
Sometimes you're connecting to a remote reasource using a driver that doesn't support connection pooling, which will limit the amount of strain your application puts on that reasource, and allow for reuse of existing connections instead of increasing overhead by reconnecting each time. Connection Pool support in Rhod is provided by the [connection_pool](https://github.com/mperham/connection_pool) gem.
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
require 'rhod'
|
231
|
+
require 'redis'
|
232
|
+
require 'connection_pool'
|
233
|
+
|
234
|
+
Rhod.connection_pools[:redis] = ConnectionPool.new(size:3, timeout:5) { Redis.new }
|
235
|
+
|
236
|
+
Rhod.execute(:pool => :redis) {|redis| redis.set("foo", "bar") }
|
237
|
+
```
|
238
|
+
|
239
|
+
The connection is always the first argument passed into the block, the other arguments are passed in their original order after.
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
key = "foo"
|
243
|
+
value = "bar"
|
244
|
+
Rhod.execute(key, value, :pool => :redis) {|redis, k, v| redis.set(k, v) }
|
245
|
+
```
|
246
|
+
|
179
247
|
## Contributing
|
180
248
|
|
181
249
|
1. Fork it
|
data/lib/rhod.rb
CHANGED
@@ -1,9 +1,27 @@
|
|
1
1
|
require_relative "rhod/version"
|
2
2
|
require_relative "rhod/backoffs"
|
3
3
|
require_relative "rhod/command"
|
4
|
+
require 'connection_pool'
|
4
5
|
|
5
6
|
module Rhod
|
6
7
|
def self.execute(*args, &block)
|
7
8
|
Rhod::Command.execute(*args, &block)
|
8
9
|
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :defaults
|
13
|
+
|
14
|
+
attr_accessor :connection_pools
|
15
|
+
end
|
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
|
+
|
9
27
|
end
|
data/lib/rhod/backoffs.rb
CHANGED
@@ -7,13 +7,19 @@ module Rhod::Backoffs
|
|
7
7
|
backoff
|
8
8
|
elsif backoff.is_a?(Numeric)
|
9
9
|
constant_backoff(backoff)
|
10
|
+
elsif backoff.is_a?(Range)
|
11
|
+
random_backoffs(backoff)
|
10
12
|
elsif backoff.is_a?(String)
|
11
|
-
n = (backoff[1..-1].
|
13
|
+
n = (backoff[1..-1].to_f)
|
12
14
|
case backoff[0]
|
13
15
|
when "^"
|
14
16
|
expoential_backoffs(n)
|
15
17
|
when "l"
|
16
18
|
logarithmic_backoffs(n)
|
19
|
+
when "r"
|
20
|
+
min = backoff[1..-1].split("..")[0].to_f
|
21
|
+
max = backoff[1..-1].split("..")[1].to_f
|
22
|
+
random_backoffs((min..max))
|
17
23
|
end
|
18
24
|
elsif backoff.is_a?(Symbol)
|
19
25
|
case backoff
|
@@ -21,6 +27,8 @@ module Rhod::Backoffs
|
|
21
27
|
expoential_backoffs
|
22
28
|
when :l
|
23
29
|
logarithmic_backoffs
|
30
|
+
when :r
|
31
|
+
random_backoffs
|
24
32
|
end
|
25
33
|
end
|
26
34
|
end
|
@@ -46,6 +54,7 @@ module Rhod::Backoffs
|
|
46
54
|
end
|
47
55
|
end
|
48
56
|
end
|
57
|
+
alias default logarithmic_backoffs
|
49
58
|
|
50
59
|
# Always the same backoff
|
51
60
|
def constant_backoff(n)
|
@@ -56,5 +65,14 @@ module Rhod::Backoffs
|
|
56
65
|
end
|
57
66
|
end
|
58
67
|
|
59
|
-
|
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)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
60
78
|
end
|
data/lib/rhod/command.rb
CHANGED
@@ -10,13 +10,17 @@ class Rhod::Command
|
|
10
10
|
@request = block
|
11
11
|
|
12
12
|
@retries = opts[:retries]
|
13
|
-
@retries ||=
|
13
|
+
@retries ||= Rhod.defaults[:retries]
|
14
14
|
@attempts = 0
|
15
15
|
|
16
16
|
@backoffs = Rhod::Backoffs.backoff_sugar_to_enumerator(opts[:backoffs])
|
17
|
-
@backoffs ||= Rhod
|
17
|
+
@backoffs ||= Rhod.defaults[:backoffs]
|
18
18
|
|
19
19
|
@fallback = opts[:fallback]
|
20
|
+
@fallback ||= Rhod.defaults[:fallback]
|
21
|
+
|
22
|
+
@pool = Rhod.connection_pools[opts[:pool]]
|
23
|
+
@pool ||= Rhod.connection_pools[:default]
|
20
24
|
end
|
21
25
|
|
22
26
|
### Class methods
|
@@ -30,7 +34,12 @@ class Rhod::Command
|
|
30
34
|
|
31
35
|
def execute
|
32
36
|
begin
|
33
|
-
@
|
37
|
+
@pool.with do |conn|
|
38
|
+
@args = [conn].concat(@args)
|
39
|
+
@args[0] == nil ? @args.shift : nil
|
40
|
+
|
41
|
+
@request.call(*@args)
|
42
|
+
end
|
34
43
|
rescue *EXCEPTIONS
|
35
44
|
@attempts += 1
|
36
45
|
if @attempts <= @retries
|
data/lib/rhod/version.rb
CHANGED
data/rhod.gemspec
CHANGED
@@ -18,8 +18,10 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.3"
|
21
|
-
spec.add_development_dependency "rake"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0.4"
|
22
22
|
spec.add_development_dependency "pry"
|
23
|
-
spec.add_development_dependency "minitest"
|
24
|
-
spec.add_development_dependency "turn"
|
23
|
+
spec.add_development_dependency "minitest", "~> 4.4.0"
|
24
|
+
spec.add_development_dependency "turn", "~> 0.9.6"
|
25
|
+
|
26
|
+
spec.add_dependency "connection_pool", "~> 1.0.0"
|
25
27
|
end
|
data/test/test_backoffs.rb
CHANGED
@@ -22,6 +22,13 @@ describe Rhod::Backoffs do
|
|
22
22
|
take(3).must_equal [3.169925001442312, 4.0, 4.643856189774724]
|
23
23
|
end
|
24
24
|
|
25
|
+
it "generates random backoffs with 'r' syntax" do
|
26
|
+
sample = Rhod::Backoffs.backoff_sugar_to_enumerator("r1..2").
|
27
|
+
take(3)
|
28
|
+
sample.min.floor.must_equal 1
|
29
|
+
sample.max.ceil.must_equal 2
|
30
|
+
end
|
31
|
+
|
25
32
|
it "generates expoential backoffs with :^ syntax" do
|
26
33
|
Rhod::Backoffs.backoff_sugar_to_enumerator(:^).take(3).must_equal [2.0, 4.0, 8.0]
|
27
34
|
end
|
@@ -30,5 +37,12 @@ describe Rhod::Backoffs do
|
|
30
37
|
Rhod::Backoffs.backoff_sugar_to_enumerator(:l).
|
31
38
|
take(3).must_equal [0.7570232465074598, 2.403267722339301, 3.444932048942182]
|
32
39
|
end
|
40
|
+
|
41
|
+
it "generates random backoffs with :r syntax" do
|
42
|
+
sample = Rhod::Backoffs.backoff_sugar_to_enumerator(:r).
|
43
|
+
take(100)
|
44
|
+
sample.min.floor.must_equal 0
|
45
|
+
sample.max.ceil.must_equal 10
|
46
|
+
end
|
33
47
|
end
|
34
48
|
end
|
data/test/test_command.rb
CHANGED
@@ -72,5 +72,19 @@ describe Rhod::Command do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
75
|
+
|
76
|
+
describe "with connection pools" do
|
77
|
+
it "uses the provided pool" do
|
78
|
+
Rhod.connection_pools[:test] = ConnectionPool.new(size: 1, timeout: 0) { :conn }
|
79
|
+
Rhod::Command.new {|a| a}.execute.must_equal nil
|
80
|
+
Rhod::Command.new(pool: :test) {|a| a}.execute.must_equal :conn
|
81
|
+
end
|
82
|
+
|
83
|
+
it "correctly handles arguements" do
|
84
|
+
Rhod.connection_pools[:test] = ConnectionPool.new(size: 1, timeout: 0) { :conn }
|
85
|
+
Rhod::Command.new(1, pool: :test) {|a, b| [a,b]}.execute.must_equal [:conn, 1]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
75
89
|
end
|
76
90
|
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.0.3
|
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-04-
|
11
|
+
date: 2013-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 10.0.4
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 10.0.4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: pry
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,30 +56,44 @@ dependencies:
|
|
56
56
|
name: minitest
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 4.4.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 4.4.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: turn
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ~>
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 0.9.6
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ~>
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 0.9.6
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: connection_pool
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.0.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.0.0
|
83
97
|
description:
|
84
98
|
email:
|
85
99
|
- paul.d.bergeron@gmail.com
|