bones-rpc 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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