bones-rpc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/bones-rpc.gemspec +29 -0
- data/lib/bones-rpc.rb +2 -0
- data/lib/bones/rpc.rb +23 -0
- data/lib/bones/rpc/adapter.rb +49 -0
- data/lib/bones/rpc/adapter/base.rb +41 -0
- data/lib/bones/rpc/adapter/erlang.rb +28 -0
- data/lib/bones/rpc/adapter/json.rb +23 -0
- data/lib/bones/rpc/adapter/msgpack.rb +52 -0
- data/lib/bones/rpc/adapter/parser.rb +37 -0
- data/lib/bones/rpc/address.rb +167 -0
- data/lib/bones/rpc/cluster.rb +266 -0
- data/lib/bones/rpc/connection.rb +146 -0
- data/lib/bones/rpc/connection/reader.rb +49 -0
- data/lib/bones/rpc/connection/socket.rb +4 -0
- data/lib/bones/rpc/connection/socket/connectable.rb +196 -0
- data/lib/bones/rpc/connection/socket/ssl.rb +35 -0
- data/lib/bones/rpc/connection/socket/tcp.rb +28 -0
- data/lib/bones/rpc/connection/writer.rb +51 -0
- data/lib/bones/rpc/context.rb +48 -0
- data/lib/bones/rpc/errors.rb +33 -0
- data/lib/bones/rpc/failover.rb +38 -0
- data/lib/bones/rpc/failover/disconnect.rb +33 -0
- data/lib/bones/rpc/failover/ignore.rb +31 -0
- data/lib/bones/rpc/failover/retry.rb +39 -0
- data/lib/bones/rpc/future.rb +26 -0
- data/lib/bones/rpc/instrumentable.rb +41 -0
- data/lib/bones/rpc/instrumentable/log.rb +45 -0
- data/lib/bones/rpc/instrumentable/noop.rb +33 -0
- data/lib/bones/rpc/loggable.rb +112 -0
- data/lib/bones/rpc/node.rb +317 -0
- data/lib/bones/rpc/node/registry.rb +32 -0
- data/lib/bones/rpc/parser.rb +114 -0
- data/lib/bones/rpc/parser/buffer.rb +80 -0
- data/lib/bones/rpc/protocol.rb +106 -0
- data/lib/bones/rpc/protocol/acknowledge.rb +82 -0
- data/lib/bones/rpc/protocol/adapter_helper.rb +164 -0
- data/lib/bones/rpc/protocol/binary_helper.rb +431 -0
- data/lib/bones/rpc/protocol/ext_message.rb +86 -0
- data/lib/bones/rpc/protocol/notify.rb +38 -0
- data/lib/bones/rpc/protocol/request.rb +45 -0
- data/lib/bones/rpc/protocol/response.rb +58 -0
- data/lib/bones/rpc/protocol/synchronize.rb +70 -0
- data/lib/bones/rpc/read_preference.rb +43 -0
- data/lib/bones/rpc/read_preference/nearest.rb +57 -0
- data/lib/bones/rpc/read_preference/selectable.rb +81 -0
- data/lib/bones/rpc/readable.rb +57 -0
- data/lib/bones/rpc/session.rb +195 -0
- data/lib/bones/rpc/uri.rb +222 -0
- data/lib/bones/rpc/version.rb +6 -0
- metadata +198 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Bones
|
3
|
+
module RPC
|
4
|
+
module Protocol
|
5
|
+
class Notify
|
6
|
+
include AdapterHelper
|
7
|
+
|
8
|
+
integer :op_code
|
9
|
+
binary :method
|
10
|
+
list :params
|
11
|
+
|
12
|
+
finalize
|
13
|
+
|
14
|
+
def initialize(method, params)
|
15
|
+
@method = method
|
16
|
+
@params = params
|
17
|
+
end
|
18
|
+
|
19
|
+
undef op_code
|
20
|
+
|
21
|
+
def op_code
|
22
|
+
2
|
23
|
+
end
|
24
|
+
|
25
|
+
def log_inspect
|
26
|
+
type = "NOTIFY"
|
27
|
+
fields = []
|
28
|
+
fields << ["%-12s", type]
|
29
|
+
fields << ["method=%s", method]
|
30
|
+
fields << ["params=%s", params]
|
31
|
+
f, v = fields.transpose
|
32
|
+
f.join(" ") % v
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Bones
|
3
|
+
module RPC
|
4
|
+
module Protocol
|
5
|
+
class Request
|
6
|
+
include AdapterHelper
|
7
|
+
|
8
|
+
integer :op_code
|
9
|
+
integer :id
|
10
|
+
binary :method
|
11
|
+
list :params
|
12
|
+
|
13
|
+
finalize
|
14
|
+
|
15
|
+
def initialize(id, method, params)
|
16
|
+
@id = id
|
17
|
+
@method = method
|
18
|
+
@params = params
|
19
|
+
end
|
20
|
+
|
21
|
+
undef op_code
|
22
|
+
|
23
|
+
def op_code
|
24
|
+
0
|
25
|
+
end
|
26
|
+
|
27
|
+
def log_inspect
|
28
|
+
type = "REQUEST"
|
29
|
+
fields = []
|
30
|
+
fields << ["%-12s", type]
|
31
|
+
fields << ["id=%s", id]
|
32
|
+
fields << ["method=%s", method]
|
33
|
+
fields << ["params=%s", params]
|
34
|
+
f, v = fields.transpose
|
35
|
+
f.join(" ") % v
|
36
|
+
end
|
37
|
+
|
38
|
+
def attach(node, future)
|
39
|
+
node.attach(:request, id, future)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Bones
|
3
|
+
module RPC
|
4
|
+
module Protocol
|
5
|
+
class Response
|
6
|
+
include AdapterHelper
|
7
|
+
|
8
|
+
integer :op_code
|
9
|
+
integer :id
|
10
|
+
any :error
|
11
|
+
any :result
|
12
|
+
|
13
|
+
finalize
|
14
|
+
|
15
|
+
def initialize(id, error, result)
|
16
|
+
@id = id
|
17
|
+
@error = error
|
18
|
+
@result = result
|
19
|
+
end
|
20
|
+
|
21
|
+
undef op_code
|
22
|
+
|
23
|
+
def op_code
|
24
|
+
@op_code ||= 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def log_inspect
|
28
|
+
type = "RESPONSE"
|
29
|
+
fields = []
|
30
|
+
fields << ["%-12s", type]
|
31
|
+
fields << ["id=%s", id]
|
32
|
+
fields << ["error=%s", error]
|
33
|
+
fields << ["result=%s", result]
|
34
|
+
f, v = fields.transpose
|
35
|
+
f.join(" ") % v
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.map_from(object)
|
39
|
+
message = allocate
|
40
|
+
message.op_code = object[0]
|
41
|
+
message.id = object[1]
|
42
|
+
message.error = object[2]
|
43
|
+
message.result = object[3]
|
44
|
+
message
|
45
|
+
end
|
46
|
+
|
47
|
+
def get(node)
|
48
|
+
node.detach(:request, id)
|
49
|
+
end
|
50
|
+
|
51
|
+
def signal(future)
|
52
|
+
future.signal(FutureValue.new(self))
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Bones
|
3
|
+
module RPC
|
4
|
+
module Protocol
|
5
|
+
class Synchronize < ExtMessage
|
6
|
+
|
7
|
+
uint32 :id
|
8
|
+
binary :adapter
|
9
|
+
|
10
|
+
finalize
|
11
|
+
|
12
|
+
def initialize(id, adapter)
|
13
|
+
self.id = id
|
14
|
+
self.adapter = adapter
|
15
|
+
end
|
16
|
+
|
17
|
+
undef ext_head
|
18
|
+
undef :adapter=
|
19
|
+
undef serialize_adapter
|
20
|
+
|
21
|
+
def ext_head
|
22
|
+
0
|
23
|
+
end
|
24
|
+
|
25
|
+
def adapter=(adapter)
|
26
|
+
@adapter = Adapter.get(adapter)
|
27
|
+
end
|
28
|
+
|
29
|
+
def deserialize_adapter(buffer)
|
30
|
+
self.adapter = buffer.read(ext_length - 5)
|
31
|
+
end
|
32
|
+
|
33
|
+
def serialize_adapter(buffer)
|
34
|
+
buffer << adapter.adapter_name.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def log_inspect
|
38
|
+
type = "SYNCHRONIZE"
|
39
|
+
fields = []
|
40
|
+
fields << ["%-12s", type]
|
41
|
+
fields << ["id=%s", id]
|
42
|
+
fields << ["adapter=%s", adapter]
|
43
|
+
f, v = fields.transpose
|
44
|
+
f.join(" ") % v
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.deserialize(buffer, adapter = nil)
|
48
|
+
message = super
|
49
|
+
message.deserialize_id(buffer)
|
50
|
+
message.deserialize_adapter(buffer)
|
51
|
+
message
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.unpack(data)
|
55
|
+
buffer = StringIO.new(data)
|
56
|
+
id, = buffer.read(4).unpack('N')
|
57
|
+
adapter = buffer.read
|
58
|
+
new(id, adapter)
|
59
|
+
end
|
60
|
+
|
61
|
+
def attach(node, future)
|
62
|
+
node.attach(:synack, id, future)
|
63
|
+
end
|
64
|
+
|
65
|
+
Protocol.register_ext_head self, 0
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'bones/rpc/read_preference/selectable'
|
3
|
+
require 'bones/rpc/read_preference/nearest'
|
4
|
+
|
5
|
+
module Bones
|
6
|
+
module RPC
|
7
|
+
|
8
|
+
# Provides behaviour around getting various read preference implementations.
|
9
|
+
#
|
10
|
+
# @since 2.0.0
|
11
|
+
module ReadPreference
|
12
|
+
extend self
|
13
|
+
|
14
|
+
# Hash lookup for the read preference classes based off the symbols
|
15
|
+
# provided in configuration.
|
16
|
+
#
|
17
|
+
# @since 2.0.0
|
18
|
+
PREFERENCES = {
|
19
|
+
nearest: Nearest
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
# Get a read preference for the provided name. Valid names are:
|
23
|
+
# - :nearest
|
24
|
+
# - :primary
|
25
|
+
# - :primary_preferred
|
26
|
+
# - :secondary
|
27
|
+
# - :secondary_preferred
|
28
|
+
#
|
29
|
+
# @example Get the primary read preference.
|
30
|
+
# Bones::RPC::ReadPreference.get(:primary)
|
31
|
+
#
|
32
|
+
# @param [ Symbol ] name The name of the preference.
|
33
|
+
# @param [ Array<Hash> ] tags The tag sets to match the node on.
|
34
|
+
#
|
35
|
+
# @return [ Object ] The appropriate read preference.
|
36
|
+
#
|
37
|
+
# @since 2.0.0
|
38
|
+
def get(name, tags = nil)
|
39
|
+
PREFERENCES.fetch(name.to_sym).new(tags)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Bones
|
3
|
+
module RPC
|
4
|
+
module ReadPreference
|
5
|
+
|
6
|
+
# Encapsulates behaviour around a nearest read preference.
|
7
|
+
#
|
8
|
+
# @since 2.0.0
|
9
|
+
class Nearest
|
10
|
+
include Selectable
|
11
|
+
|
12
|
+
# Get the name for the read preference on the server side.
|
13
|
+
#
|
14
|
+
# @example Get the name of the read preference.
|
15
|
+
# nearest.name
|
16
|
+
#
|
17
|
+
# @return [ Symbol ] :nearest.
|
18
|
+
#
|
19
|
+
# @since 2.0.0
|
20
|
+
def name
|
21
|
+
:nearest
|
22
|
+
end
|
23
|
+
|
24
|
+
# Execute the provided block on the node with the lowest latency,
|
25
|
+
# allowing either primary or secondary.
|
26
|
+
#
|
27
|
+
# @example Read from the nearest node in the cluster.
|
28
|
+
# preference.with_node(cluster) do |node|
|
29
|
+
# node.command(ismaster: 1)
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# @note If tag sets are provided then selection will need to
|
33
|
+
# match the provided tags.
|
34
|
+
#
|
35
|
+
# @param [ Cluster ] cluster The cluster of nodes to select from.
|
36
|
+
# @param [ Proc ] block The block to execute on the node.
|
37
|
+
#
|
38
|
+
# @raise [ Errors::ConnectionFailure ] If no node was available in the
|
39
|
+
# cluster.
|
40
|
+
#
|
41
|
+
# @return [ Object ] The result of the block.
|
42
|
+
#
|
43
|
+
# @since 2.0.0
|
44
|
+
def with_node(cluster, &block)
|
45
|
+
with_retry(cluster) do
|
46
|
+
nearest = cluster.nodes.sort_by(&:latency).first
|
47
|
+
if nearest
|
48
|
+
block.call(nearest)
|
49
|
+
else
|
50
|
+
raise Errors::ConnectionFailure, "No nodes available to select in the cluster"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Bones
|
3
|
+
module RPC
|
4
|
+
module ReadPreference
|
5
|
+
|
6
|
+
# Provides the shared behaviour for read preferences that can filter by a
|
7
|
+
# tag set or add query options.
|
8
|
+
#
|
9
|
+
# @since 2.0.0
|
10
|
+
module Selectable
|
11
|
+
|
12
|
+
# @!attribute tags
|
13
|
+
# @return [ Array<Hash> ] The tag sets.
|
14
|
+
attr_reader :tags
|
15
|
+
|
16
|
+
# Instantiate the new taggable read preference.
|
17
|
+
#
|
18
|
+
# @example Instantiate the taggable.
|
19
|
+
# Bones::RPC::ReadPreference::Secondary.new({ east_coast: 1 })
|
20
|
+
#
|
21
|
+
# @param [ Array<Hash> ] tags The tag sets.
|
22
|
+
#
|
23
|
+
# @since 2.0.0
|
24
|
+
def initialize(tags = nil)
|
25
|
+
@tags = tags
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get the provided options as query options for this read preference.
|
29
|
+
#
|
30
|
+
# @example Get the query options.
|
31
|
+
# preference.query_options({})
|
32
|
+
#
|
33
|
+
# @param [ Hash ] options The existing options for the query.
|
34
|
+
#
|
35
|
+
# @return [ Hash ] The options plus additional query options.
|
36
|
+
#
|
37
|
+
# @since 2.0.0
|
38
|
+
def query_options(options)
|
39
|
+
options[:flags] ||= []
|
40
|
+
options[:flags] |= [ :slave_ok ]
|
41
|
+
options
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Execute the provided block on the cluster and retry if the execution
|
47
|
+
# fails.
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
#
|
51
|
+
# @example Execute with retry.
|
52
|
+
# preference.with_retry(cluster) do
|
53
|
+
# cluster.with_primary do |node|
|
54
|
+
# node.refresh
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# @param [ Cluster ] cluster The cluster.
|
59
|
+
# @param [ Integer ] retries The number of times to retry.
|
60
|
+
#
|
61
|
+
# @return [ Object ] The result of the block.
|
62
|
+
#
|
63
|
+
# @since 2.0.0
|
64
|
+
def with_retry(cluster, retries = cluster.max_retries, &block)
|
65
|
+
begin
|
66
|
+
block.call
|
67
|
+
rescue Errors::ConnectionFailure => e
|
68
|
+
if retries > 0
|
69
|
+
Loggable.warn(" BONES-RPC:", "Retrying connection attempt #{retries} more time(s).", "n/a")
|
70
|
+
sleep(cluster.retry_interval)
|
71
|
+
cluster.refresh
|
72
|
+
with_retry(cluster, retries - 1, &block)
|
73
|
+
else
|
74
|
+
raise e
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Bones
|
3
|
+
module RPC
|
4
|
+
|
5
|
+
# Provides behaviour around readable objects.
|
6
|
+
#
|
7
|
+
# @since 2.0.0
|
8
|
+
module Readable
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# Convenience method for getting the cluster from the session.
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
# @example Get the cluster from the session.
|
17
|
+
# database.cluster
|
18
|
+
#
|
19
|
+
# @return [ Cluster ] The cluster.
|
20
|
+
#
|
21
|
+
# @since 2.0.0
|
22
|
+
def cluster
|
23
|
+
session.cluster
|
24
|
+
end
|
25
|
+
|
26
|
+
# Convenience method for getting the read preference from the session.
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
#
|
30
|
+
# @example Get the read preference.
|
31
|
+
# database.read_preference
|
32
|
+
#
|
33
|
+
# @return [ Object ] The session's read preference.
|
34
|
+
#
|
35
|
+
# @since 2.0.0
|
36
|
+
def read_preference
|
37
|
+
session.read_preference
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get the query options from the read preference.
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
#
|
44
|
+
# @example Get the query options.
|
45
|
+
# database.query_options
|
46
|
+
#
|
47
|
+
# @param [ Hash ] options The existing options on the query.
|
48
|
+
#
|
49
|
+
# @return [ Hash ] The new query options.
|
50
|
+
#
|
51
|
+
# @since 2.0.0
|
52
|
+
def query_options(options = {})
|
53
|
+
read_preference.query_options(options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|