bones-rpc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +1 -0
  9. data/bones-rpc.gemspec +29 -0
  10. data/lib/bones-rpc.rb +2 -0
  11. data/lib/bones/rpc.rb +23 -0
  12. data/lib/bones/rpc/adapter.rb +49 -0
  13. data/lib/bones/rpc/adapter/base.rb +41 -0
  14. data/lib/bones/rpc/adapter/erlang.rb +28 -0
  15. data/lib/bones/rpc/adapter/json.rb +23 -0
  16. data/lib/bones/rpc/adapter/msgpack.rb +52 -0
  17. data/lib/bones/rpc/adapter/parser.rb +37 -0
  18. data/lib/bones/rpc/address.rb +167 -0
  19. data/lib/bones/rpc/cluster.rb +266 -0
  20. data/lib/bones/rpc/connection.rb +146 -0
  21. data/lib/bones/rpc/connection/reader.rb +49 -0
  22. data/lib/bones/rpc/connection/socket.rb +4 -0
  23. data/lib/bones/rpc/connection/socket/connectable.rb +196 -0
  24. data/lib/bones/rpc/connection/socket/ssl.rb +35 -0
  25. data/lib/bones/rpc/connection/socket/tcp.rb +28 -0
  26. data/lib/bones/rpc/connection/writer.rb +51 -0
  27. data/lib/bones/rpc/context.rb +48 -0
  28. data/lib/bones/rpc/errors.rb +33 -0
  29. data/lib/bones/rpc/failover.rb +38 -0
  30. data/lib/bones/rpc/failover/disconnect.rb +33 -0
  31. data/lib/bones/rpc/failover/ignore.rb +31 -0
  32. data/lib/bones/rpc/failover/retry.rb +39 -0
  33. data/lib/bones/rpc/future.rb +26 -0
  34. data/lib/bones/rpc/instrumentable.rb +41 -0
  35. data/lib/bones/rpc/instrumentable/log.rb +45 -0
  36. data/lib/bones/rpc/instrumentable/noop.rb +33 -0
  37. data/lib/bones/rpc/loggable.rb +112 -0
  38. data/lib/bones/rpc/node.rb +317 -0
  39. data/lib/bones/rpc/node/registry.rb +32 -0
  40. data/lib/bones/rpc/parser.rb +114 -0
  41. data/lib/bones/rpc/parser/buffer.rb +80 -0
  42. data/lib/bones/rpc/protocol.rb +106 -0
  43. data/lib/bones/rpc/protocol/acknowledge.rb +82 -0
  44. data/lib/bones/rpc/protocol/adapter_helper.rb +164 -0
  45. data/lib/bones/rpc/protocol/binary_helper.rb +431 -0
  46. data/lib/bones/rpc/protocol/ext_message.rb +86 -0
  47. data/lib/bones/rpc/protocol/notify.rb +38 -0
  48. data/lib/bones/rpc/protocol/request.rb +45 -0
  49. data/lib/bones/rpc/protocol/response.rb +58 -0
  50. data/lib/bones/rpc/protocol/synchronize.rb +70 -0
  51. data/lib/bones/rpc/read_preference.rb +43 -0
  52. data/lib/bones/rpc/read_preference/nearest.rb +57 -0
  53. data/lib/bones/rpc/read_preference/selectable.rb +81 -0
  54. data/lib/bones/rpc/readable.rb +57 -0
  55. data/lib/bones/rpc/session.rb +195 -0
  56. data/lib/bones/rpc/uri.rb +222 -0
  57. data/lib/bones/rpc/version.rb +6 -0
  58. 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