resilient_socket 0.4.0 → 0.5.0
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.
- checksums.yaml +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +38 -0
- data/Rakefile +5 -3
- data/lib/resilient_socket/tcp_client.rb +32 -24
- data/lib/resilient_socket/version.rb +1 -1
- data/test/simple_tcp_server.rb +2 -2
- data/test/tcp_client_test.rb +2 -2
- metadata +19 -27
- data/nbproject/private/config.properties +0 -0
- data/nbproject/private/private.properties +0 -3
- data/nbproject/private/private.xml +0 -4
- data/nbproject/private/rake-d.txt +0 -4
- data/nbproject/project.properties +0 -7
- data/nbproject/project.xml +0 -15
- data/test.log.working +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4ffdee0e077d518369067ed0b3248ec8c67f2aa2
|
4
|
+
data.tar.gz: 82f2d747a1bf0caee65bf4428be6d7e47ea83337
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5c7d639d4af9a6493a34fcf14c06e20d36e95fe352608d01484fc230029d672623364ef79430a508cff5b1d8045250962ac8009fe37b823d24bb8698f75ddad4
|
7
|
+
data.tar.gz: 9120d0ee7557ca7f512afe84ed4d3546a2c3c749e5ecd7b1392387db9fbf24ced1363552aa31385f6f1457acdf84d23f32d5399ce6afc90e8de0294cbbc83e42
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.2.12)
|
5
|
+
i18n (~> 0.6)
|
6
|
+
multi_json (~> 1.0)
|
7
|
+
atomic (1.0.1)
|
8
|
+
bourne (1.1.2)
|
9
|
+
mocha (= 0.10.5)
|
10
|
+
bson (1.8.3)
|
11
|
+
bson_ext (1.8.3)
|
12
|
+
bson (~> 1.8.3)
|
13
|
+
i18n (0.6.4)
|
14
|
+
metaclass (0.0.1)
|
15
|
+
mocha (0.10.5)
|
16
|
+
metaclass (~> 0.0.1)
|
17
|
+
multi_json (1.6.1)
|
18
|
+
semantic_logger (2.0.0)
|
19
|
+
sync_attr
|
20
|
+
thread_safe
|
21
|
+
shoulda (3.3.2)
|
22
|
+
shoulda-context (~> 1.0.1)
|
23
|
+
shoulda-matchers (~> 1.4.1)
|
24
|
+
shoulda-context (1.0.2)
|
25
|
+
shoulda-matchers (1.4.2)
|
26
|
+
activesupport (>= 3.0.0)
|
27
|
+
bourne (~> 1.1.2)
|
28
|
+
sync_attr (0.1.1)
|
29
|
+
thread_safe (0.1.0)
|
30
|
+
atomic
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
bson_ext
|
37
|
+
semantic_logger
|
38
|
+
shoulda
|
data/Rakefile
CHANGED
@@ -2,6 +2,7 @@ lib = File.expand_path('../lib/', __FILE__)
|
|
2
2
|
$:.unshift lib unless $:.include?(lib)
|
3
3
|
|
4
4
|
require 'rubygems'
|
5
|
+
require 'rubygems/package'
|
5
6
|
require 'rake/clean'
|
6
7
|
require 'rake/testtask'
|
7
8
|
require 'date'
|
@@ -19,11 +20,12 @@ task :gem do |t|
|
|
19
20
|
spec.date = Date.today.to_s
|
20
21
|
spec.summary = "A Resilient TCP Socket Client with built-in timeouts, retries, and logging"
|
21
22
|
spec.description = "A Resilient TCP Socket Client with built-in timeouts, retries, and logging"
|
22
|
-
spec.files = FileList["./**/*"].exclude(
|
23
|
+
spec.files = FileList["./**/*"].exclude(/\.gem$/, /\.log$/,/nbproject/).map{|f| f.sub(/^\.\//, '')}
|
24
|
+
spec.license = "Apache License V2.0"
|
23
25
|
spec.has_rdoc = true
|
24
|
-
spec.add_dependency 'semantic_logger'
|
26
|
+
spec.add_dependency 'semantic_logger', '>= 2.1'
|
25
27
|
end
|
26
|
-
Gem::
|
28
|
+
Gem::Package.build gemspec
|
27
29
|
end
|
28
30
|
|
29
31
|
desc "Run Test Suite"
|
@@ -29,9 +29,6 @@ module ResilientSocket
|
|
29
29
|
# * write
|
30
30
|
#
|
31
31
|
# Future:
|
32
|
-
#
|
33
|
-
# * Automatic failover to another server should the current server not respond
|
34
|
-
# to a connection request by supplying an array of host names
|
35
32
|
# * Add auto-reconnect feature to sysread, syswrite, etc...
|
36
33
|
# * To be a drop-in replacement to TCPSocket should also need to implement the
|
37
34
|
# following TCPSocket instance methods: :addr, :peeraddr
|
@@ -41,6 +38,8 @@ module ResilientSocket
|
|
41
38
|
# has to be completely destroyed and recreated after a connection failure
|
42
39
|
#
|
43
40
|
class TCPClient
|
41
|
+
include SemanticLogger::Loggable
|
42
|
+
|
44
43
|
# Supports embedding user supplied data along with this connection
|
45
44
|
# such as sequence number and other connection specific information
|
46
45
|
attr_accessor :user_data
|
@@ -49,7 +48,7 @@ module ResilientSocket
|
|
49
48
|
#
|
50
49
|
# Example:
|
51
50
|
# localhost:2000
|
52
|
-
attr_reader :server
|
51
|
+
attr_reader :server
|
53
52
|
|
54
53
|
attr_accessor :read_timeout, :connect_timeout, :connect_retry_count,
|
55
54
|
:retry_count, :connect_retry_interval, :server_selector, :close_on_error
|
@@ -131,10 +130,10 @@ module ResilientSocket
|
|
131
130
|
# Default: Half of the :read_timeout ( 30 seconds )
|
132
131
|
#
|
133
132
|
# :log_level [Symbol]
|
134
|
-
#
|
135
|
-
# Can be used to turn on trace or debug level logging in production
|
133
|
+
# Set the logging level for the TCPClient
|
136
134
|
# Any valid SemanticLogger log level:
|
137
135
|
# :trace, :debug, :info, :warn, :error, :fatal
|
136
|
+
# Default: SemanticLogger.default_level
|
138
137
|
#
|
139
138
|
# :buffered [Boolean]
|
140
139
|
# Whether to use Nagle's Buffering algorithm (http://en.wikipedia.org/wiki/Nagle's_algorithm)
|
@@ -177,6 +176,15 @@ module ResilientSocket
|
|
177
176
|
# :random
|
178
177
|
# Randomly select a server from the list every time a connection
|
179
178
|
# is established, including during automatic connection recovery.
|
179
|
+
# :nearest
|
180
|
+
# FUTURE - Not implemented yet
|
181
|
+
# The server with an IP address that most closely matches the
|
182
|
+
# local ip address will be attempted first
|
183
|
+
# This will result in connections to servers on the localhost
|
184
|
+
# first prior to looking at remote servers
|
185
|
+
# :ping_time
|
186
|
+
# FUTURE - Not implemented yet
|
187
|
+
# The server with the lowest ping time will be selected first
|
180
188
|
# Proc:
|
181
189
|
# When a Proc is supplied, it will be called passing in the list
|
182
190
|
# of servers. The Proc must return one server name
|
@@ -226,8 +234,8 @@ module ResilientSocket
|
|
226
234
|
raise "Missing mandatory :server or :servers" unless server = params.delete(:server)
|
227
235
|
@servers = [ server ]
|
228
236
|
end
|
229
|
-
|
230
|
-
params.each_pair {|k,v|
|
237
|
+
self.logger = SemanticLogger::Logger.new("#{self.class.name} #{@servers.inspect}", params.delete(:log_level))
|
238
|
+
params.each_pair {|k,v| logger.warn "Ignoring unknown option #{k} = #{v}"}
|
231
239
|
|
232
240
|
# Connect to the Server
|
233
241
|
connect
|
@@ -318,12 +326,12 @@ module ResilientSocket
|
|
318
326
|
# For a description of the errors, see Socket#write
|
319
327
|
#
|
320
328
|
def write(data)
|
321
|
-
|
322
|
-
|
329
|
+
logger.trace("#write ==> sending", data)
|
330
|
+
logger.benchmark_debug("#write ==> sent #{data.length} bytes") do
|
323
331
|
begin
|
324
332
|
@socket.write(data)
|
325
333
|
rescue SystemCallError => exception
|
326
|
-
|
334
|
+
logger.warn "#write Connection failure: #{exception.class}: #{exception.message}"
|
327
335
|
close if close_on_error
|
328
336
|
raise ConnectionFailure.new("Send Connection failure: #{exception.class}: #{exception.message}", @server, exception)
|
329
337
|
rescue Exception
|
@@ -371,13 +379,13 @@ module ResilientSocket
|
|
371
379
|
#
|
372
380
|
def read(length, buffer=nil, timeout=nil)
|
373
381
|
result = nil
|
374
|
-
|
382
|
+
logger.benchmark_debug("#read <== read #{length} bytes") do
|
375
383
|
if timeout != -1
|
376
384
|
# Block on data to read for @read_timeout seconds
|
377
385
|
ready = begin
|
378
386
|
ready = IO.select([@socket], nil, [@socket], timeout || @read_timeout)
|
379
387
|
rescue IOError => exception
|
380
|
-
|
388
|
+
logger.warn "#read Connection failure while waiting for data: #{exception.class}: #{exception.message}"
|
381
389
|
close if close_on_error
|
382
390
|
raise ConnectionFailure.new("#{exception.class}: #{exception.message}", @server, exception)
|
383
391
|
rescue Exception
|
@@ -388,7 +396,7 @@ module ResilientSocket
|
|
388
396
|
end
|
389
397
|
unless ready
|
390
398
|
close if close_on_error
|
391
|
-
|
399
|
+
logger.warn "#read Timeout waiting for server to reply"
|
392
400
|
raise ReadTimeout.new("Timedout after #{timeout || @read_timeout} seconds trying to read from #{@server}")
|
393
401
|
end
|
394
402
|
end
|
@@ -396,17 +404,17 @@ module ResilientSocket
|
|
396
404
|
# Read data from socket
|
397
405
|
begin
|
398
406
|
result = buffer.nil? ? @socket.read(length) : @socket.read(length, buffer)
|
399
|
-
|
407
|
+
logger.trace("#read <== received", result.inspect)
|
400
408
|
|
401
409
|
# EOF before all the data was returned
|
402
410
|
if result.nil? || (result.length < length)
|
403
411
|
close if close_on_error
|
404
|
-
|
412
|
+
logger.warn "#read server closed the connection before #{length} bytes were returned"
|
405
413
|
raise ConnectionFailure.new("Connection lost while reading data", @server, EOFError.new("end of file reached"))
|
406
414
|
end
|
407
415
|
rescue SystemCallError, IOError => exception
|
408
416
|
close if close_on_error
|
409
|
-
|
417
|
+
logger.warn "#read Connection failure while reading data: #{exception.class}: #{exception.message}"
|
410
418
|
raise ConnectionFailure.new("#{exception.class}: #{exception.message}", @server, exception)
|
411
419
|
rescue Exception
|
412
420
|
# Close the connection on any other exception since the connection
|
@@ -468,15 +476,15 @@ module ResilientSocket
|
|
468
476
|
exc_str = exception.cause ? "#{exception.cause.class}: #{exception.cause.message}" : exception.message
|
469
477
|
# Re-raise exceptions that should not be retried
|
470
478
|
if !self.class.reconnect_on_errors.include?(exception.cause.class)
|
471
|
-
|
479
|
+
logger.warn "#retry_on_connection_failure not configured to retry: #{exc_str}"
|
472
480
|
raise exception
|
473
481
|
elsif retries < @retry_count
|
474
482
|
retries += 1
|
475
|
-
|
483
|
+
logger.warn "#retry_on_connection_failure retry #{retries} due to #{exception.class}: #{exception.message}"
|
476
484
|
connect
|
477
485
|
retry
|
478
486
|
end
|
479
|
-
|
487
|
+
logger.error "#retry_on_connection_failure Connection failure: #{exception.class}: #{exception.message}. Giving up after #{retries} retries"
|
480
488
|
raise ConnectionFailure.new("After #{retries} retries to host '#{server}': #{exc_str}", @server, exception.cause)
|
481
489
|
end
|
482
490
|
end
|
@@ -487,7 +495,7 @@ module ResilientSocket
|
|
487
495
|
def close
|
488
496
|
@socket.close unless @socket.closed?
|
489
497
|
rescue IOError => exception
|
490
|
-
|
498
|
+
logger.warn "IOError when attempting to close socket: #{exception.class}: #{exception.message}"
|
491
499
|
end
|
492
500
|
|
493
501
|
# Returns whether the socket is closed
|
@@ -539,7 +547,7 @@ module ResilientSocket
|
|
539
547
|
# :accept, :accept_nonblock, :bind, :connect, :connect_nonblock, :getpeereid,
|
540
548
|
# :ipv6only!, :listen, :recvfrom_nonblock, :sysaccept
|
541
549
|
retries = 0
|
542
|
-
|
550
|
+
logger.benchmark_info "Connected to #{server}" do
|
543
551
|
host_name, port = server.split(":")
|
544
552
|
port = port.to_i
|
545
553
|
|
@@ -570,11 +578,11 @@ module ResilientSocket
|
|
570
578
|
rescue SystemCallError => exception
|
571
579
|
if retries < @connect_retry_count && self.class.reconnect_on_errors.include?(exception.class)
|
572
580
|
retries += 1
|
573
|
-
|
581
|
+
logger.warn "Connection failure: #{exception.class}: #{exception.message}. Retry: #{retries}"
|
574
582
|
sleep @connect_retry_interval
|
575
583
|
retry
|
576
584
|
end
|
577
|
-
|
585
|
+
logger.error "Connection failure: #{exception.class}: #{exception.message}. Giving up after #{retries} retries"
|
578
586
|
raise ConnectionFailure.new("After #{retries} connection attempts to host '#{server}': #{exception.class}: #{exception.message}", @server, exception)
|
579
587
|
end
|
580
588
|
end
|
data/test/simple_tcp_server.rb
CHANGED
@@ -107,8 +107,8 @@ class SimpleTCPServer
|
|
107
107
|
end
|
108
108
|
|
109
109
|
if $0 == __FILE__
|
110
|
-
SemanticLogger
|
111
|
-
SemanticLogger
|
110
|
+
SemanticLogger.default_level = :trace
|
111
|
+
SemanticLogger.add_appender(STDOUT)
|
112
112
|
server = SimpleTCPServer.new(2000)
|
113
113
|
server.thread.join
|
114
114
|
end
|
data/test/tcp_client_test.rb
CHANGED
@@ -8,8 +8,8 @@ require 'shoulda'
|
|
8
8
|
require 'resilient_socket'
|
9
9
|
require 'simple_tcp_server'
|
10
10
|
|
11
|
-
SemanticLogger
|
12
|
-
SemanticLogger
|
11
|
+
SemanticLogger.default_level = :trace
|
12
|
+
SemanticLogger.add_appender('test.log')
|
13
13
|
|
14
14
|
# Unit Test for ResilientSocket::TCPClient
|
15
15
|
class TCPClientTest < Test::Unit::TestCase
|
metadata
CHANGED
@@ -1,32 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resilient_socket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.5.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Reid Morrison
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-04-03 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: semantic_logger
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
19
|
+
version: '2.1'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
26
|
+
version: '2.1'
|
30
27
|
description: A Resilient TCP Socket Client with built-in timeouts, retries, and logging
|
31
28
|
email:
|
32
29
|
- reidmo@gmail.com
|
@@ -34,44 +31,39 @@ executables: []
|
|
34
31
|
extensions: []
|
35
32
|
extra_rdoc_files: []
|
36
33
|
files:
|
34
|
+
- Gemfile
|
35
|
+
- Gemfile.lock
|
36
|
+
- LICENSE.txt
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- lib/resilient_socket.rb
|
37
40
|
- lib/resilient_socket/exceptions.rb
|
38
41
|
- lib/resilient_socket/tcp_client.rb
|
39
42
|
- lib/resilient_socket/version.rb
|
40
|
-
- lib/resilient_socket.rb
|
41
|
-
- LICENSE.txt
|
42
|
-
- nbproject/private/config.properties
|
43
|
-
- nbproject/private/private.properties
|
44
|
-
- nbproject/private/private.xml
|
45
|
-
- nbproject/private/rake-d.txt
|
46
|
-
- nbproject/project.properties
|
47
|
-
- nbproject/project.xml
|
48
|
-
- Rakefile
|
49
|
-
- README.md
|
50
43
|
- test/simple_tcp_server.rb
|
51
44
|
- test/tcp_client_test.rb
|
52
|
-
- test.log.working
|
53
45
|
homepage: https://github.com/ClarityServices/resilient_socket
|
54
|
-
licenses:
|
46
|
+
licenses:
|
47
|
+
- Apache License V2.0
|
48
|
+
metadata: {}
|
55
49
|
post_install_message:
|
56
50
|
rdoc_options: []
|
57
51
|
require_paths:
|
58
52
|
- lib
|
59
53
|
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
-
none: false
|
61
54
|
requirements:
|
62
|
-
- -
|
55
|
+
- - '>='
|
63
56
|
- !ruby/object:Gem::Version
|
64
57
|
version: '0'
|
65
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
59
|
requirements:
|
68
|
-
- -
|
60
|
+
- - '>='
|
69
61
|
- !ruby/object:Gem::Version
|
70
62
|
version: '0'
|
71
63
|
requirements: []
|
72
64
|
rubyforge_project:
|
73
|
-
rubygems_version:
|
65
|
+
rubygems_version: 2.0.2
|
74
66
|
signing_key:
|
75
|
-
specification_version:
|
67
|
+
specification_version: 4
|
76
68
|
summary: A Resilient TCP Socket Client with built-in timeouts, retries, and logging
|
77
69
|
test_files: []
|
File without changes
|
data/nbproject/project.xml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<project xmlns="http://www.netbeans.org/ns/project/1">
|
3
|
-
<type>org.netbeans.modules.ruby.rubyproject</type>
|
4
|
-
<configuration>
|
5
|
-
<data xmlns="http://www.netbeans.org/ns/ruby-project/1">
|
6
|
-
<name>resilient_socket</name>
|
7
|
-
<source-roots>
|
8
|
-
<root id="src.dir"/>
|
9
|
-
</source-roots>
|
10
|
-
<test-roots>
|
11
|
-
<root id="test.src.dir"/>
|
12
|
-
</test-roots>
|
13
|
-
</data>
|
14
|
-
</configuration>
|
15
|
-
</project>
|
data/test.log.working
DELETED
Binary file
|