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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-gemset +1 -1
  3. data/.ruby-version +1 -1
  4. data/bones-rpc.gemspec +1 -4
  5. data/lib/bones/rpc.rb +1 -1
  6. data/lib/bones/rpc/adapter.rb +44 -4
  7. data/lib/bones/rpc/adapter/json.rb +66 -0
  8. data/lib/bones/rpc/address.rb +6 -4
  9. data/lib/bones/rpc/backend.rb +31 -0
  10. data/lib/bones/rpc/backend/base.rb +30 -0
  11. data/lib/bones/rpc/backend/synchronous.rb +29 -0
  12. data/lib/bones/rpc/cluster.rb +18 -18
  13. data/lib/bones/rpc/connection.rb +19 -15
  14. data/lib/bones/rpc/connection/socket.rb +0 -2
  15. data/lib/bones/rpc/connection/socket/connectable.rb +15 -11
  16. data/lib/bones/rpc/context.rb +2 -2
  17. data/lib/bones/rpc/dns_resolver.rb +85 -0
  18. data/lib/bones/rpc/errors.rb +3 -0
  19. data/lib/bones/rpc/failover.rb +3 -3
  20. data/lib/bones/rpc/failover/disconnect.rb +2 -2
  21. data/lib/bones/rpc/failover/ignore.rb +2 -2
  22. data/lib/bones/rpc/failover/retry.rb +2 -2
  23. data/lib/bones/rpc/instrumentable.rb +3 -3
  24. data/lib/bones/rpc/instrumentable/log.rb +2 -2
  25. data/lib/bones/rpc/instrumentable/noop.rb +2 -2
  26. data/lib/bones/rpc/loggable.rb +8 -8
  27. data/lib/bones/rpc/node.rb +47 -37
  28. data/lib/bones/rpc/node/registry.rb +4 -0
  29. data/lib/bones/rpc/parser.rb +16 -5
  30. data/lib/bones/rpc/parser/buffer.rb +6 -2
  31. data/lib/bones/rpc/protocol/adapter_helper.rb +2 -2
  32. data/lib/bones/rpc/protocol/binary_helper.rb +2 -2
  33. data/lib/bones/rpc/read_preference.rb +3 -3
  34. data/lib/bones/rpc/read_preference/nearest.rb +3 -3
  35. data/lib/bones/rpc/read_preference/selectable.rb +4 -4
  36. data/lib/bones/rpc/readable.rb +4 -4
  37. data/lib/bones/rpc/session.rb +25 -16
  38. data/lib/bones/rpc/synchronous.rb +2 -0
  39. data/lib/bones/rpc/synchronous/connection.rb +36 -0
  40. data/lib/bones/rpc/synchronous/connection/reader.rb +59 -0
  41. data/lib/bones/rpc/synchronous/connection/socket.rb +4 -0
  42. data/lib/bones/rpc/synchronous/connection/socket/ssl.rb +57 -0
  43. data/lib/bones/rpc/synchronous/connection/socket/tcp.rb +30 -0
  44. data/lib/bones/rpc/synchronous/connection/writer.rb +86 -0
  45. data/lib/bones/rpc/synchronous/future.rb +91 -0
  46. data/lib/bones/rpc/synchronous/node.rb +45 -0
  47. data/lib/bones/rpc/uri.rb +20 -20
  48. data/lib/bones/rpc/version.rb +1 -1
  49. metadata +16 -52
  50. data/lib/bones/rpc/adapter/erlang.rb +0 -28
  51. data/lib/bones/rpc/adapter/msgpack.rb +0 -52
  52. data/lib/bones/rpc/connection/reader.rb +0 -49
  53. data/lib/bones/rpc/connection/socket/ssl.rb +0 -35
  54. data/lib/bones/rpc/connection/socket/tcp.rb +0 -28
  55. data/lib/bones/rpc/connection/writer.rb +0 -51
  56. data/lib/bones/rpc/future.rb +0 -26
@@ -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 2.0.0
9
+ # @since 0.0.1
12
10
  class Connection
13
11
 
14
12
  # The default connection timeout, in seconds.
15
13
  #
16
- # @since 2.0.0
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 1.0.0
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 1.0.0
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 1.0.0
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 1.0.0
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
- with_connection do |socket|
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 1.3.0
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 ||= Writer.new(self, @socket, node.adapter)
146
+ @writer ||= self.class.writer_class.new(self, @socket, node.adapter)
143
147
  end
144
148
  end
145
149
  end
@@ -1,4 +1,2 @@
1
1
  # encoding: utf-8
2
2
  require 'bones/rpc/connection/socket/connectable'
3
- require 'bones/rpc/connection/socket/ssl'
4
- require 'bones/rpc/connection/socket/tcp'
@@ -14,7 +14,7 @@ module Bones
14
14
  #
15
15
  # @return [ true, false ] If the socket is alive.
16
16
  #
17
- # @since 1.0.0
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 1.3.0
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 1.2.0
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 1.2.0
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 1.3.0
71
+ # @since 0.0.1
72
72
  def set_encoding(string)
73
- to_io.set_encoding(string)
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 1.0.0
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 1.4.0
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 1.4.0
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 1.0.0
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 1.0.0
179
+ # @since 0.0.1
176
180
  def connect(host, port, timeout)
177
181
  begin
178
182
  Timeout::timeout(timeout) do
@@ -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 1.0.0
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 1.0.0
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
@@ -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
 
@@ -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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
11
+ # @since 0.0.1
12
12
  TOPIC = "bones-rpc.operations"
13
13
 
14
14
  # Topic for warning instrumentation.
15
15
  #
16
- # @since 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
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 2.0.0
25
+ # @since 0.0.1
26
26
  def instrument(name, payload = {})
27
27
  yield payload if block_given?
28
28
  end