protobuf 2.0.0.rc3 → 2.0.0.rc4

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 (43) hide show
  1. data/lib/protobuf/cli.rb +1 -2
  2. data/lib/protobuf/{common/exceptions.rb → exceptions.rb} +0 -0
  3. data/lib/protobuf/field/base_field.rb +1 -1
  4. data/lib/protobuf/{common/logger.rb → logger.rb} +21 -0
  5. data/lib/protobuf/message/decoder.rb +2 -2
  6. data/lib/protobuf/message/encoder.rb +6 -4
  7. data/lib/protobuf/rpc/buffer.rb +2 -2
  8. data/lib/protobuf/rpc/client.rb +18 -18
  9. data/lib/protobuf/rpc/connectors/base.rb +3 -8
  10. data/lib/protobuf/rpc/connectors/common.rb +29 -28
  11. data/lib/protobuf/rpc/connectors/em_client.rb +9 -9
  12. data/lib/protobuf/rpc/connectors/eventmachine.rb +11 -9
  13. data/lib/protobuf/rpc/connectors/socket.rb +13 -17
  14. data/lib/protobuf/rpc/connectors/zmq.rb +13 -17
  15. data/lib/protobuf/rpc/error.rb +3 -3
  16. data/lib/protobuf/rpc/server.rb +41 -93
  17. data/lib/protobuf/rpc/servers/evented/server.rb +7 -9
  18. data/lib/protobuf/rpc/servers/evented_runner.rb +0 -11
  19. data/lib/protobuf/rpc/servers/socket/server.rb +8 -7
  20. data/lib/protobuf/rpc/servers/socket/worker.rb +22 -15
  21. data/lib/protobuf/rpc/servers/zmq/server.rb +3 -3
  22. data/lib/protobuf/rpc/servers/zmq/util.rb +1 -1
  23. data/lib/protobuf/rpc/servers/zmq/worker.rb +6 -15
  24. data/lib/protobuf/rpc/service.rb +145 -228
  25. data/lib/protobuf/rpc/service_dispatcher.rb +114 -0
  26. data/lib/protobuf/rpc/stat.rb +46 -33
  27. data/lib/protobuf/version.rb +1 -1
  28. data/lib/protobuf/{common/wire_type.rb → wire_type.rb} +0 -0
  29. data/spec/benchmark/tasks.rb +18 -18
  30. data/spec/functional/evented_server_spec.rb +3 -4
  31. data/spec/functional/socket_server_spec.rb +3 -3
  32. data/spec/functional/zmq_server_spec.rb +3 -3
  33. data/spec/lib/protobuf/{common/logger_spec.rb → logger_spec.rb} +46 -36
  34. data/spec/lib/protobuf/rpc/client_spec.rb +10 -58
  35. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +1 -39
  36. data/spec/lib/protobuf/rpc/connectors/common_spec.rb +3 -6
  37. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +0 -12
  38. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +1 -6
  39. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +94 -0
  40. data/spec/lib/protobuf/rpc/service_spec.rb +132 -45
  41. data/spec/spec_helper.rb +4 -3
  42. data/spec/support/server.rb +8 -4
  43. metadata +41 -35
@@ -8,43 +8,39 @@ module Protobuf
8
8
  include Protobuf::Logger::LogMethods
9
9
 
10
10
  def send_request
11
- check_async
12
11
  setup_connection
13
12
  connect_to_rpc_server
14
13
  post_init
15
14
  read_response
16
15
  end
17
16
 
18
- private
19
-
20
- def check_async
21
- if async?
22
- log_error { "[client-#{self.class}] Cannot run in async mode" }
23
- raise "Cannot use Socket client in async mode"
24
- end
17
+ def log_signature
18
+ @_log_signature ||= "[client-#{self.class}]"
25
19
  end
26
20
 
21
+ private
22
+
27
23
  def close_connection
28
24
  @socket.close
29
- log_debug { "[client-#{self.class}] Connector closed" }
25
+ log_debug { sign_message('Connector closed') }
30
26
  end
31
27
 
32
28
  def connect_to_rpc_server
33
29
  @socket = TCPSocket.new(options[:host], options[:port])
34
- log_debug { "[client-#{self.class}] Connection established #{options[:host]}:#{options[:port]}" }
30
+ log_debug { sign_message("Connection established #{options[:host]}:#{options[:port]}") }
35
31
  end
36
32
 
37
33
  # Method to determine error state, must be used with Connector api
38
34
  def error?
39
- return true if(@error)
40
- log_debug { "[client-#{self.class}] Error state : #{@socket.closed?}" }
35
+ return true if @error
36
+ log_debug { sign_message("Error state : #{@socket.closed?}") }
41
37
  @socket.closed?
42
38
  end
43
39
 
44
40
  def read_data
45
41
  size_io = StringIO.new
46
42
 
47
- while((size_reader = @socket.getc) != "-")
43
+ until (size_reader = @socket.getc) == "-"
48
44
  size_io << size_reader
49
45
  end
50
46
  str_size_io = size_io.string
@@ -53,8 +49,8 @@ module Protobuf
53
49
  end
54
50
 
55
51
  def read_response
56
- log_debug { "[client-#{self.class}] error? is #{error?}" }
57
- return if(error?)
52
+ log_debug { sign_message("error? is #{error?}") }
53
+ return if error?
58
54
  response_buffer = ::Protobuf::Rpc::Buffer.new(:read)
59
55
  response_buffer << read_data
60
56
  @stats.response_size = response_buffer.size
@@ -63,12 +59,12 @@ module Protobuf
63
59
  end
64
60
 
65
61
  def send_data
66
- return if(error?)
62
+ return if error?
67
63
  request_buffer = ::Protobuf::Rpc::Buffer.new(:write)
68
64
  request_buffer.set_data(@request_data)
69
65
  @socket.write(request_buffer.write)
70
66
  @socket.flush
71
- log_debug { "[client-#{self.class}] write closed" }
67
+ log_debug { sign_message("write closed") }
72
68
  end
73
69
 
74
70
  end
@@ -8,7 +8,6 @@ module Protobuf
8
8
  include Protobuf::Logger::LogMethods
9
9
 
10
10
  def send_request
11
- check_async
12
11
  setup_connection
13
12
  connect_to_rpc_server
14
13
  post_init
@@ -19,29 +18,26 @@ module Protobuf
19
18
  @zmq_context = nil
20
19
  end
21
20
 
22
- private
23
-
24
- def check_async
25
- if async?
26
- log_error { "[client-#{self.class}] Cannot run in async mode" }
27
- raise "Cannot use Zmq client in async mode"
28
- end
21
+ def log_signature
22
+ @_log_signature ||= "[client-#{self.class}]"
29
23
  end
30
24
 
25
+ private
26
+
31
27
  def close_connection
32
- return if(@error)
28
+ return if @error
33
29
  zmq_error_check(@socket.close)
34
30
  zmq_error_check(@zmq_context.terminate)
35
- log_debug { "[client-#{self.class}] Connector closed" }
31
+ log_debug { sign_message("Connector closed") }
36
32
  end
37
33
 
38
34
  def connect_to_rpc_server
39
- return if(@error)
40
- log_debug { "[client-#{self.class} Establishing connection: #{options[:host]}:#{options[:port]}" }
35
+ return if @error
36
+ log_debug { sign_message("Establishing connection: #{options[:host]}:#{options[:port]}") }
41
37
  @zmq_context = ::ZMQ::Context.new
42
38
  @socket = @zmq_context.socket(::ZMQ::REQ)
43
39
  zmq_error_check(@socket.connect("tcp://#{options[:host]}:#{options[:port]}"))
44
- log_debug { "[client-#{self.class}] Connection established #{options[:host]}:#{options[:port]}" }
40
+ log_debug { sign_message("Connection established #{options[:host]}:#{options[:port]}") }
45
41
  end
46
42
 
47
43
  # Method to determine error state, must be used with Connector api
@@ -50,18 +46,18 @@ module Protobuf
50
46
  end
51
47
 
52
48
  def read_response
53
- return if(@error)
49
+ return if @error
54
50
  @response_data = ''
55
51
  zmq_error_check(@socket.recv_string(@response_data))
56
52
  parse_response
57
53
  end
58
54
 
59
55
  def send_data
60
- return if(@error)
61
- log_debug { "[#{log_signature}] Sending Request: %s" % @request_data }
56
+ return if @error
57
+ log_debug { sign_message("Sending Request: #{@request_data}") }
62
58
  @stats.request_size = @request_data.size
63
59
  zmq_error_check(@socket.send_string(@request_data))
64
- log_debug { "[client-#{self.class}] write closed" }
60
+ log_debug { sign_message("write closed") }
65
61
  end
66
62
 
67
63
  def zmq_error_check(return_code)
@@ -3,16 +3,16 @@ require 'protobuf/rpc/rpc.pb'
3
3
  module Protobuf
4
4
  module Rpc
5
5
  ClientError = Struct.new("ClientError", :code, :message)
6
-
6
+
7
7
  # Base PbError class for client and server errors
8
8
  class PbError < StandardError
9
9
  attr_reader :error_type
10
-
10
+
11
11
  def initialize message='An unknown RpcError occurred', error_type='RPC_ERROR'
12
12
  @error_type = error_type.is_a?(String) ? ::Protobuf::Socketrpc::ErrorReason.const_get(error_type) : error_type
13
13
  super message
14
14
  end
15
-
15
+
16
16
  def to_response response
17
17
  response.error = message
18
18
  response.error_reason = @error_type
@@ -1,140 +1,88 @@
1
- require 'protobuf/common/logger'
1
+ require 'protobuf/logger'
2
2
  require 'protobuf/rpc/rpc.pb'
3
3
  require 'protobuf/rpc/buffer'
4
4
  require 'protobuf/rpc/error'
5
5
  require 'protobuf/rpc/stat'
6
+ require 'protobuf/rpc/service_dispatcher'
6
7
 
7
8
  module Protobuf
8
9
  module Rpc
9
10
  module Server
10
11
 
12
+ def initialize_request!
13
+ log_debug { sign_message('Post init') }
14
+ @request = ::Protobuf::Socketrpc::Request.new
15
+ @response = ::Protobuf::Socketrpc::Response.new
16
+ @stats = Protobuf::Rpc::Stat.new(:SERVER)
17
+ set_peer
18
+ end
19
+
20
+ def disable_gc!
21
+ ::GC.disable if ::Protobuf.gc_pause_server_request?
22
+ end
23
+
24
+ def enable_gc!
25
+ ::GC.enable if ::Protobuf.gc_pause_server_request?
26
+ end
27
+
28
+ # no-op, implemented by including class if desired.
29
+ def set_peer; end
30
+
11
31
  # Invoke the service method dictated by the proto wrapper request object
12
32
  def handle_client
13
- # Parse the protobuf request from the socket
14
- log_debug { "[#{log_signature}] Parsing request from client" }
15
33
  parse_request_from_buffer
34
+ @dispatcher = ::Protobuf::Rpc::ServiceDispatcher.new(@request)
35
+ @stats.dispatcher = @dispatcher
16
36
 
17
- # Determine the service class and method name from the request
18
- log_debug { "[#{log_signature}] Extracting procedure call info from request" }
19
- parse_service_info
20
-
21
- # Call the service method
22
- log_debug { "[#{log_signature}] Dispatching client request to service" }
23
- ::GC.disable if ::Protobuf.gc_pause_server_request?
24
- invoke_rpc_method
37
+ disable_gc!
38
+ @dispatcher.invoke!
39
+ if @dispatcher.success?
40
+ @response.response_proto = @dispatcher.response
41
+ else
42
+ handle_error(@dispatcher.error)
43
+ end
25
44
  rescue => error
26
- # Ensure we're handling any errors that try to slip out the back door
27
- log_error(error.message)
28
- log_error(error.backtrace.join("\n"))
45
+ log_exception(error)
29
46
  handle_error(error)
47
+ ensure
30
48
  send_response
31
49
  end
32
50
 
33
51
  # Client error handler. Receives an exception object and writes it into the @response
34
52
  def handle_error(error)
35
- log_debug { "[#{log_signature}] handle_error: %s" % error.inspect }
53
+ log_debug { sign_message("handle_error: #{error.inspect}") }
36
54
  if error.respond_to?(:to_response)
37
55
  error.to_response(@response)
38
56
  else
39
57
  message = error.respond_to?(:message) ? error.message : error.to_s
40
- code = error.respond_to?(:code) ? error.code.to_s : "RPC_ERROR"
58
+ code = error.respond_to?(:code) ? error.code : 'RPC_ERROR'
41
59
  ::Protobuf::Rpc::PbError.new(message, code).to_response(@response)
42
60
  end
43
61
  end
44
62
 
45
- # Assuming all things check out, we can call the service method
46
- def invoke_rpc_method
47
- # Get a new instance of the service
48
- @service = @klass.new
49
-
50
- # Define our response callback to perform the "successful" response to our client
51
- # This decouples the service's rpc method from our response to the client,
52
- # allowing the service to be the dictator for when the response should be sent back.
53
- #
54
- # In other words, we don't send the response once the service method finishes executing
55
- # since the service may perform it's own operations asynchronously.
56
- @service.on_send_response do |response|
57
- unless @did_respond
58
- parse_response_from_service(response)
59
- send_response
60
- end
61
- end
62
-
63
- @service.on_rpc_failed do |error|
64
- unless @did_respond
65
- handle_error(error)
66
- send_response
67
- end
68
- end
69
-
70
- # Call the service method
71
- log_debug { "[#{log_signature}] Invoking %s#%s with request %s" % [@klass.name, @method, @request.inspect] }
72
- @service.__send__(@method, @request)
73
- end
74
-
75
63
  def log_signature
76
- @log_signature ||= "server-#{self.class}"
64
+ @_log_signature ||= "[server-#{self.class.name}]"
77
65
  end
78
66
 
79
67
  # Parse the incoming request object into our expected request object
80
68
  def parse_request_from_buffer
81
- log_debug { "[#{log_signature}] parsing request from buffer: %s" % @request_data }
69
+ log_debug { sign_message("Parsing request from buffer: #{@request_data}") }
82
70
  @request.parse_from_string(@request_data)
83
71
  rescue => error
84
- exc = ::Protobuf::Rpc::BadRequestData.new 'Unable to parse request: %s' % error.message
72
+ exc = ::Protobuf::Rpc::BadRequestData.new("Unable to parse request: #{error.message}")
85
73
  log_error { exc.message }
86
74
  raise exc
87
75
  end
88
76
 
89
- # Read out the response from the service method,
90
- # setting it on the pb request, and serializing the
91
- # response to the protobuf response wrapper
92
- def parse_response_from_service(response)
93
- expected = @klass.rpcs[@klass][@method].response_type
94
-
95
- # Cannibalize the response if it's a Hash
96
- response = expected.new(response) if response.is_a?(Hash)
97
- actual = response.class
98
- log_debug { "[#{log_signature}] response (should/actual): %s/%s" % [expected.name, actual.name] }
99
-
100
- # Determine if the service tried to change response types on us
101
- if expected != actual
102
- raise ::Protobuf::Rpc::BadResponseProto, 'Response proto changed from %s to %s' % [expected.name, actual.name]
103
- else
104
- @response.response_proto = response
105
- end
106
- rescue => error
107
- log_error error.message
108
- log_error error.backtrace.join("\n")
109
- handle_error(error)
110
- end
111
-
112
- # Parses and returns the service and method name from the request wrapper proto
113
- def parse_service_info
114
- @klass = @request.service_name.constantize
115
- @method = @request.method_name.underscore.to_sym
116
-
117
- unless @klass.instance_methods.include?(@method)
118
- raise MethodNotFound, "Service method #{@request.method_name} is not defined by the service"
119
- end
120
-
121
- @stats.service = @klass.name
122
- @stats.method = @method
123
- rescue NameError
124
- raise ServiceNotFound, "Service class #{@request.service_name} is not found"
125
- end
126
-
127
77
  # Write the response wrapper to the client
128
78
  def send_response
129
- raise 'Response already sent to client' if @did_respond
130
- log_debug { "[#{log_signature}] Sending response to client: %s" % @response.inspect }
79
+ log_debug { sign_message("Sending response to client: #{@response.inspect}") }
131
80
  send_data
132
- @stats.end
133
- @stats.log_stats
134
- @did_respond = true
81
+ @stats.stop && log_info { @stats.to_s }
135
82
  ensure
136
- ::GC.enable if ::Protobuf.gc_pause_server_request?
83
+ enable_gc!
137
84
  end
138
85
  end
139
86
  end
140
87
  end
88
+
@@ -9,19 +9,13 @@ module Protobuf
9
9
 
10
10
  # Initialize a new read buffer for storing client request info
11
11
  def post_init
12
- log_debug { '[server] Post init, new read buffer created' }
13
- @stats = Protobuf::Rpc::Stat.new(:SERVER, true)
14
- @stats.client = ::Socket.unpack_sockaddr_in(get_peername)
12
+ initialize_request!
15
13
  @request_buffer = Protobuf::Rpc::Buffer.new(:read)
16
- @request = ::Protobuf::Socketrpc::Request.new
17
- @response = ::Protobuf::Socketrpc::Response.new
18
-
19
- @did_respond = false
20
14
  end
21
15
 
22
16
  # Receive a chunk of data, potentially flushed to handle_client
23
17
  def receive_data(data)
24
- log_debug { '[server] receive_data: %s' % data }
18
+ log_debug { sign_message("receive_data: #{data}") }
25
19
 
26
20
  @request_buffer << data
27
21
  @request_data = @request_buffer.data
@@ -34,9 +28,13 @@ module Protobuf
34
28
  response_buffer = Protobuf::Rpc::Buffer.new(:write)
35
29
  response_buffer.set_data(@response)
36
30
  @stats.response_size = response_buffer.size
37
- log_debug { "[#{log_signature}] sending data: #{response_buffer.inspect}" }
31
+ log_debug { sign_message("sending data: #{response_buffer.inspect}") }
38
32
  super(response_buffer.write)
39
33
  end
34
+
35
+ def set_peer
36
+ @stats.client = ::Socket.unpack_sockaddr_in(get_peername)
37
+ end
40
38
  end
41
39
  end
42
40
  end
@@ -7,17 +7,6 @@ module Protobuf
7
7
  end
8
8
 
9
9
  def self.run(server)
10
- # Ensure errors thrown within EM are caught and logged appropriately
11
- #EventMachine.error_handler do |error|
12
- # raise error
13
- # if error.message == 'no acceptor'
14
- # raise 'Failed binding to %s:%d (%s)' % [server.host, server.port, error.message]
15
- # else
16
- # Protobuf::Logger.error(error.message)
17
- # Protobuf::Logger.error(error.backtrace.join("\n"))
18
- # end
19
- #end
20
-
21
10
  # Startup and run the rpc server
22
11
  ::EventMachine.schedule do
23
12
  ::EventMachine.start_server(server.host, server.port, ::Protobuf::Rpc::Evented::Server)
@@ -1,5 +1,6 @@
1
1
  require 'protobuf/rpc/server'
2
2
  require 'protobuf/rpc/servers/socket/worker'
3
+
3
4
  module Protobuf
4
5
  module Rpc
5
6
  module Socket
@@ -14,7 +15,7 @@ module Protobuf
14
15
  end
15
16
 
16
17
  def self.cleanup_threads
17
- log_debug { "[#{log_signature}] Thread cleanup - #{@threads.size} - start" }
18
+ log_debug { sign_message("Thread cleanup - #{@threads.size} - start") }
18
19
 
19
20
  @threads = @threads.select do |t|
20
21
  if t[:thread].alive?
@@ -26,11 +27,11 @@ module Protobuf
26
27
  end
27
28
  end
28
29
 
29
- log_debug { "[#{log_signature}] Thread cleanup - #{@threads.size} - complete" }
30
+ log_debug { sign_message("Thread cleanup - #{@threads.size} - complete") }
30
31
  end
31
32
 
32
33
  def self.log_signature
33
- @log_signature ||= "server-#{self}"
34
+ @_log_signature ||= "server-#{self.class.name}"
34
35
  end
35
36
 
36
37
  def self.new_worker(socket)
@@ -42,7 +43,7 @@ module Protobuf
42
43
  end
43
44
 
44
45
  def self.run(opts = {})
45
- log_debug { "[#{log_signature}] Run" }
46
+ log_debug { sign_message("Run") }
46
47
  host = opts.fetch(:host, "127.0.0.1")
47
48
  port = opts.fetch(:port, 9399)
48
49
  backlog = opts.fetch(:backlog, 100)
@@ -59,7 +60,7 @@ module Protobuf
59
60
  @running = true
60
61
 
61
62
  while running?
62
- log_debug { "[#{log_signature}] Waiting for connections" }
63
+ log_debug { sign_message("Waiting for connections") }
63
64
 
64
65
  if ready_cnxns = IO.select(@listen_fds, [], [], auto_collect_timeout)
65
66
  cnxns = ready_cnxns.first
@@ -68,13 +69,13 @@ module Protobuf
68
69
  when !running? then
69
70
  # no-op
70
71
  when client == @server then
71
- log_debug { "[#{log_signature}] Accepted new connection" }
72
+ log_debug { sign_message("Accepted new connection") }
72
73
  client, sockaddr = @server.accept
73
74
  @listen_fds << client
74
75
  else
75
76
  if !@working.include?(client)
76
77
  @working << @listen_fds.delete(client)
77
- log_debug { "[#{log_signature}] Working" }
78
+ log_debug { sign_message("Working") }
78
79
  @threads << { :thread => new_worker(client), :socket => client }
79
80
 
80
81
  cleanup_threads if cleanup?