bones-rpc 0.0.1
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/.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
|