protobuf 2.8.0 → 2.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1793f24401f3a3def50cf704b6beb4bb27887efc
4
- data.tar.gz: 6dbc2f1080b4d2a3f3c01cfa2248ea8945aec972
3
+ metadata.gz: 1d622f286e789d42300ea06beabd355cdab3530e
4
+ data.tar.gz: 6d4a9e0fe5fc77d8bd8c65eec0befba2ff6cf9c3
5
5
  SHA512:
6
- metadata.gz: 6107077595047bd1ce774463faf020075b3a3d1b494ce8a814dac9eab5d124b36a6eb6dfc7fd3f9010c0a54d9c7ea6bbc18d4c04181a07827814c3609dc4bd94
7
- data.tar.gz: c2598cb57886e62c86fb77cccdbfc8629ca9c78c3a26eabb8c9cd675be58000cddbc7c71928d53a3e0245b34cccbc4670334f5ebd9869c2b94831f2cf914c68b
6
+ metadata.gz: 2aa2229e5701441dce3d776080f00f3306796440e55f7b085cafa03539fcbc1e35c0067d5d14de7d0285ebbdced0b708cd854b88579af2330974a587dd6e012e
7
+ data.tar.gz: 2080bb81394099f161a98ec44622f54343333ac3381a09da2d327041d7f46fae9b2c45a1ed0936bac9dd5ce96e442afa13b6ffbc9c9b7750182448e7f3aedbda
data/CHANGES.md CHANGED
@@ -1,3 +1,11 @@
1
+ 2.8.1
2
+ ----------
3
+
4
+ - Improve `ServiceDirectory` lookup speed ~10x, lookups now done in constant time (devin-c).
5
+ - Add Timestamp to end of rpc stat log (represents ending time of request processing).
6
+ - Set `request_size` in the rpc stat within ZMQ Worker (previously missing).
7
+ - Ensure `request_size` and `response_size` are set on rpc stat for client requests.
8
+
1
9
  2.8.0
2
10
  -----------
3
11
 
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
- $:.push File.expand_path("./", File.dirname(__FILE__))
2
- $:.push File.expand_path("./spec", File.dirname(__FILE__))
1
+ $: << ::File.expand_path('../', __FILE__)
2
+ $: << ::File.expand_path('../spec', __FILE__)
3
3
 
4
4
  require "rubygems"
5
5
  require "rubygems/package_task"
@@ -13,17 +13,3 @@ task :default => :spec
13
13
 
14
14
  desc "Run specs"
15
15
  RSpec::Core::RakeTask.new(:spec)
16
-
17
- ##
18
- # rake-compiler
19
- #
20
- spec = Gem::Specification.load("protobuf.gemspec")
21
-
22
- Gem::PackageTask.new(spec) do |pkg|
23
- end
24
-
25
- if RUBY_PLATFORM =~ /java/
26
- require "rake/javaextensiontask"
27
- else
28
- require "rake/extensiontask"
29
- end
@@ -74,6 +74,7 @@ module Protobuf
74
74
  log_debug { sign_message("Parsing response from server (connection closed)") }
75
75
 
76
76
  # Parse out the raw response
77
+ @stats.response_size = @response_data.size
77
78
  response_wrapper = Protobuf::Socketrpc::Response.decode(@response_data)
78
79
 
79
80
  # Determine success or failure based on parsed data
@@ -120,6 +121,7 @@ module Protobuf
120
121
  def setup_connection
121
122
  initialize_stats
122
123
  @request_data = request_bytes
124
+ @stats.request_size = request_bytes.size
123
125
  end
124
126
 
125
127
  def succeed(response)
@@ -55,7 +55,6 @@ module Protobuf
55
55
  return if error?
56
56
  response_buffer = ::Protobuf::Rpc::Buffer.new(:read)
57
57
  response_buffer << read_data
58
- @stats.response_size = response_buffer.size
59
58
  @response_data = response_buffer.data
60
59
  parse_response if response_buffer.flushed?
61
60
  end
@@ -115,7 +115,6 @@ module Protobuf
115
115
  def send_request_with_lazy_pirate
116
116
  attempt = 0
117
117
  timeout = options[:timeout].to_f
118
- @stats.request_size = @request_data.size
119
118
 
120
119
  begin
121
120
  attempt += 1
@@ -67,6 +67,7 @@ module Protobuf
67
67
  # Parse the incoming request object into our expected request object
68
68
  def parse_request_from_buffer
69
69
  log_debug { sign_message("Parsing request from buffer: #{@request_data}") }
70
+ @stats.request_size = @request_data.size
70
71
  @request.decode(@request_data)
71
72
  rescue => error
72
73
  exc = ::Protobuf::Rpc::BadRequestData.new("Unable to parse request: #{error.message}")
@@ -19,7 +19,6 @@ module Protobuf
19
19
 
20
20
  @request_buffer << data
21
21
  @request_data = @request_buffer.data
22
- @stats.request_size = @request_buffer.size
23
22
 
24
23
  handle_client if @request_buffer.flushed?
25
24
  end
@@ -20,8 +20,6 @@ module Protobuf
20
20
  request_buffer << read_data
21
21
  @request_data = request_buffer.data
22
22
 
23
- @stats.request_size = request_buffer.size
24
-
25
23
  log_debug { sign_message("handling request") }
26
24
  handle_client if request_buffer.flushed?
27
25
  end
@@ -1,6 +1,7 @@
1
1
  require 'delegate'
2
2
  require 'singleton'
3
3
  require 'socket'
4
+ require 'set'
4
5
  require 'thread'
5
6
  require 'timeout'
6
7
 
@@ -21,20 +22,32 @@ module Protobuf
21
22
  attr_reader :expires_at
22
23
 
23
24
  def initialize(server)
24
- @server = server
25
- @expires_at = Time.now.to_i + ttl
25
+ update(server)
26
26
  end
27
27
 
28
28
  def current?
29
29
  !expired?
30
30
  end
31
31
 
32
+ def eql?(other)
33
+ uuid.eql?(other.uuid)
34
+ end
35
+
32
36
  def expired?
33
37
  Time.now.to_i >= @expires_at
34
38
  end
35
39
 
40
+ def hash
41
+ uuid.hash
42
+ end
43
+
36
44
  def ttl
37
- [super.to_i, 3].max
45
+ [super.to_i, 1].max
46
+ end
47
+
48
+ def update(server)
49
+ @server = server
50
+ @expires_at = Time.now.to_i + ttl
38
51
  end
39
52
 
40
53
  def __getobj__
@@ -65,70 +78,22 @@ module Protobuf
65
78
  self.instance.stop
66
79
  end
67
80
 
81
+ #
68
82
  # Instance Methods
69
83
  #
70
84
  def initialize
71
- @listings = {}
72
- @mutex = Mutex.new
73
- end
74
-
75
- def add_listing_for(server)
76
- if server && server.uuid
77
- @mutex.synchronize do
78
- action = @listings.key?(server.uuid) ? :updated : :added
79
- log_debug { sign_message("#{action} server: #{server.inspect}") }
80
-
81
- listing = Listing.new(server)
82
- @listings[server.uuid] = listing
83
- trigger(action, listing)
84
- end
85
- else
86
- log_info { sign_message("Cannot add server without uuid: #{server.inspect}") }
87
- end
85
+ reset
88
86
  end
89
87
 
90
88
  def each_listing(&block)
91
- @listings.each_value(&block)
89
+ @listings_by_uuid.each_value(&block)
92
90
  end
93
91
 
94
92
  def lookup(service)
95
93
  if running?
96
- @mutex.synchronize do
97
- listings = @listings.values.select do |listing|
98
- listing.services.any? do |listed_service|
99
- listing.current? && listed_service == service.to_s
100
- end
101
- end
102
-
103
- listings.sample
104
- end
105
- end
106
- end
107
-
108
- def remove_expired_listings
109
- @mutex.synchronize do
110
- @listings.delete_if do |uuid, listing|
111
- if listing.expired?
112
- trigger(:removed, listing)
113
- true
114
- else
115
- false
116
- end
117
- end
118
- end
119
- end
120
-
121
- def remove_listing_for(server)
122
- if server && server.uuid
123
- log_debug { sign_message("Removing server: #{server.inspect}") }
124
-
125
- @mutex.synchronize do
126
- deleted_listing = @listings.delete(server.uuid)
127
- trigger(:removed, deleted_listing)
94
+ if @listings_by_service.key?(service)
95
+ @listings_by_service[service].entries.sample
128
96
  end
129
-
130
- else
131
- log_info { sign_message("Cannot remove server without uuid: #{server.inspect}") }
132
97
  end
133
98
  end
134
99
 
@@ -154,18 +119,34 @@ module Protobuf
154
119
  def stop
155
120
  log_info { sign_message("Stopping directory") }
156
121
 
157
- @mutex.synchronize do
158
- @thread.try(:kill)
159
- @thread = nil
160
- @listings = {}
161
- end
162
-
122
+ @thread.try(:kill).try(:join)
163
123
  @socket.try(:close)
164
- @socket = nil
124
+
125
+ reset
165
126
  end
166
127
 
167
128
  private
168
129
 
130
+ def add_or_update_listing(uuid, server)
131
+ listing = @listings_by_uuid[uuid]
132
+
133
+ if listing
134
+ action = :updated
135
+ listing.update(server)
136
+ else
137
+ action = :added
138
+ listing = Listing.new(server)
139
+ @listings_by_uuid[uuid] = listing
140
+ end
141
+
142
+ listing.services.each do |service|
143
+ @listings_by_service[service] << listing
144
+ end
145
+
146
+ trigger(action, listing)
147
+ log_debug { sign_message("#{action} server: #{server.inspect}") }
148
+ end
149
+
169
150
  def init_socket
170
151
  @socket = UDPSocket.new
171
152
  @socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true)
@@ -178,39 +159,80 @@ module Protobuf
178
159
  end
179
160
 
180
161
  def process_beacon(beacon)
181
- case beacon.beacon_type
182
- when ::Protobuf::Rpc::DynamicDiscovery::BeaconType::HEARTBEAT
183
- add_listing_for(beacon.server)
184
- when ::Protobuf::Rpc::DynamicDiscovery::BeaconType::FLATLINE
185
- remove_listing_for(beacon.server)
162
+ server = beacon.server
163
+ uuid = server.try(:uuid)
164
+
165
+ if server && uuid
166
+ case beacon.beacon_type
167
+ when ::Protobuf::Rpc::DynamicDiscovery::BeaconType::HEARTBEAT
168
+ add_or_update_listing(uuid, server)
169
+ when ::Protobuf::Rpc::DynamicDiscovery::BeaconType::FLATLINE
170
+ remove_listing(uuid)
171
+ end
172
+ else
173
+ log_info { sign_message("Ignoring incomplete beacon: #{beacon.inspect}") }
174
+ end
175
+ end
176
+
177
+ def read_beacon
178
+ data, addr = @socket.recvfrom(2048)
179
+
180
+ beacon = ::Protobuf::Rpc::DynamicDiscovery::Beacon.decode(data)
181
+
182
+ # Favor the address captured by the socket
183
+ beacon.try(:server).try(:address=, addr[3])
184
+
185
+ beacon
186
+ end
187
+
188
+ def remove_expired_listings
189
+ log_debug { sign_message("Removing expired listings") }
190
+ @listings_by_uuid.each do |uuid, listing|
191
+ remove_listing(uuid) if listing.expired?
186
192
  end
187
193
  end
188
194
 
195
+ def remove_listing(uuid)
196
+ listing = @listings_by_uuid[uuid] or return
197
+
198
+ log_debug { sign_message("Removing listing: #{listing.inspect}") }
199
+
200
+ @listings_by_service.each do |service, listings|
201
+ listings.delete(listing)
202
+ end
203
+
204
+ trigger(:removed, @listings_by_uuid.delete(uuid))
205
+ end
206
+
207
+ def reset
208
+ @thread = nil
209
+ @socket = nil
210
+ @listings_by_uuid = {}
211
+ @listings_by_service = Hash.new { |h, k| h[k] = Set.new }
212
+ end
213
+
189
214
  def run
215
+ sweep_interval = 1 # sweep expired listings every 1 second
216
+ next_sweep = Time.now.to_i + sweep_interval
217
+
190
218
  loop do
191
- beacon = wait_for_beacon
192
- process_beacon(beacon)
193
- remove_expired_listings
219
+ timeout = [next_sweep - Time.now.to_i, 0.1].max
220
+ readable = IO.select([@socket], nil, nil, timeout)
221
+ process_beacon(read_beacon) if readable
222
+
223
+ if Time.now.to_i >= next_sweep
224
+ remove_expired_listings
225
+ next_sweep = Time.now.to_i + sweep_interval
226
+ end
194
227
  end
195
228
  rescue => e
196
- log_debug { sign_message("error: (#{e.class}) #{e.message}") }
229
+ log_debug { sign_message("ERROR: (#{e.class}) #{e.message}\n#{e.backtrace.join("\n")}") }
197
230
  retry
198
231
  end
199
232
 
200
233
  def trigger(action, listing)
201
234
  ::Protobuf::Lifecycle.trigger("directory.listing.#{action}", listing)
202
235
  end
203
-
204
- def wait_for_beacon
205
- data, addr = @socket.recvfrom(2048)
206
-
207
- beacon = ::Protobuf::Rpc::DynamicDiscovery::Beacon.decode(data)
208
-
209
- # Favor the address captured by the socket
210
- beacon.try(:server).try(:address=, addr[3])
211
-
212
- beacon
213
- end
214
236
  end
215
237
  end
216
238
  end
@@ -1,4 +1,5 @@
1
1
  require 'date'
2
+ require 'time'
2
3
  require 'protobuf/logger'
3
4
 
4
5
  module Protobuf
@@ -11,6 +12,8 @@ module Protobuf
11
12
 
12
13
  def initialize(mode = :SERVER)
13
14
  @mode = mode
15
+ @request_size = 0
16
+ @response_size = 0
14
17
  start
15
18
  end
16
19
 
@@ -43,16 +46,20 @@ module Protobuf
43
46
  end
44
47
 
45
48
  def sizes
46
- "#{@request_size || 0}B/#{@response_size || 0}B" if stopped?
49
+ if stopped?
50
+ "#{@request_size}B/#{@response_size}B"
51
+ else
52
+ "#{@request_size}B/-"
53
+ end
47
54
  end
48
55
 
49
56
  def start
50
- @start_time ||= Time.now
57
+ @start_time ||= ::Time.now
51
58
  end
52
59
 
53
60
  def stop
54
61
  start unless @start_time
55
- @end_time ||= Time.now
62
+ @end_time ||= ::Time.now
56
63
  end
57
64
 
58
65
  def stopped?
@@ -77,13 +84,14 @@ module Protobuf
77
84
  server? ? client : server,
78
85
  trace_id,
79
86
  rpc,
87
+ sizes,
80
88
  elapsed_time,
81
- sizes
89
+ @end_time.try(:iso8601)
82
90
  ].compact.join(' - ')
83
91
  end
84
92
 
85
93
  def trace_id
86
- Thread.current.object_id.to_s(16)
94
+ ::Thread.current.object_id.to_s(16)
87
95
  end
88
96
 
89
97
  end
@@ -1,4 +1,4 @@
1
1
  module Protobuf
2
- VERSION = '2.8.0'
3
- PROTOC_VERSION = '2.4.1'
2
+ VERSION = '2.8.1'
3
+ PROTOC_VERSION = '2.5.0'
4
4
  end
@@ -27,9 +27,9 @@ require "protobuf/version"
27
27
  s.add_development_dependency 'ffi-rzmq'
28
28
  s.add_development_dependency 'pry-nav'
29
29
  s.add_development_dependency 'rake'
30
- s.add_development_dependency 'rake-compiler'
31
30
  s.add_development_dependency 'rspec'
32
31
  s.add_development_dependency 'simplecov'
33
32
  s.add_development_dependency 'yard'
33
+ s.add_development_dependency 'timecop'
34
34
  # s.add_development_dependency 'perftools.rb'
35
35
  end
@@ -15,8 +15,8 @@ describe ::Protobuf::CodeGenerator do
15
15
  let(:output_file1) { COMPILER::CodeGeneratorResponse::File.new(:name => 'test/foo.pb.rb') }
16
16
  let(:output_file2) { COMPILER::CodeGeneratorResponse::File.new(:name => 'test/bar.pb.rb') }
17
17
 
18
- let(:file_generator1) { mock('file generator 1', :generate_output_file => output_file1) }
19
- let(:file_generator2) { mock('file generator 2', :generate_output_file => output_file2) }
18
+ let(:file_generator1) { double('file generator 1', :generate_output_file => output_file1) }
19
+ let(:file_generator2) { double('file generator 2', :generate_output_file => output_file2) }
20
20
 
21
21
  let(:request_bytes) do
22
22
  COMPILER::CodeGeneratorRequest.encode(:proto_file => [ input_file1, input_file2 ])
@@ -5,10 +5,10 @@ require 'protobuf/generators/base'
5
5
 
6
6
  describe ::Protobuf::Generators::Base do
7
7
 
8
- subject { described_class.new(mock) }
8
+ subject { described_class.new(double) }
9
9
 
10
10
  context 'namespaces' do
11
- let(:descriptor) { mock(:name => 'Baz') }
11
+ let(:descriptor) { double(:name => 'Baz') }
12
12
  subject { described_class.new(descriptor, 0, :namespace => [ :foo, :bar ]) }
13
13
  its(:type_namespace) { should eq [ :foo, :bar, 'Baz' ] }
14
14
  its(:fully_qualified_type_namespace) { should eq '.foo.bar.Baz' }
@@ -55,7 +55,7 @@ describe ::Protobuf::Generators::Base do
55
55
  end
56
56
  end
57
57
 
58
- subject { ToStringTest.new(mock) }
58
+ subject { ToStringTest.new(double) }
59
59
 
60
60
  it 'compiles and returns the contents' do
61
61
  10.times do
@@ -7,9 +7,9 @@ describe ::Protobuf::Generators::ExtensionGenerator do
7
7
 
8
8
  let(:field_descriptors) {
9
9
  [
10
- mock('field descriptor 1', :to_s => " field 1\n"),
11
- mock('field descriptor 2', :to_s => " field 2\n"),
12
- mock('field descriptor 3', :to_s => " field 3\n")
10
+ double('field descriptor 1', :to_s => " field 1\n"),
11
+ double('field descriptor 2', :to_s => " field 2\n"),
12
+ double('field descriptor 3', :to_s => " field 3\n")
13
13
  ]
14
14
  }
15
15
  let(:message_type) { 'FooBar' }
@@ -12,20 +12,20 @@ describe ::Protobuf::Rpc::Connectors::Zmq do
12
12
  :port => "9400"
13
13
  }}
14
14
 
15
- let(:socket_mock) do
16
- sm = mock(::ZMQ::Socket)
15
+ let(:socket_double) do
16
+ sm = double(::ZMQ::Socket)
17
17
  sm.stub(:connect).and_return(0)
18
18
  sm
19
19
  end
20
20
 
21
- let(:zmq_context_mock) do
22
- zc = mock(::ZMQ::Context)
23
- zc.stub(:socket).and_return(socket_mock)
21
+ let(:zmq_context_double) do
22
+ zc = double(::ZMQ::Context)
23
+ zc.stub(:socket).and_return(socket_double)
24
24
  zc
25
25
  end
26
26
 
27
27
  before do
28
- ::ZMQ::Context.stub(:new).and_return(zmq_context_mock)
28
+ ::ZMQ::Context.stub(:new).and_return(zmq_context_double)
29
29
  end
30
30
 
31
31
  describe "#lookup_server_uri" do
@@ -3,185 +3,268 @@ require 'spec_helper'
3
3
  require 'protobuf/rpc/service_directory'
4
4
 
5
5
  describe ::Protobuf::Rpc::ServiceDirectory do
6
- let(:instance) { ::Protobuf::Rpc::ServiceDirectory.instance }
7
-
8
- def listings
9
- instance.instance_variable_get(:@listings)
6
+ subject { described_class.instance }
7
+
8
+ let(:echo_server) {
9
+ ::Protobuf::Rpc::DynamicDiscovery::Server.new(
10
+ :uuid => 'echo',
11
+ :address => '127.0.0.1',
12
+ :port => '1111',
13
+ :ttl => 10,
14
+ :services => %w[EchoService]
15
+ )
16
+ }
17
+
18
+ let(:hello_server) {
19
+ ::Protobuf::Rpc::DynamicDiscovery::Server.new(
20
+ :uuid => "hello",
21
+ :address => '127.0.0.1',
22
+ :port => "1112",
23
+ :ttl => 10,
24
+ :services => %w[HelloService]
25
+ )
26
+ }
27
+
28
+ let(:hello_server_with_short_ttl) {
29
+ ::Protobuf::Rpc::DynamicDiscovery::Server.new(
30
+ :uuid => "hello_server_with_short_ttl",
31
+ :address => '127.0.0.1',
32
+ :port => '1113',
33
+ :ttl => 1,
34
+ :services => %w[HelloService]
35
+ )
36
+ }
37
+
38
+ let(:combo_server) {
39
+ ::Protobuf::Rpc::DynamicDiscovery::Server.new(
40
+ :uuid => "combo",
41
+ :address => '127.0.0.1',
42
+ :port => '1114',
43
+ :ttl => 10,
44
+ :services => %w[HelloService EchoService]
45
+ )
46
+ }
47
+
48
+ before(:all) do
49
+ @address = "127.0.0.1"
50
+ @port = 33333
51
+ @socket = UDPSocket.new
52
+
53
+ described_class.address = @address
54
+ described_class.port = @port
10
55
  end
11
56
 
12
- def duration
13
- start = Time.now.to_f
14
- yield
15
- Time.now.to_f - start
57
+ def expect_event_trigger(event)
58
+ ::Protobuf::Lifecycle
59
+ .should_receive(:trigger)
60
+ .with(event,
61
+ an_instance_of(::Protobuf::Rpc::ServiceDirectory::Listing))
62
+ .once
16
63
  end
17
64
 
18
- after do
19
- instance.stop
65
+ def send_beacon(type, server)
66
+ type = type.to_s.upcase
67
+ beacon = ::Protobuf::Rpc::DynamicDiscovery::Beacon.new(
68
+ :server => server,
69
+ :beacon_type => ::Protobuf::Rpc::DynamicDiscovery::BeaconType.fetch(type)
70
+ )
71
+
72
+ @socket.send(beacon.encode, 0, @address, @port)
73
+ sleep 0.01 # give the service directory time to process the beacon
20
74
  end
21
75
 
22
- it "is a singleton" do
23
- instance.should be_a_kind_of(Singleton)
76
+ it "should be a singleton" do
77
+ subject.should be_a_kind_of(Singleton)
24
78
  end
25
79
 
26
- describe "#lookup" do
27
- let(:server) { double('server', :uuid => '123',
28
- :services => ['Known::Service'],
29
- :address => "0.0.0.0",
30
- :port => 9999,
31
- :ttl => 15) }
32
- let(:listing) { ::Protobuf::Rpc::ServiceDirectory::Listing.new(server) }
80
+ it "should be configured to listen to address 127.0.0.1" do
81
+ described_class.address.should eq '127.0.0.1'
82
+ end
33
83
 
34
- before do
35
- instance.stub(:running?) { true }
36
- end
84
+ it "should be configured to listen to port 33333" do
85
+ described_class.port.should eq 33333
86
+ end
37
87
 
38
- it "returns a listing for the given service" do
39
- instance.add_listing_for(server)
40
- instance.lookup("Known::Service").should eq listing
41
- end
88
+ it "should defer .start to the instance#start" do
89
+ described_class.instance.should_receive(:start)
90
+ described_class.start
91
+ end
42
92
 
43
- it "returns random listings" do
44
- instance.add_listing_for double(:uuid => 1, :ttl => 5, :services => ["Test"])
45
- instance.add_listing_for double(:uuid => 2, :ttl => 5, :services => ["Test"])
93
+ it "should yeild itself to blocks passed to .start" do
94
+ described_class.instance.stub(:start)
95
+ expect { |b| described_class.start(&b) }.to yield_with_args(described_class)
96
+ end
46
97
 
47
- uuids = 100.times.map { instance.lookup("Test").uuid }
48
- uuids.count(1).should be_within(25).of(50)
49
- uuids.count(2).should be_within(25).of(50)
50
- end
98
+ it "should defer .stop to the instance#stop" do
99
+ described_class.instance.should_receive(:stop)
100
+ described_class.stop
101
+ end
51
102
 
52
- it "does not return expired listings" do
53
- instance.instance_variable_set(:@listings, {
54
- '1' => double(:current? => false, :services => ["Test"]),
55
- })
103
+ context "stopped" do
104
+ before { subject.stop }
56
105
 
57
- instance.lookup("Test").should be_nil
106
+ describe "#lookup" do
107
+ it "should return nil" do
108
+ send_beacon(:heartbeat, echo_server)
109
+ subject.lookup("EchoService").should be_nil
110
+ end
58
111
  end
59
- end
60
112
 
61
- describe '#add_listing_for' do
62
- let(:server) { double('server', { :uuid => '123',
63
- :services => ['Known::Service'],
64
- :address => "0.0.0.0",
65
- :port => 9999,
66
- :ttl => 15 }) }
67
-
68
- it 'adds the listing to the known @listings' do
69
- expect {
70
- ::Protobuf::Lifecycle.should_receive(:trigger)
71
- .with('directory.listing.added', an_instance_of(::Protobuf::Rpc::ServiceDirectory::Listing))
72
- .once
73
- instance.add_listing_for(server)
74
- }.to change(listings, :size).from(0).to(1)
113
+ describe "#restart" do
114
+ it "should start the service" do
115
+ subject.restart
116
+ subject.should be_running
117
+ end
75
118
  end
76
- end
77
-
78
- describe '#each_listing' do
79
- let(:listing_doubles) { { '1' => double('listing 1'),
80
- '2' => double('listing 2'),
81
- '3' => double('listing 3') } }
82
119
 
83
- before do
84
- instance.instance_variable_set(:@listings, listing_doubles)
120
+ describe "#running" do
121
+ it "should be false" do
122
+ subject.should_not be_running
123
+ end
85
124
  end
86
125
 
87
- it 'invokes the given block for each listing known by the directory' do
88
- yielded_listings = []
89
- instance.each_listing do |listing|
90
- yielded_listings << listing
126
+ describe "#stop" do
127
+ it "has no effect" do
128
+ subject.stop
91
129
  end
92
- yielded_listings.should eq(listing_doubles.values)
93
130
  end
94
131
  end
95
132
 
96
- describe "#remove_expired_listings" do
97
- let(:listing_doubles) { { '1' => double(:expired? => true),
98
- '2' => double(:expired? => true),
99
- '3' => double(:expired? => false) } }
133
+ context "started" do
134
+ before { subject.start }
135
+ after { subject.stop }
100
136
 
101
- before do
102
- instance.instance_variable_set(:@listings, listing_doubles)
103
- end
137
+ it { should be_running }
104
138
 
105
- it "removes expired listings" do
106
- expect {
107
- ::Protobuf::Lifecycle.should_receive(:trigger)
108
- .with('directory.listing.removed', an_instance_of(RSpec::Mocks::Mock))
109
- .twice
110
- instance.remove_expired_listings
111
- }.to change(listings, :size).from(3).to(1)
112
- listings.keys.should eq ['3']
139
+ it "should trigger added events" do
140
+ expect_event_trigger("directory.listing.added")
141
+ send_beacon(:heartbeat, echo_server)
113
142
  end
114
- end
115
143
 
116
- describe "#start" do
117
- it "creates a thread" do
118
- Thread.should_receive(:new)
119
- instance.start
144
+ it "should trigger updated events" do
145
+ send_beacon(:heartbeat, echo_server)
146
+ expect_event_trigger("directory.listing.updated")
147
+ send_beacon(:heartbeat, echo_server)
120
148
  end
121
149
 
122
- it "initializes the socket" do
123
- instance.should_receive :init_socket
124
- instance.start
150
+ it "should trigger removed events" do
151
+ send_beacon(:heartbeat, echo_server)
152
+ expect_event_trigger("directory.listing.removed")
153
+ send_beacon(:flatline, echo_server)
125
154
  end
126
155
 
127
- it "calls #run" do
128
- instance.should_receive(:run)
129
- instance.start
130
- sleep 0.01
131
- end
156
+ describe "#each_listing" do
157
+ it "should yield to a block for each listing" do
158
+ send_beacon(:heartbeat, hello_server)
159
+ send_beacon(:heartbeat, echo_server)
160
+ send_beacon(:heartbeat, combo_server)
132
161
 
133
- it "changes the running state" do
134
- expect {
135
- instance.start
136
- }.to change(instance, :running?).from(false).to(true)
162
+ expect { |block|
163
+ subject.each_listing(&block)
164
+ }.to yield_control.exactly(3).times
165
+ end
137
166
  end
138
- end
139
167
 
140
- describe "a running service directory" do
141
- let(:socket) { UDPSocket.new }
168
+ describe "#lookup" do
169
+ it "should provide listings by service" do
170
+ send_beacon(:heartbeat, hello_server)
171
+ subject.lookup("HelloService").to_hash.should eq hello_server.to_hash
172
+ end
173
+
174
+ it "should return random listings" do
175
+ send_beacon(:heartbeat, hello_server)
176
+ send_beacon(:heartbeat, combo_server)
177
+
178
+ uuids = 100.times.map { subject.lookup("HelloService").uuid }
179
+ uuids.count("hello").should be_within(25).of(50)
180
+ uuids.count("combo").should be_within(25).of(50)
181
+ end
182
+
183
+ it "should not return expired listings" do
184
+ send_beacon(:heartbeat, hello_server_with_short_ttl)
185
+ sleep 1
186
+ subject.lookup("HelloService").should be_nil
187
+ end
188
+
189
+ it "should not return flatlined servers" do
190
+ send_beacon(:heartbeat, echo_server)
191
+ send_beacon(:heartbeat, combo_server)
192
+ send_beacon(:flatline, echo_server)
193
+
194
+ uuids = 100.times.map { subject.lookup("EchoService").uuid }
195
+ uuids.count("combo").should eq 100
196
+ end
197
+
198
+ it "should return up-to-date listings" do
199
+ send_beacon(:heartbeat, echo_server)
200
+ echo_server.port = "7777"
201
+ send_beacon(:heartbeat, echo_server)
142
202
 
143
- def thread
144
- instance.instance_variable_get(:@thread)
203
+ subject.lookup("EchoService").port.should eq "7777"
204
+ end
145
205
  end
146
206
 
147
- before do
148
- described_class.start do |config|
149
- config.address = "127.0.0.1"
150
- config.port = 33333
207
+ describe "#restart" do
208
+ it "should clear all listings" do
209
+ send_beacon(:heartbeat, echo_server)
210
+ send_beacon(:heartbeat, combo_server)
211
+ subject.restart
212
+ subject.lookup("EchoService").should be_nil
151
213
  end
214
+ end
152
215
 
153
- socket.connect described_class.address, described_class.port
216
+ describe "#running" do
217
+ it "should be true" do
218
+ subject.should be_running
219
+ end
154
220
  end
155
221
 
156
- context "receiving a heartbeat" do
157
- let(:server) { ::Protobuf::Rpc::DynamicDiscovery::Server.new(:uuid => 'heartbeat', :address => '127.0.0.1') }
158
- let(:beacon) { ::Protobuf::Rpc::DynamicDiscovery::Beacon.new(
159
- :server => server,
160
- :beacon_type => ::Protobuf::Rpc::DynamicDiscovery::BeaconType::HEARTBEAT
161
- )}
162
- let(:payload) { beacon.encode }
222
+ describe "#stop" do
223
+ it "should clear all listings" do
224
+ send_beacon(:heartbeat, echo_server)
225
+ send_beacon(:heartbeat, combo_server)
226
+ subject.stop
227
+ subject.lookup("EchoService").should be_nil
228
+ end
163
229
 
164
- it "adds a listing" do
165
- instance.should_receive(:add_listing_for).with(server)
166
- instance.should_receive(:remove_expired_listings)
167
- socket.send(payload, 0)
168
- sleep 0.01
230
+ it "should stop the server" do
231
+ subject.stop
232
+ subject.should_not be_running
169
233
  end
170
234
  end
235
+ end
236
+
237
+ if ENV.key?("BENCH")
238
+ context "performance" do
239
+ let(:servers) {
240
+ 100.times.collect do |x|
241
+ ::Protobuf::Rpc::DynamicDiscovery::Server.new(
242
+ :uuid => server_name = "performance_server#{x + 1}",
243
+ :address => '127.0.0.1',
244
+ :port => (5555 + x).to_s,
245
+ :ttl => rand(1..5),
246
+ :services => 10.times.collect { |y| "PerformanceService#{y}" }
247
+ )
248
+ end
249
+ }
250
+
251
+ before do
252
+ require 'benchmark'
253
+ subject.start
254
+ servers.each { |server| send_beacon(:heartbeat, server) }
255
+ end
256
+
257
+ after do
258
+ subject.stop
259
+ end
171
260
 
172
- context "receiving a flatline" do
173
- let(:server) { ::Protobuf::Rpc::DynamicDiscovery::Server.new(:uuid => 'flatline', :address => '127.0.0.1') }
174
- let(:beacon) { ::Protobuf::Rpc::DynamicDiscovery::Beacon.new(
175
- :server => server,
176
- :beacon_type => ::Protobuf::Rpc::DynamicDiscovery::BeaconType::FLATLINE
177
- )}
178
- let(:payload) { beacon.encode }
179
-
180
- it "removes a listing" do
181
- instance.should_receive(:remove_listing_for).with(server)
182
- instance.should_receive(:remove_expired_listings)
183
- socket.send(payload, 0)
184
- sleep 0.01
261
+ it "should perform lookups in constant time" do
262
+ print "\n\n"
263
+ Benchmark.bm(17) do |x|
264
+ x.report(" 1_000 lookups:") { 1_000.times { subject.lookup("PerformanceService#{rand(0..9)}") } }
265
+ x.report(" 10_000 lookups:") { 10_000.times { subject.lookup("PerformanceService#{rand(0..9)}") } }
266
+ x.report("100_000 lookups:") { 100_000.times { subject.lookup("PerformanceService#{rand(0..9)}") } }
267
+ end
185
268
  end
186
269
  end
187
270
  end
@@ -67,7 +67,7 @@ describe Protobuf::Rpc::ServiceDispatcher do
67
67
 
68
68
  context 'an object that responds to to_hash but is not a hash' do
69
69
  let(:hashable) do
70
- mock('hashable', :to_hash => { :name => 'hashable' })
70
+ double('hashable', :to_hash => { :name => 'hashable' })
71
71
  end
72
72
  before { subject.callable_method.should_receive(:call) }
73
73
  before { subject.service.stub(:response).and_return(hashable) }
@@ -78,7 +78,7 @@ describe Protobuf::Rpc::ServiceDispatcher do
78
78
 
79
79
  context 'an object that responds to to_proto' do
80
80
  let(:protoable) do
81
- mock('protoable', :to_proto => Test::Resource.new(:name => 'protoable'))
81
+ double('protoable', :to_proto => Test::Resource.new(:name => 'protoable'))
82
82
  end
83
83
  before { subject.callable_method.should_receive(:call) }
84
84
  before { subject.service.stub(:response).and_return(protoable) }
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+ require 'timecop'
3
+ require 'active_support/core_ext/numeric/time'
4
+
5
+ describe ::Protobuf::Rpc::Stat do
6
+
7
+ before(:all) do
8
+ unless defined?(BarService)
9
+ class BarService < ::Struct.new(:method_name); end
10
+ end
11
+ end
12
+
13
+ describe 'server mode' do
14
+ it 'describes a server response to a client' do
15
+ ::Timecop.freeze(10.minutes.ago) do
16
+ stats = ::Protobuf::Rpc::Stat.new(:SERVER)
17
+ stats.client = 'myserver1'
18
+ stats.dispatcher = double('dispatcher', :service => BarService.new(:find_bars))
19
+ stats.request_size = 43
20
+ stats.response_size = 1302
21
+
22
+ ::Timecop.freeze(1.62.seconds.from_now) do
23
+ stats.stop
24
+ stats.to_s.should eq "[SRV] - myserver1 - #{stats.trace_id} - BarService#find_bars - 43B/1302B - 1.62s - #{::Time.now.iso8601}"
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'when request is still running' do
30
+ it 'omits response size, duration, and timestamp' do
31
+ stats = ::Protobuf::Rpc::Stat.new(:SERVER)
32
+ stats.client = 'myserver1'
33
+ stats.dispatcher = double('dispatcher', :service => BarService.new(:find_bars))
34
+ stats.request_size = 43
35
+ stats.to_s.should eq "[SRV] - myserver1 - #{stats.trace_id} - BarService#find_bars - 43B/-"
36
+ end
37
+ end
38
+ end
39
+
40
+ describe 'client mode' do
41
+ it 'describes a client request to a server' do
42
+ ::Timecop.freeze(10.minutes.ago) do
43
+ stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
44
+ stats.server = ['30000', 'myserver1.myhost.com']
45
+ stats.service = 'Foo::BarService'
46
+ stats.method_name = 'find_bars'
47
+ stats.request_size = 37
48
+ stats.response_size = 12345
49
+
50
+ ::Timecop.freeze(0.832.seconds.from_now) do
51
+ stats.stop
52
+ stats.to_s.should eq "[CLT] - myserver1.myhost.com:30000 - #{stats.trace_id} - Foo::BarService#find_bars - 37B/12345B - 0.832s - #{::Time.now.iso8601}"
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ context 'when request is still running' do
59
+ it 'omits response size, duration, and timestamp' do
60
+ stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
61
+ stats.server = ['30000', 'myserver1.myhost.com']
62
+ stats.service = 'Foo::BarService'
63
+ stats.method_name = 'find_bars'
64
+ stats.request_size = 37
65
+ stats.to_s.should eq "[CLT] - myserver1.myhost.com:30000 - #{stats.trace_id} - Foo::BarService#find_bars - 37B/-"
66
+ end
67
+ end
68
+ end
69
+
70
+ end
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  ##
4
2
  # This file is auto-generated. DO NOT EDIT!
5
3
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protobuf
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.0
4
+ version: 2.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - BJ Neilsen
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-08-21 00:00:00.000000000 Z
13
+ date: 2013-08-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -111,7 +111,7 @@ dependencies:
111
111
  - !ruby/object:Gem::Version
112
112
  version: '0'
113
113
  - !ruby/object:Gem::Dependency
114
- name: rake-compiler
114
+ name: rspec
115
115
  requirement: !ruby/object:Gem::Requirement
116
116
  requirements:
117
117
  - - '>='
@@ -125,7 +125,7 @@ dependencies:
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  - !ruby/object:Gem::Dependency
128
- name: rspec
128
+ name: simplecov
129
129
  requirement: !ruby/object:Gem::Requirement
130
130
  requirements:
131
131
  - - '>='
@@ -139,7 +139,7 @@ dependencies:
139
139
  - !ruby/object:Gem::Version
140
140
  version: '0'
141
141
  - !ruby/object:Gem::Dependency
142
- name: simplecov
142
+ name: yard
143
143
  requirement: !ruby/object:Gem::Requirement
144
144
  requirements:
145
145
  - - '>='
@@ -153,7 +153,7 @@ dependencies:
153
153
  - !ruby/object:Gem::Version
154
154
  version: '0'
155
155
  - !ruby/object:Gem::Dependency
156
- name: yard
156
+ name: timecop
157
157
  requirement: !ruby/object:Gem::Requirement
158
158
  requirements:
159
159
  - - '>='
@@ -317,6 +317,7 @@ files:
317
317
  - spec/lib/protobuf/rpc/service_dispatcher_spec.rb
318
318
  - spec/lib/protobuf/rpc/service_filters_spec.rb
319
319
  - spec/lib/protobuf/rpc/service_spec.rb
320
+ - spec/lib/protobuf/rpc/stat_spec.rb
320
321
  - spec/lib/protobuf_spec.rb
321
322
  - spec/spec_helper.rb
322
323
  - spec/support/all.rb
@@ -409,6 +410,7 @@ test_files:
409
410
  - spec/lib/protobuf/rpc/service_dispatcher_spec.rb
410
411
  - spec/lib/protobuf/rpc/service_filters_spec.rb
411
412
  - spec/lib/protobuf/rpc/service_spec.rb
413
+ - spec/lib/protobuf/rpc/stat_spec.rb
412
414
  - spec/lib/protobuf_spec.rb
413
415
  - spec/spec_helper.rb
414
416
  - spec/support/all.rb