protobuf 2.8.0 → 2.8.1

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