ione-rpc 1.0.0.pre4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ione/rpc/client.rb +31 -11
- data/lib/ione/rpc/client_peer.rb +20 -3
- data/lib/ione/rpc/peer.rb +4 -0
- data/lib/ione/rpc/server.rb +2 -1
- data/lib/ione/rpc/version.rb +1 -1
- data/lib/ione/rpc.rb +3 -0
- data/spec/ione/rpc/client_peer_spec.rb +28 -3
- data/spec/ione/rpc/client_spec.rb +23 -3
- data/spec/ione/rpc/peer_common.rb +15 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 518c9857895955f9c26c4be3d3be5d21773c7525
|
4
|
+
data.tar.gz: 6b6c39ffa664e4b290bb5c0ebaeab462dd8fbb23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3e6c4d47dda87d60ac7034243d0c946af0117bd8fed3b043d23ecbbbc6cb6817253e3b5199bb5967d38b077d51ed40a4991d04de29e93cd7adbb0e12098427e
|
7
|
+
data.tar.gz: 9f60335dcdb60a2353fcf07684fdd9e9f17715c679253badb1f6415e4e276de67759bfe427392e48266db2216d7a7be3a816dbb2543e6eeb9152e53c702926bb
|
data/lib/ione/rpc/client.rb
CHANGED
@@ -179,31 +179,34 @@ module Ione
|
|
179
179
|
# error response – that is protocol specific and up to the implementation
|
180
180
|
# to handle), or when there was no connection open.
|
181
181
|
def send_request(request, connection=nil, timeout=nil)
|
182
|
-
|
182
|
+
if connection
|
183
|
+
chosen_connection = connection
|
184
|
+
else
|
183
185
|
@lock.lock
|
184
186
|
begin
|
185
|
-
|
187
|
+
chosen_connection = choose_connection(@connections, request)
|
186
188
|
ensure
|
187
189
|
@lock.unlock
|
188
190
|
end
|
189
191
|
end
|
190
|
-
if
|
191
|
-
f =
|
192
|
+
if chosen_connection && !chosen_connection.closed?
|
193
|
+
f = chosen_connection.send_message(request, timeout)
|
192
194
|
f = f.fallback do |error|
|
193
|
-
if error.is_a?(
|
195
|
+
if error.is_a?(Rpc::ConnectionClosedError)
|
194
196
|
@logger.warn('Request failed because the connection closed, retrying') if @logger
|
195
|
-
send_request(request)
|
197
|
+
send_request(request, connection, timeout)
|
196
198
|
else
|
197
|
-
|
199
|
+
Ione::Future.failed(error)
|
198
200
|
end
|
199
201
|
end
|
200
202
|
f.on_failure do |error|
|
201
203
|
@logger.warn('Request failed: %s' % error.message) if @logger
|
202
204
|
end
|
203
205
|
f
|
206
|
+
elsif chosen_connection
|
207
|
+
Future.failed(Rpc::RequestNotSentError.new('Not connected'))
|
204
208
|
else
|
205
|
-
|
206
|
-
Future.failed(Io::ConnectionError.new('Not connected'))
|
209
|
+
Future.failed(Rpc::NoConnectionError.new('No connection'))
|
207
210
|
end
|
208
211
|
rescue => e
|
209
212
|
Future.failed(e)
|
@@ -252,6 +255,19 @@ module Ione
|
|
252
255
|
connections.sample
|
253
256
|
end
|
254
257
|
|
258
|
+
# Override this method to control if, and how many times, the client should
|
259
|
+
# attempt to reconnect on connection failures.
|
260
|
+
#
|
261
|
+
# You can, for example, stop reconnecting after a certain number of attempts.
|
262
|
+
#
|
263
|
+
# @param [String] host the host to connect to
|
264
|
+
# @param [Integer] port the port to connect to
|
265
|
+
# @param [Integer] attempts the number of attempts that have been made so
|
266
|
+
# far – when 1 or above a connection attempt has just failed, when 0
|
267
|
+
# an open connection was abruptly closed and the question is whether or
|
268
|
+
# not to attempt to connect again.
|
269
|
+
# @return [Boolean] `true` if a connection attempt should be made, `false`
|
270
|
+
# otherwise.
|
255
271
|
def reconnect?(host, port, attempts)
|
256
272
|
true
|
257
273
|
end
|
@@ -288,7 +304,7 @@ module Ione
|
|
288
304
|
else
|
289
305
|
@logger.info('Not reconnecting to %s:%d' % [host, port]) if @logger
|
290
306
|
remove_host(host, port)
|
291
|
-
|
307
|
+
Ione::Future.failed(e)
|
292
308
|
end
|
293
309
|
end
|
294
310
|
f.flat_map do |connection|
|
@@ -326,7 +342,11 @@ module Ione
|
|
326
342
|
@logger.info(message) if @logger
|
327
343
|
end
|
328
344
|
@lock.synchronize { @connections.delete(connection) }
|
329
|
-
|
345
|
+
if error && reconnect?(connection.host, connection.port, 0)
|
346
|
+
connect(connection.host, connection.port)
|
347
|
+
else
|
348
|
+
remove_host(connection.host, connection.port)
|
349
|
+
end
|
330
350
|
end
|
331
351
|
|
332
352
|
def normalize_address(host, port)
|
data/lib/ione/rpc/client_peer.rb
CHANGED
@@ -36,6 +36,9 @@ module Ione
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def send_message(request, timeout=nil)
|
39
|
+
if closed?
|
40
|
+
return Ione::Future.failed(Rpc::RequestNotSentError.new('Connection closed'))
|
41
|
+
end
|
39
42
|
promise = Ione::Promise.new
|
40
43
|
channel = nil
|
41
44
|
@lock.lock
|
@@ -117,9 +120,23 @@ module Ione
|
|
117
120
|
end
|
118
121
|
|
119
122
|
def handle_closed(cause=nil)
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
+
in_flight_promises = nil
|
124
|
+
queued_promises = nil
|
125
|
+
@lock.lock
|
126
|
+
begin
|
127
|
+
in_flight_promises = @channels.reject(&:nil?)
|
128
|
+
@channels = [nil] * @channels.size
|
129
|
+
queued_promises = @queue.map(&:last)
|
130
|
+
@queue = []
|
131
|
+
ensure
|
132
|
+
@lock.unlock
|
133
|
+
end
|
134
|
+
message = 'Connection closed'
|
135
|
+
message << ": #{cause.message}" if cause
|
136
|
+
error = Io::ConnectionClosedError.new(message)
|
137
|
+
in_flight_promises.each { |p| p.fail(error) }
|
138
|
+
error = RequestNotSentError.new(message)
|
139
|
+
queued_promises.each { |p| p.fail(error) }
|
123
140
|
super
|
124
141
|
end
|
125
142
|
end
|
data/lib/ione/rpc/peer.rb
CHANGED
data/lib/ione/rpc/server.rb
CHANGED
@@ -107,8 +107,9 @@ module Ione
|
|
107
107
|
|
108
108
|
def setup_server
|
109
109
|
@io_reactor.bind(@bind_address, @port, @queue_length) do |acceptor|
|
110
|
+
@acceptor = acceptor
|
110
111
|
@logger.info('Server listening for connections on %s:%d' % [@bind_address, @port]) if @logger
|
111
|
-
acceptor.on_accept do |connection|
|
112
|
+
@acceptor.on_accept do |connection|
|
112
113
|
@logger.info('Connection from %s:%d accepted' % [connection.host, connection.port]) if @logger
|
113
114
|
peer = ServerPeer.new(connection, @codec, self, @logger)
|
114
115
|
peer.on_closed do
|
data/lib/ione/rpc/version.rb
CHANGED
data/lib/ione/rpc.rb
CHANGED
@@ -59,12 +59,31 @@ module Ione
|
|
59
59
|
include_examples 'peers'
|
60
60
|
|
61
61
|
context 'when the connection closes' do
|
62
|
-
it 'fails all
|
62
|
+
it 'fails all in-flight requests' do
|
63
63
|
f1 = peer.send_message('hello')
|
64
64
|
f2 = peer.send_message('world')
|
65
65
|
connection.closed_listener.call
|
66
|
-
expect { f1.value }.to raise_error(Io::ConnectionClosedError)
|
67
|
-
expect { f2.value }.to raise_error(Io::ConnectionClosedError)
|
66
|
+
expect { f1.value }.to raise_error(Io::ConnectionClosedError, /connection closed/i)
|
67
|
+
expect { f2.value }.to raise_error(Io::ConnectionClosedError, /connection closed/i)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'includes the error message from the cause when there is one' do
|
71
|
+
f = peer.send_message('hello')
|
72
|
+
connection.closed_listener.call(StandardError.new('foo'))
|
73
|
+
expect { f.value }.to raise_error(Io::ConnectionClosedError, /connection closed: foo/i)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'fails queued requests' do
|
77
|
+
fs = Array.new(max_channels + 2) { peer.send_message('foo') }
|
78
|
+
connection.closed_listener.call
|
79
|
+
expect { fs[0].value }.to raise_error(Io::ConnectionClosedError)
|
80
|
+
expect { fs[max_channels].value }.to raise_error(Io::ConnectionClosedError)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'fails queued messages with an error that shows that the request was never sent' do
|
84
|
+
fs = Array.new(max_channels + 2) { peer.send_message('foo') }
|
85
|
+
connection.closed_listener.call
|
86
|
+
expect { fs[max_channels].value }.to raise_error(Rpc::RequestNotSentError)
|
68
87
|
end
|
69
88
|
end
|
70
89
|
|
@@ -127,6 +146,12 @@ module Ione
|
|
127
146
|
expect { f.value }.to_not raise_error
|
128
147
|
end
|
129
148
|
|
149
|
+
it 'fails the request when the connection is closed' do
|
150
|
+
connection.stub(:closed?).and_return(true)
|
151
|
+
f = peer.send_message('foo', 2)
|
152
|
+
expect { f.value }.to raise_error(Rpc::RequestNotSentError)
|
153
|
+
end
|
154
|
+
|
130
155
|
context 'with a non-recoding codec' do
|
131
156
|
let :codec do
|
132
157
|
double(:codec, recoding?: false)
|
@@ -53,7 +53,8 @@ module Ione
|
|
53
53
|
connection.stub(:on_data)
|
54
54
|
connection.stub(:on_closed)
|
55
55
|
connection.stub(:write)
|
56
|
-
connection.stub(:close)
|
56
|
+
connection.stub(:close) { connection.stub(:closed?).and_return(true) }
|
57
|
+
connection.stub(:closed?).and_return(false)
|
57
58
|
connection
|
58
59
|
end
|
59
60
|
|
@@ -214,7 +215,7 @@ module Ione
|
|
214
215
|
|
215
216
|
it 'returns a failed future when called when not connected' do
|
216
217
|
client.stop.value
|
217
|
-
expect { client.send_request('PING').value }.to raise_error(
|
218
|
+
expect { client.send_request('PING').value }.to raise_error(Rpc::NoConnectionError)
|
218
219
|
end
|
219
220
|
end
|
220
221
|
|
@@ -274,13 +275,24 @@ module Ione
|
|
274
275
|
expect { f.value }.to raise_error('Bork')
|
275
276
|
end
|
276
277
|
|
278
|
+
it 'fails the request when #choose_connection returns a closed connection' do
|
279
|
+
client.override_choose_connection do |connections, request|
|
280
|
+
c = connections.first
|
281
|
+
c.close
|
282
|
+
c
|
283
|
+
end
|
284
|
+
client.start.value
|
285
|
+
f = client.send_request('PING')
|
286
|
+
expect { f.value }.to raise_error(Rpc::RequestNotSentError)
|
287
|
+
end
|
288
|
+
|
277
289
|
it 'fails the request when #choose_connection returns nil' do
|
278
290
|
client.override_choose_connection do |connections, request|
|
279
291
|
nil
|
280
292
|
end
|
281
293
|
client.start.value
|
282
294
|
f = client.send_request('PING')
|
283
|
-
expect { f.value }.to raise_error(
|
295
|
+
expect { f.value }.to raise_error(Rpc::NoConnectionError)
|
284
296
|
end
|
285
297
|
end
|
286
298
|
end
|
@@ -510,6 +522,14 @@ module Ione
|
|
510
522
|
connection_attempts_by_host['node1.example.com'].should == 10
|
511
523
|
connection_attempts_by_host['node2.example.com'].should == 1
|
512
524
|
end
|
525
|
+
|
526
|
+
it 'allows the connection to be manually reconnected' do
|
527
|
+
client.start.value
|
528
|
+
c1 = client.created_connections.find { |c| c.host == 'node1.example.com' }
|
529
|
+
c1.closed_listener.call
|
530
|
+
client.add_host(c1.host, c1.port).value
|
531
|
+
io_reactor.should have_received(:connect).with('node1.example.com', anything, anything).twice
|
532
|
+
end
|
513
533
|
end
|
514
534
|
|
515
535
|
context 'with multiple connections' do
|
@@ -120,6 +120,15 @@ shared_examples 'peers' do
|
|
120
120
|
connection.should have_received(:close)
|
121
121
|
end
|
122
122
|
end
|
123
|
+
|
124
|
+
describe '#closed?' do
|
125
|
+
it 'reflects the underlying connection\'s state' do
|
126
|
+
connection.stub(:closed?).and_return(true)
|
127
|
+
peer.should be_closed
|
128
|
+
connection.stub(:closed?).and_return(false)
|
129
|
+
peer.should_not be_closed
|
130
|
+
end
|
131
|
+
end
|
123
132
|
end
|
124
133
|
|
125
134
|
module RpcSpec
|
@@ -130,10 +139,16 @@ module RpcSpec
|
|
130
139
|
@host = 'example.com'
|
131
140
|
@port = 9999
|
132
141
|
@written_bytes = ''
|
142
|
+
@closed = false
|
143
|
+
end
|
144
|
+
|
145
|
+
def closed?
|
146
|
+
@closed
|
133
147
|
end
|
134
148
|
|
135
149
|
def close(cause=nil)
|
136
150
|
@closed_listener.call(cause) if @closed_listener
|
151
|
+
@closed = true
|
137
152
|
end
|
138
153
|
|
139
154
|
def write(bytes)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ione-rpc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theo Hultberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ione
|
@@ -61,9 +61,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
61
|
version: 1.9.3
|
62
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
63
|
requirements:
|
64
|
-
- - '
|
64
|
+
- - '>='
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version:
|
66
|
+
version: '0'
|
67
67
|
requirements: []
|
68
68
|
rubyforge_project:
|
69
69
|
rubygems_version: 2.2.1
|