klomp 0.0.3 → 0.0.4
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.
- data/ChangeLog.md +6 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +7 -1
- data/README.md +20 -0
- data/klomp.gemspec +1 -0
- data/lib/klomp.rb +1 -1
- data/lib/klomp/client.rb +30 -11
- data/test/test_client.rb +120 -6
- metadata +19 -3
data/ChangeLog.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
Changes
|
2
2
|
--------------------------------------------------------------------------------
|
3
3
|
|
4
|
+
0.0.4 (2012/6/22)
|
5
|
+
================================================================================
|
6
|
+
|
7
|
+
- Add fibonacci-based retry/reconnect back-off logic
|
8
|
+
- Add generated UUID message IDs to every message (and log them)
|
9
|
+
|
4
10
|
0.0.3 (2012/6/21)
|
5
11
|
================================================================================
|
6
12
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -5,9 +5,14 @@ GEM
|
|
5
5
|
foreman (0.46.0)
|
6
6
|
thor (>= 0.13.6)
|
7
7
|
json (1.7.1)
|
8
|
+
macaddr (1.6.1)
|
9
|
+
systemu (~> 2.5.0)
|
8
10
|
onstomp (1.0.7)
|
9
11
|
rake (0.9.2.2)
|
12
|
+
systemu (2.5.1)
|
10
13
|
thor (0.15.2)
|
14
|
+
uuid (2.3.5)
|
15
|
+
macaddr (~> 1.0)
|
11
16
|
|
12
17
|
PLATFORMS
|
13
18
|
ruby
|
@@ -16,5 +21,6 @@ DEPENDENCIES
|
|
16
21
|
ZenTest
|
17
22
|
foreman
|
18
23
|
json
|
19
|
-
onstomp
|
24
|
+
onstomp (~> 1.0.7)
|
20
25
|
rake
|
26
|
+
uuid (~> 2.3.5)
|
data/README.md
CHANGED
@@ -36,6 +36,21 @@ manages connections. For example, while the `connected?` method normally
|
|
36
36
|
returns a boolean value, Klomp's `connected?` will return an array of booleans
|
37
37
|
(i.e. one result for each broker).
|
38
38
|
|
39
|
+
### Fibonacci back-off retry behavior
|
40
|
+
|
41
|
+
The OnStomp failover client takes `:retry_attempts` and `:retry_delay` options,
|
42
|
+
and Klomp supports these too. However, if you do not specify either of these
|
43
|
+
values, Klomp's default behavior will be to try to reconnect indefinitely, but
|
44
|
+
use a fibonacci backoff approach, i.e., it will wait `fib(N)` seconds before
|
45
|
+
trying to reconnect on the Nth attempt.
|
46
|
+
|
47
|
+
### Message IDs
|
48
|
+
|
49
|
+
Klomp uses the `uuid` gem to generate per-message unique identifiers. To disable
|
50
|
+
generated message IDs, pass `:uuid => false` in the options hash. To customize
|
51
|
+
to use your own generator, simply pass `:uuid => object` where the object you
|
52
|
+
pass implements a `#generate` method that returns a string ID.
|
53
|
+
|
39
54
|
### Additional options for Klomp::Client
|
40
55
|
|
41
56
|
<table>
|
@@ -59,6 +74,11 @@ returns a boolean value, Klomp's `connected?` will return an array of booleans
|
|
59
74
|
<td>false</td>
|
60
75
|
<td>Logger object</td>
|
61
76
|
</tr>
|
77
|
+
<tr>
|
78
|
+
<td>:uuid</td>
|
79
|
+
<td>UUID.new</td>
|
80
|
+
<td>UUID generator object, responds to :generate and returns an ID</td>
|
81
|
+
</tr>
|
62
82
|
</table>
|
63
83
|
|
64
84
|
## Developers
|
data/klomp.gemspec
CHANGED
data/lib/klomp.rb
CHANGED
data/lib/klomp/client.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
require 'onstomp'
|
2
2
|
require 'onstomp/failover'
|
3
3
|
require 'json'
|
4
|
+
require 'uuid'
|
4
5
|
require 'logger'
|
5
6
|
|
7
|
+
class OnStomp::Failover::Client
|
8
|
+
# Save previous N, N-1 delays for fibonacci backoff
|
9
|
+
attr_accessor :prev_retry_delay
|
10
|
+
end
|
11
|
+
|
6
12
|
module Klomp
|
7
13
|
|
8
14
|
class Client
|
@@ -13,10 +19,13 @@ module Klomp
|
|
13
19
|
@translate_json = options.fetch(:translate_json, true)
|
14
20
|
@auto_reply_to = options.fetch(:auto_reply_to, true)
|
15
21
|
@logger = options.fetch(:logger, nil)
|
22
|
+
@uuid = options.fetch(:uuid) { UUID.new }
|
23
|
+
|
24
|
+
@fib_retry_backoff = !options.has_key?(:retry_attempts) && !options.has_key?(:retry_delay)
|
16
25
|
|
17
26
|
# defaults for retry delay and attempts
|
18
|
-
options[:retry_delay] ||=
|
19
|
-
options[:retry_attempts] ||=
|
27
|
+
options[:retry_delay] ||= 1
|
28
|
+
options[:retry_attempts] ||= -1
|
20
29
|
|
21
30
|
if uri.is_a?(Array)
|
22
31
|
@write_conn = OnStomp::Failover::Client.new(uri, options)
|
@@ -46,16 +55,16 @@ module Klomp
|
|
46
55
|
self
|
47
56
|
end
|
48
57
|
|
49
|
-
def send(
|
50
|
-
if @translate_json &&
|
51
|
-
|
52
|
-
|
53
|
-
args[2][:'content-type'] = 'application/json'
|
58
|
+
def send(dest, body, headers={}, &cb)
|
59
|
+
if @translate_json && body.respond_to?(:to_json)
|
60
|
+
body = body.to_json
|
61
|
+
headers[:'content-type'] = 'application/json'
|
54
62
|
else
|
55
|
-
|
63
|
+
body = body.to_s
|
56
64
|
end
|
57
|
-
|
58
|
-
|
65
|
+
uuid = headers[:id] = @uuid.generate if @uuid
|
66
|
+
log.info("[Sending] ID=#{uuid} Destination=#{dest} Body=#{body.inspect} Headers=#{headers.inspect}") if log
|
67
|
+
@write_conn.send(dest, body, headers, &cb)
|
59
68
|
end
|
60
69
|
alias publish send
|
61
70
|
|
@@ -63,7 +72,7 @@ module Klomp
|
|
63
72
|
frames = []
|
64
73
|
@read_conn.each do |c|
|
65
74
|
frames << c.subscribe(*args) do |msg|
|
66
|
-
log.info("[Received] Body=#{msg.body} Headers=#{msg.headers.to_hash.
|
75
|
+
log.info("[Received] ID=#{msg[:id]} Body=#{msg.body.inspect} Headers=#{msg.headers.to_hash.inspect}") if log
|
67
76
|
if @translate_json
|
68
77
|
msg.body = begin
|
69
78
|
JSON.parse(msg.body)
|
@@ -126,6 +135,16 @@ module Klomp
|
|
126
135
|
def configure_connections
|
127
136
|
klomp_client = self
|
128
137
|
@all_conn.each do |c|
|
138
|
+
if @fib_retry_backoff
|
139
|
+
c.before_failover_retry do |conn, attempt|
|
140
|
+
if attempt == 1
|
141
|
+
conn.prev_retry_delay, conn.retry_delay = 0, 1
|
142
|
+
else
|
143
|
+
conn.prev_retry_delay, conn.retry_delay = conn.retry_delay, conn.prev_retry_delay + conn.retry_delay
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
129
148
|
c.on_failover_connect_failure do
|
130
149
|
klomp_client.last_connect_exception = $!
|
131
150
|
end
|
data/test/test_client.rb
CHANGED
@@ -52,15 +52,14 @@ describe Klomp::Client do
|
|
52
52
|
|
53
53
|
it 'has a logger' do
|
54
54
|
logger = Logger.new(STDOUT)
|
55
|
-
client = Klomp::Client.new(@uris, :logger=>logger)
|
56
|
-
|
55
|
+
client = Klomp::Client.new(@uris, :logger=>logger)
|
57
56
|
assert_equal client.log, logger
|
58
|
-
|
59
|
-
client.disconnect
|
60
57
|
end
|
61
58
|
|
62
59
|
it 'sends heartbeat' do
|
63
|
-
client = Klomp::Client.new(@uris).connect
|
60
|
+
client = Klomp::Client.new(@uris).connect
|
61
|
+
client.beat
|
62
|
+
client.disconnect
|
64
63
|
end
|
65
64
|
|
66
65
|
it 'sends requests and gets responses' do
|
@@ -84,7 +83,7 @@ describe Klomp::Client do
|
|
84
83
|
client = Klomp::Client.new(@uris).connect
|
85
84
|
reply_to_body = { 'reply_to_body' => rand(36**128).to_s(36) }
|
86
85
|
|
87
|
-
client.
|
86
|
+
client.send(@destination, nil, { 'reply-to' => @destination })
|
88
87
|
|
89
88
|
got_message = false
|
90
89
|
client.subscribe(@destination) do |msg|
|
@@ -114,5 +113,120 @@ describe Klomp::Client do
|
|
114
113
|
client = Klomp::Client.new(@uris.first, :haz_cheezburgers => true, :retry_attempts => 42).connect
|
115
114
|
assert client.write_conn.connected?
|
116
115
|
assert_equal 42, client.write_conn.retry_attempts
|
116
|
+
client.disconnect
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'uses a fibonacci back-off approach to reconnect' do
|
120
|
+
good_client = Object.new
|
121
|
+
def good_client.connect; true; end
|
122
|
+
def good_client.connected?; true; end
|
123
|
+
def good_client.connection; true; end
|
124
|
+
|
125
|
+
bad_client = Object.new
|
126
|
+
def bad_client.connect; raise "could not connect"; end
|
127
|
+
def bad_client.connected?; false; end
|
128
|
+
|
129
|
+
test_context = self
|
130
|
+
attempts = 0
|
131
|
+
conn = nil
|
132
|
+
fib = lambda {|n| (1..n).inject([0, 1]) {|fib,_| [fib[1], fib[0]+fib[1]]}.first}
|
133
|
+
|
134
|
+
pool_class = Class.new do
|
135
|
+
def initialize(*) end
|
136
|
+
def each(&blk) end
|
137
|
+
define_method :next_client do
|
138
|
+
attempts += 1
|
139
|
+
test_context.assert_equal fib[attempts], conn.retry_delay
|
140
|
+
if attempts == 6
|
141
|
+
good_client
|
142
|
+
else
|
143
|
+
bad_client
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
client = Klomp::Client.new(@uris.first, :pool => pool_class)
|
149
|
+
conn = client.write_conn
|
150
|
+
def conn.sleep_for_retry(*) end # skip sleep between retries for test
|
151
|
+
|
152
|
+
client.reconnect
|
153
|
+
assert_equal 6, attempts
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'sends messages with uuids in the :id header' do
|
157
|
+
client = Klomp::Client.new(@uris, :translate_json => false).connect
|
158
|
+
client.send(@destination, '')
|
159
|
+
|
160
|
+
received_message = false
|
161
|
+
client.subscribe(@destination) do |msg|
|
162
|
+
received_message = msg
|
163
|
+
end
|
164
|
+
let_background_processor_run
|
165
|
+
assert received_message
|
166
|
+
assert received_message[:id], "message did not have an id"
|
167
|
+
assert received_message[:id] =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
168
|
+
"message id did not look like a uuid"
|
169
|
+
|
170
|
+
client.disconnect
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'allows customization of the uuid generator' do
|
174
|
+
generator = Object.new
|
175
|
+
def generator.generate; "42"; end
|
176
|
+
|
177
|
+
client = Klomp::Client.new(@uris, :translate_json => false, :uuid => generator).connect
|
178
|
+
client.send(@destination, '')
|
179
|
+
|
180
|
+
received_message = false
|
181
|
+
client.subscribe(@destination) do |msg|
|
182
|
+
received_message = msg
|
183
|
+
end
|
184
|
+
let_background_processor_run
|
185
|
+
assert received_message
|
186
|
+
assert received_message[:id], "message did not have an id"
|
187
|
+
assert_equal "42", received_message[:id]
|
188
|
+
|
189
|
+
client.disconnect
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'allows disabling generated message ids' do
|
193
|
+
client = Klomp::Client.new(@uris, :translate_json => false, :uuid => false).connect
|
194
|
+
client.send(@destination, '')
|
195
|
+
|
196
|
+
received_message = false
|
197
|
+
client.subscribe(@destination) do |msg|
|
198
|
+
received_message = msg
|
199
|
+
end
|
200
|
+
let_background_processor_run
|
201
|
+
assert received_message
|
202
|
+
refute received_message[:id], "message had an id"
|
203
|
+
|
204
|
+
client.disconnect
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'logs message ids' do
|
208
|
+
logger = Object.new
|
209
|
+
def logger.msgs; @msgs; end
|
210
|
+
def logger.info(msg) (@msgs ||= []) << msg end
|
211
|
+
|
212
|
+
client = Klomp::Client.new(@uris, :translate_json => false, :logger => logger).connect
|
213
|
+
client.send(@destination, '')
|
214
|
+
|
215
|
+
received_message = false
|
216
|
+
client.subscribe(@destination) do |msg|
|
217
|
+
received_message = msg
|
218
|
+
end
|
219
|
+
let_background_processor_run
|
220
|
+
assert received_message
|
221
|
+
assert received_message[:id], "message did not have an id"
|
222
|
+
|
223
|
+
assert_equal 2, logger.msgs.length
|
224
|
+
assert logger.msgs[0] =~ /\[Sending\] ID=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
|
225
|
+
sent_id = $1
|
226
|
+
assert logger.msgs[1] =~ /\[Received\] ID=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/
|
227
|
+
received_id = $1
|
228
|
+
assert_equal sent_id, received_id
|
229
|
+
|
230
|
+
client.disconnect
|
117
231
|
end
|
118
232
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: klomp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-
|
12
|
+
date: 2012-06-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: onstomp
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: uuid
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.3.5
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.3.5
|
46
62
|
description: A simple wrapper around the OnStomp library with additional features
|
47
63
|
email:
|
48
64
|
- dev.happiness@livingsocial.com
|
@@ -78,7 +94,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
94
|
version: '0'
|
79
95
|
segments:
|
80
96
|
- 0
|
81
|
-
hash:
|
97
|
+
hash: 395786091883431087
|
82
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
99
|
none: false
|
84
100
|
requirements:
|