bones-rpc 0.0.2 → 0.0.3
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 +4 -4
- data/.ruby-gemset +1 -1
- data/.ruby-version +1 -1
- data/bones-rpc.gemspec +1 -4
- data/lib/bones/rpc.rb +1 -1
- data/lib/bones/rpc/adapter.rb +44 -4
- data/lib/bones/rpc/adapter/json.rb +66 -0
- data/lib/bones/rpc/address.rb +6 -4
- data/lib/bones/rpc/backend.rb +31 -0
- data/lib/bones/rpc/backend/base.rb +30 -0
- data/lib/bones/rpc/backend/synchronous.rb +29 -0
- data/lib/bones/rpc/cluster.rb +18 -18
- data/lib/bones/rpc/connection.rb +19 -15
- data/lib/bones/rpc/connection/socket.rb +0 -2
- data/lib/bones/rpc/connection/socket/connectable.rb +15 -11
- data/lib/bones/rpc/context.rb +2 -2
- data/lib/bones/rpc/dns_resolver.rb +85 -0
- data/lib/bones/rpc/errors.rb +3 -0
- data/lib/bones/rpc/failover.rb +3 -3
- data/lib/bones/rpc/failover/disconnect.rb +2 -2
- data/lib/bones/rpc/failover/ignore.rb +2 -2
- data/lib/bones/rpc/failover/retry.rb +2 -2
- data/lib/bones/rpc/instrumentable.rb +3 -3
- data/lib/bones/rpc/instrumentable/log.rb +2 -2
- data/lib/bones/rpc/instrumentable/noop.rb +2 -2
- data/lib/bones/rpc/loggable.rb +8 -8
- data/lib/bones/rpc/node.rb +47 -37
- data/lib/bones/rpc/node/registry.rb +4 -0
- data/lib/bones/rpc/parser.rb +16 -5
- data/lib/bones/rpc/parser/buffer.rb +6 -2
- data/lib/bones/rpc/protocol/adapter_helper.rb +2 -2
- data/lib/bones/rpc/protocol/binary_helper.rb +2 -2
- data/lib/bones/rpc/read_preference.rb +3 -3
- data/lib/bones/rpc/read_preference/nearest.rb +3 -3
- data/lib/bones/rpc/read_preference/selectable.rb +4 -4
- data/lib/bones/rpc/readable.rb +4 -4
- data/lib/bones/rpc/session.rb +25 -16
- data/lib/bones/rpc/synchronous.rb +2 -0
- data/lib/bones/rpc/synchronous/connection.rb +36 -0
- data/lib/bones/rpc/synchronous/connection/reader.rb +59 -0
- data/lib/bones/rpc/synchronous/connection/socket.rb +4 -0
- data/lib/bones/rpc/synchronous/connection/socket/ssl.rb +57 -0
- data/lib/bones/rpc/synchronous/connection/socket/tcp.rb +30 -0
- data/lib/bones/rpc/synchronous/connection/writer.rb +86 -0
- data/lib/bones/rpc/synchronous/future.rb +91 -0
- data/lib/bones/rpc/synchronous/node.rb +45 -0
- data/lib/bones/rpc/uri.rb +20 -20
- data/lib/bones/rpc/version.rb +1 -1
- metadata +16 -52
- data/lib/bones/rpc/adapter/erlang.rb +0 -28
- data/lib/bones/rpc/adapter/msgpack.rb +0 -52
- data/lib/bones/rpc/connection/reader.rb +0 -49
- data/lib/bones/rpc/connection/socket/ssl.rb +0 -35
- data/lib/bones/rpc/connection/socket/tcp.rb +0 -28
- data/lib/bones/rpc/connection/writer.rb +0 -51
- data/lib/bones/rpc/future.rb +0 -26
data/lib/bones/rpc/connection.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require 'bones/rpc/connection/reader'
|
3
2
|
require 'bones/rpc/connection/socket'
|
4
|
-
require 'bones/rpc/connection/writer'
|
5
3
|
|
6
4
|
module Bones
|
7
5
|
module RPC
|
8
6
|
|
9
7
|
# This class contains behaviour of Bones::RPC socket connections.
|
10
8
|
#
|
11
|
-
# @since
|
9
|
+
# @since 0.0.1
|
12
10
|
class Connection
|
13
11
|
|
14
12
|
# The default connection timeout, in seconds.
|
15
13
|
#
|
16
|
-
# @since
|
14
|
+
# @since 0.0.1
|
17
15
|
TIMEOUT = 5
|
18
16
|
|
19
17
|
attr_reader :node, :socket
|
20
18
|
|
19
|
+
def self.writer_class(klass = nil)
|
20
|
+
if klass.nil?
|
21
|
+
@writer_class
|
22
|
+
else
|
23
|
+
@writer_class = klass
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
21
27
|
# Is the connection alive?
|
22
28
|
#
|
23
29
|
# @example Is the connection alive?
|
@@ -25,7 +31,7 @@ module Bones
|
|
25
31
|
#
|
26
32
|
# @return [ true, false ] If the connection is alive.
|
27
33
|
#
|
28
|
-
# @since
|
34
|
+
# @since 0.0.1
|
29
35
|
def alive?
|
30
36
|
connected? ? @socket.alive? : false
|
31
37
|
end
|
@@ -45,16 +51,16 @@ module Bones
|
|
45
51
|
#
|
46
52
|
# @return [ TCPSocket ] The socket.
|
47
53
|
#
|
48
|
-
# @since
|
54
|
+
# @since 0.0.1
|
49
55
|
def connect
|
50
56
|
if @writer
|
51
57
|
@writer.terminate
|
52
58
|
@writer = nil
|
53
59
|
end
|
54
60
|
@socket = if !!options[:ssl]
|
55
|
-
Socket::SSL.connect(host, port, timeout)
|
61
|
+
self.class::Socket::SSL.connect(host, port, timeout)
|
56
62
|
else
|
57
|
-
Socket::TCP.connect(host, port, timeout)
|
63
|
+
self.class::Socket::TCP.connect(host, port, timeout)
|
58
64
|
end
|
59
65
|
writer
|
60
66
|
return true
|
@@ -67,7 +73,7 @@ module Bones
|
|
67
73
|
#
|
68
74
|
# @return [ true, false ] If the connection is connected.
|
69
75
|
#
|
70
|
-
# @since
|
76
|
+
# @since 0.0.1
|
71
77
|
def connected?
|
72
78
|
!!@socket
|
73
79
|
end
|
@@ -79,7 +85,7 @@ module Bones
|
|
79
85
|
#
|
80
86
|
# @return [ nil ] nil.
|
81
87
|
#
|
82
|
-
# @since
|
88
|
+
# @since 0.0.1
|
83
89
|
def disconnect
|
84
90
|
@socket.close
|
85
91
|
rescue
|
@@ -113,9 +119,7 @@ module Bones
|
|
113
119
|
end
|
114
120
|
|
115
121
|
def write(operations)
|
116
|
-
|
117
|
-
writer.write(operations)
|
118
|
-
end
|
122
|
+
raise NotImplementedError, "Connection#write not implemented for this backend"
|
119
123
|
end
|
120
124
|
|
121
125
|
private
|
@@ -132,14 +136,14 @@ module Bones
|
|
132
136
|
#
|
133
137
|
# @return The yielded block
|
134
138
|
#
|
135
|
-
# @since
|
139
|
+
# @since 0.0.1
|
136
140
|
def with_connection
|
137
141
|
connect if @socket.nil? || !@socket.alive?
|
138
142
|
yield @socket
|
139
143
|
end
|
140
144
|
|
141
145
|
def writer
|
142
|
-
@writer ||=
|
146
|
+
@writer ||= self.class.writer_class.new(self, @socket, node.adapter)
|
143
147
|
end
|
144
148
|
end
|
145
149
|
end
|
@@ -14,7 +14,7 @@ module Bones
|
|
14
14
|
#
|
15
15
|
# @return [ true, false ] If the socket is alive.
|
16
16
|
#
|
17
|
-
# @since
|
17
|
+
# @since 0.0.1
|
18
18
|
def alive?
|
19
19
|
io = to_io
|
20
20
|
if Kernel::select([ io ], nil, [ io ], 0)
|
@@ -33,7 +33,7 @@ module Bones
|
|
33
33
|
#
|
34
34
|
# @param [ Class ] klass The class including the module.
|
35
35
|
#
|
36
|
-
# @since
|
36
|
+
# @since 0.0.1
|
37
37
|
def self.included(klass)
|
38
38
|
klass.send(:extend, ClassMethods)
|
39
39
|
end
|
@@ -45,7 +45,7 @@ module Bones
|
|
45
45
|
#
|
46
46
|
# @return [ Object ] The data.
|
47
47
|
#
|
48
|
-
# @since
|
48
|
+
# @since 0.0.1
|
49
49
|
def read(size = nil, buf = nil)
|
50
50
|
check_if_alive!
|
51
51
|
handle_socket_errors { super }
|
@@ -58,7 +58,7 @@ module Bones
|
|
58
58
|
#
|
59
59
|
# @return [ Object ] The data.
|
60
60
|
#
|
61
|
-
# @since
|
61
|
+
# @since 0.0.1
|
62
62
|
def readpartial(maxlen, buf = nil)
|
63
63
|
check_if_alive!
|
64
64
|
handle_socket_errors { super }
|
@@ -68,9 +68,13 @@ module Bones
|
|
68
68
|
#
|
69
69
|
# @param [ String ] string The encoding.
|
70
70
|
#
|
71
|
-
# @since
|
71
|
+
# @since 0.0.1
|
72
72
|
def set_encoding(string)
|
73
|
-
to_io
|
73
|
+
if to_io != self
|
74
|
+
to_io.set_encoding(string)
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
74
78
|
end
|
75
79
|
|
76
80
|
# Write to the socket.
|
@@ -82,7 +86,7 @@ module Bones
|
|
82
86
|
#
|
83
87
|
# @return [ Integer ] The number of bytes written.
|
84
88
|
#
|
85
|
-
# @since
|
89
|
+
# @since 0.0.1
|
86
90
|
def write(*args)
|
87
91
|
check_if_alive!
|
88
92
|
handle_socket_errors { super }
|
@@ -100,7 +104,7 @@ module Bones
|
|
100
104
|
#
|
101
105
|
# @raise [ ConnectionFailure ] If the connectable is not alive.
|
102
106
|
#
|
103
|
-
# @since
|
107
|
+
# @since 0.0.1
|
104
108
|
def check_if_alive!
|
105
109
|
unless alive?
|
106
110
|
raise Errors::ConnectionFailure, "Socket connection was closed by remote host"
|
@@ -119,7 +123,7 @@ module Bones
|
|
119
123
|
#
|
120
124
|
# @return [ String ] The error message.
|
121
125
|
#
|
122
|
-
# @since
|
126
|
+
# @since 0.0.1
|
123
127
|
def generate_message(error)
|
124
128
|
"#{host}:#{port}: #{error.class.name} (#{error.errno}): #{error.message}"
|
125
129
|
end
|
@@ -140,7 +144,7 @@ module Bones
|
|
140
144
|
#
|
141
145
|
# @return [ Object ] The result of the yield.
|
142
146
|
#
|
143
|
-
# @since
|
147
|
+
# @since 0.0.1
|
144
148
|
def handle_socket_errors
|
145
149
|
yield
|
146
150
|
rescue Errno::ECONNREFUSED => e
|
@@ -172,7 +176,7 @@ module Bones
|
|
172
176
|
#
|
173
177
|
# @return [ TCPSocket ] The socket.
|
174
178
|
#
|
175
|
-
# @since
|
179
|
+
# @since 0.0.1
|
176
180
|
def connect(host, port, timeout)
|
177
181
|
begin
|
178
182
|
Timeout::timeout(timeout) do
|
data/lib/bones/rpc/context.rb
CHANGED
@@ -5,7 +5,7 @@ module Bones
|
|
5
5
|
# The class for interacting with a MongoDB database. One only interacts with
|
6
6
|
# this class indirectly through a session.
|
7
7
|
#
|
8
|
-
# @since
|
8
|
+
# @since 0.0.1
|
9
9
|
class Context
|
10
10
|
include Readable
|
11
11
|
|
@@ -21,7 +21,7 @@ module Bones
|
|
21
21
|
# @param [ Session ] session The session.
|
22
22
|
# @param [ String, Symbol ] name The name of the database.
|
23
23
|
#
|
24
|
-
# @since
|
24
|
+
# @since 0.0.1
|
25
25
|
def initialize(session)
|
26
26
|
@session = session
|
27
27
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
3
|
+
module Bones
|
4
|
+
module RPC
|
5
|
+
class DNSResolver
|
6
|
+
RESOLV_CONF = '/etc/resolv.conf'
|
7
|
+
DNS_PORT = 53
|
8
|
+
|
9
|
+
@mutex = Mutex.new
|
10
|
+
@identifier = 1
|
11
|
+
|
12
|
+
def self.generate_id
|
13
|
+
@mutex.synchronize { @identifier = (@identifier + 1) & 0xFFFF }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.nameservers(config = RESOLV_CONF)
|
17
|
+
File.read(config).scan(/^\s*nameserver\s+([0-9.:]+)/).flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@nameservers = self.class.nameservers
|
22
|
+
|
23
|
+
# TODO: fall back on other nameservers if the first one is unavailable
|
24
|
+
@server = @nameservers.first
|
25
|
+
|
26
|
+
@socket = UDPSocket.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def resolve(hostname)
|
30
|
+
if host = resolve_hostname(hostname)
|
31
|
+
unless ip_address = resolve_host(host)
|
32
|
+
raise Resolv::ResolvError, "invalid entry in hosts file: #{host}"
|
33
|
+
end
|
34
|
+
return ip_address
|
35
|
+
end
|
36
|
+
|
37
|
+
query = build_query(hostname)
|
38
|
+
@socket.send query.encode, 0, @server, DNS_PORT
|
39
|
+
data, _ = @socket.recvfrom(512)
|
40
|
+
response = Resolv::DNS::Message.decode(data)
|
41
|
+
|
42
|
+
addrs = []
|
43
|
+
# The answer might include IN::CNAME entries so filters them out
|
44
|
+
# to include IN::A & IN::AAAA entries only.
|
45
|
+
response.each_answer { |name, ttl, value| addrs << value.address if value.respond_to?(:address) }
|
46
|
+
|
47
|
+
return if addrs.empty?
|
48
|
+
return addrs.first if addrs.size == 1
|
49
|
+
addrs
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def resolve_hostname(hostname)
|
55
|
+
# Resolv::Hosts#getaddresses pushes onto a stack
|
56
|
+
# so since we want the first occurance, simply
|
57
|
+
# pop off the stack.
|
58
|
+
resolv.getaddresses(hostname).pop rescue nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def resolv
|
62
|
+
@resolv ||= Resolv::Hosts.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_query(hostname)
|
66
|
+
Resolv::DNS::Message.new.tap do |query|
|
67
|
+
query.id = self.class.generate_id
|
68
|
+
query.rd = 1
|
69
|
+
query.add_question hostname, Resolv::DNS::Resource::IN::A
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def resolve_host(host)
|
74
|
+
resolve_ip(Resolv::IPv4, host) || resolve_ip(Resolv::IPv6, host)
|
75
|
+
end
|
76
|
+
|
77
|
+
def resolve_ip(klass, host)
|
78
|
+
begin
|
79
|
+
klass.create(host)
|
80
|
+
rescue ArgumentError
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/bones/rpc/errors.rb
CHANGED
@@ -21,6 +21,9 @@ module Bones
|
|
21
21
|
# Raised when an Adapter is invalid.
|
22
22
|
class InvalidAdapter < StandardError; end
|
23
23
|
|
24
|
+
# Raised when an Backend is invalid.
|
25
|
+
class InvalidBackend < StandardError; end
|
26
|
+
|
24
27
|
# Raised when a Bones::RPC URI is invalid.
|
25
28
|
class InvalidBonesRPCURI < StandardError; end
|
26
29
|
|
data/lib/bones/rpc/failover.rb
CHANGED
@@ -9,13 +9,13 @@ module Bones
|
|
9
9
|
# Provides behaviour around failover scenarios for different types of
|
10
10
|
# exceptions that get raised on connection and execution of operations.
|
11
11
|
#
|
12
|
-
# @since
|
12
|
+
# @since 0.0.1
|
13
13
|
module Failover
|
14
14
|
extend self
|
15
15
|
|
16
16
|
# Hash lookup for the failover classes based off the exception type.
|
17
17
|
#
|
18
|
-
# @since
|
18
|
+
# @since 0.0.1
|
19
19
|
STRATEGIES = {
|
20
20
|
Errors::ConnectionFailure => Retry
|
21
21
|
}.freeze
|
@@ -29,7 +29,7 @@ module Bones
|
|
29
29
|
#
|
30
30
|
# @return [ Object ] The failover handler.
|
31
31
|
#
|
32
|
-
# @since
|
32
|
+
# @since 0.0.1
|
33
33
|
def get(exception)
|
34
34
|
STRATEGIES.fetch(exception.class, Disconnect)
|
35
35
|
end
|
@@ -6,7 +6,7 @@ module Bones
|
|
6
6
|
# Disconnect is for the case when we get exceptions we do not know about,
|
7
7
|
# and need to disconnect the node to cleanup the problem.
|
8
8
|
#
|
9
|
-
# @since
|
9
|
+
# @since 0.0.1
|
10
10
|
module Disconnect
|
11
11
|
extend self
|
12
12
|
|
@@ -22,7 +22,7 @@ module Bones
|
|
22
22
|
#
|
23
23
|
# @raise [ Errors::SocketError ] The extended exception that was thrown.
|
24
24
|
#
|
25
|
-
# @since
|
25
|
+
# @since 0.0.1
|
26
26
|
def execute(exception, node)
|
27
27
|
node.disconnect
|
28
28
|
raise(exception.extend(Errors::SocketError))
|
@@ -6,7 +6,7 @@ module Bones
|
|
6
6
|
# Ignore is for the case when we get exceptions we deem are proper user
|
7
7
|
# or datbase errors and should be re-raised.
|
8
8
|
#
|
9
|
-
# @since
|
9
|
+
# @since 0.0.1
|
10
10
|
module Ignore
|
11
11
|
extend self
|
12
12
|
|
@@ -21,7 +21,7 @@ module Bones
|
|
21
21
|
#
|
22
22
|
# @raise [ Exception ] The exception that was previously thrown.
|
23
23
|
#
|
24
|
-
# @since
|
24
|
+
# @since 0.0.1
|
25
25
|
def execute(exception, node)
|
26
26
|
raise(exception)
|
27
27
|
end
|
@@ -6,7 +6,7 @@ module Bones
|
|
6
6
|
# Retry is for the case when we get exceptions around the connection, and
|
7
7
|
# want to make another attempt to try and resolve the issue.
|
8
8
|
#
|
9
|
-
# @since
|
9
|
+
# @since 0.0.1
|
10
10
|
module Retry
|
11
11
|
extend self
|
12
12
|
|
@@ -23,7 +23,7 @@ module Bones
|
|
23
23
|
#
|
24
24
|
# @return [ Object ] The result of the block yield.
|
25
25
|
#
|
26
|
-
# @since
|
26
|
+
# @since 0.0.1
|
27
27
|
def execute(exception, node)
|
28
28
|
node.disconnect
|
29
29
|
begin
|
@@ -8,12 +8,12 @@ module Bones
|
|
8
8
|
|
9
9
|
# The name of the topic of operations for Bones::RPC.
|
10
10
|
#
|
11
|
-
# @since
|
11
|
+
# @since 0.0.1
|
12
12
|
TOPIC = "bones-rpc.operations"
|
13
13
|
|
14
14
|
# Topic for warning instrumentation.
|
15
15
|
#
|
16
|
-
# @since
|
16
|
+
# @since 0.0.1
|
17
17
|
WARN = "bones-rpc.warn"
|
18
18
|
|
19
19
|
# @!attribute instrumenter
|
@@ -32,7 +32,7 @@ module Bones
|
|
32
32
|
#
|
33
33
|
# @return [ Object ] The result of the yield.
|
34
34
|
#
|
35
|
-
# @since
|
35
|
+
# @since 0.0.1
|
36
36
|
def instrument(name, payload = {}, &block)
|
37
37
|
instrumenter.instrument(name, payload, &block)
|
38
38
|
end
|
@@ -6,7 +6,7 @@ module Bones
|
|
6
6
|
# Provides logging instrumentation for compatibility with active support
|
7
7
|
# notifications.
|
8
8
|
#
|
9
|
-
# @since
|
9
|
+
# @since 0.0.1
|
10
10
|
class Log
|
11
11
|
|
12
12
|
class << self
|
@@ -21,7 +21,7 @@ module Bones
|
|
21
21
|
#
|
22
22
|
# @return [ Object ] The result of the yield.
|
23
23
|
#
|
24
|
-
# @since
|
24
|
+
# @since 0.0.1
|
25
25
|
def instrument(name, payload = {})
|
26
26
|
started = Time.new
|
27
27
|
begin
|
@@ -5,7 +5,7 @@ module Bones
|
|
5
5
|
|
6
6
|
# Does not instrument anything, just yields.
|
7
7
|
#
|
8
|
-
# @since
|
8
|
+
# @since 0.0.1
|
9
9
|
class Noop
|
10
10
|
|
11
11
|
class << self
|
@@ -22,7 +22,7 @@ module Bones
|
|
22
22
|
#
|
23
23
|
# @return [ Object ] The result of the yield.
|
24
24
|
#
|
25
|
-
# @since
|
25
|
+
# @since 0.0.1
|
26
26
|
def instrument(name, payload = {})
|
27
27
|
yield payload if block_given?
|
28
28
|
end
|