bunny 1.0.0.pre1 → 1.0.0.pre2

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: ae97d1a6c3f9bd4e72d67961007677f20b86636b
4
- data.tar.gz: d7fd9c501d71379469e61f68e99d0d3292bc3b32
3
+ metadata.gz: b675d7cc26c0951af3fda991f438c8bbc3cb03c5
4
+ data.tar.gz: 0cdf2879b75d769676c3dce890bd1d7213a96448
5
5
  SHA512:
6
- metadata.gz: c405d0617f646cffc2f5fb8ab9950277e1913ed131c6c7aeba95a8ac89742dfe5f56f24332753c7ed1de6ed7490affd33c619f0eaafa9dc6d76cc6c000c64233
7
- data.tar.gz: 39cd8727b32ad97113fe5f5f07197da4c3b8fa884de2a86201c00603f2a5df7037a9778653ab19b20b4972e992f296e791fb15c13ffcfe25f42eaa3e517cd837
6
+ metadata.gz: e881cb1d9695f16102ca8a08bdd9191a38cc044c16e4c1f89a32b2331d0abd6d58a00abc731e3c809d9cd538a1d243eca9029074da228ee28400c2307e7deb4f
7
+ data.tar.gz: 8390a12a2595bd1f7b65dd83c31686117081aa0672c258935e07c040ccf89222a1160e29cab0d519c24483f13adc24b3d8a5af41598b584d43c6913e1965d18e
@@ -1,3 +1,44 @@
1
+ ## Changes between Bunny 1.0.0.pre1 and 1.0.0.pre2
2
+
3
+ ### Reentrant Mutex Implementation
4
+
5
+ Bunny now allows mutex impl to be configurable, uses reentrant Monitor
6
+ by default.
7
+
8
+ Non-reentrant mutexes is a major PITA and may affect code that
9
+ uses Bunny.
10
+
11
+ Avg. publishing throughput with Monitor drops slightly from
12
+ 5.73 Khz to 5.49 Khz (about 4% decrease), which is reasonable
13
+ for Bunny.
14
+
15
+ Apps that need these 4% can configure what mutex implementation
16
+ is used on per-connection basis.
17
+
18
+ ### Eliminated Race Condition in Bunny::Session#close
19
+
20
+ `Bunny::Session#close` had a race condition that caused (non-deterministic)
21
+ exceptions when connection transport was closed before connection
22
+ reader loop was guaranteed to have stopped.
23
+
24
+ ### connection.close Raises Exceptions on Connection Thread
25
+
26
+ Connection-level exceptions (including when a connection is closed via
27
+ management UI or `rabbitmqctl`) will now be raised on the connection
28
+ thread so they
29
+
30
+ * can be handled by applications
31
+ * do not start connection recovery, which may be uncalled for
32
+
33
+ ### Client TLS Certificates are Optional
34
+
35
+ Bunny will no longer require client TLS certificates. Note that CA certificate
36
+ list is still necessary.
37
+
38
+ If RabbitMQ TLS configuration requires peer verification, client certificate
39
+ and private key are mandatory.
40
+
41
+
1
42
  ## Changes between Bunny 0.9.0 and 1.0.0.pre1
2
43
 
3
44
  ### Publishing Over Closed Connections
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "thread"
3
+ require "monitor"
3
4
  require "set"
4
5
 
5
6
  require "bunny/consumer_work_pool"
@@ -173,10 +174,10 @@ module Bunny
173
174
  @work_pool = work_pool
174
175
 
175
176
  # synchronizes frameset delivery. MK.
176
- @publishing_mutex = Mutex.new
177
- @consumer_mutex = Mutex.new
177
+ @publishing_mutex = @connection.mutex_impl.new
178
+ @consumer_mutex = @connection.mutex_impl.new
178
179
 
179
- @unconfirmed_set_mutex = Mutex.new
180
+ @unconfirmed_set_mutex = @connection.mutex_impl.new
180
181
 
181
182
  self.reset_continuations
182
183
 
@@ -1,4 +1,5 @@
1
1
  require "thread"
2
+ require "monitor"
2
3
  require "amq/int_allocator"
3
4
 
4
5
  module Bunny
@@ -18,7 +19,7 @@ module Bunny
18
19
  # @param [Integer] max_channel Max allowed channel id
19
20
  def initialize(max_channel = ((1 << 16) - 1))
20
21
  @allocator = AMQ::IntAllocator.new(1, max_channel)
21
- @mutex = Mutex.new
22
+ @mutex = Monitor.new
22
23
  end
23
24
 
24
25
 
@@ -1,4 +1,5 @@
1
1
  require "thread"
2
+ require "monitor"
2
3
 
3
4
  module Bunny
4
5
  # @private
@@ -14,7 +15,7 @@ module Bunny
14
15
 
15
16
 
16
17
  def initialize(description = nil)
17
- @mutex = Mutex.new
18
+ @mutex = Monitor.new
18
19
  @waiting_threads = []
19
20
  @description = description
20
21
  end
@@ -15,7 +15,7 @@ module Bunny
15
15
  def initialize(transport, logger)
16
16
  @transport = transport
17
17
  @logger = logger
18
- @mutex = Mutex.new
18
+ @mutex = Monitor.new
19
19
 
20
20
  @last_activity_time = Time.now
21
21
  end
@@ -51,6 +51,8 @@ module Bunny
51
51
  @session_thread.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
52
52
  end
53
53
  end
54
+
55
+ @stopped = true
54
56
  end
55
57
 
56
58
  def run_once
@@ -80,6 +82,10 @@ module Bunny
80
82
  @stopping = true
81
83
  end
82
84
 
85
+ def stopped?
86
+ @stopped
87
+ end
88
+
83
89
  def kill
84
90
  @thread.kill
85
91
  @thread.join
@@ -1,5 +1,6 @@
1
1
  require "socket"
2
2
  require "thread"
3
+ require "monitor"
3
4
 
4
5
  require "bunny/transport"
5
6
  require "bunny/channel_id_allocator"
@@ -144,11 +145,14 @@ module Bunny
144
145
  @mechanism = opts.fetch(:auth_mechanism, "PLAIN")
145
146
  @credentials_encoder = credentials_encoder_for(@mechanism)
146
147
  @locale = @opts.fetch(:locale, DEFAULT_LOCALE)
148
+
149
+ @mutex_impl = @opts.fetch(:mutex_impl, Monitor)
150
+
147
151
  # mutex for the channel id => channel hash
148
- @channel_mutex = Mutex.new
152
+ @channel_mutex = @mutex_impl.new
149
153
  # transport operations/continuations mutex. A workaround for
150
154
  # the non-reentrant Ruby mutexes. MK.
151
- @transport_mutex = Mutex.new
155
+ @transport_mutex = @mutex_impl.new
152
156
  @channels = Hash.new
153
157
 
154
158
  @origin_thread = Thread.current
@@ -186,6 +190,9 @@ module Bunny
186
190
  @threaded
187
191
  end
188
192
 
193
+ # @private
194
+ attr_reader :mutex_impl
195
+
189
196
  def configure_socket(&block)
190
197
  raise ArgumentError, "No block provided!" if block.nil?
191
198
 
@@ -259,8 +266,13 @@ module Bunny
259
266
  close_all_channels
260
267
 
261
268
  Bunny::Timer.timeout(@transport.disconnect_timeout, ClientTimeout) do
262
- self.close_connection(false)
269
+ self.close_connection(true)
263
270
  end
271
+
272
+ maybe_shutdown_reader_loop
273
+ close_transport
274
+
275
+ @status = :closed
264
276
  end
265
277
  end
266
278
  alias stop close
@@ -370,13 +382,15 @@ module Bunny
370
382
 
371
383
  # @private
372
384
  def close_connection(sync = true)
373
- @transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0))
385
+ if @transport.open?
386
+ @transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0))
374
387
 
375
- maybe_shutdown_heartbeat_sender
376
- @status = :not_connected
388
+ maybe_shutdown_heartbeat_sender
389
+ @status = :not_connected
377
390
 
378
- if sync
379
- @last_connection_close_ok = wait_on_continuations
391
+ if sync
392
+ @last_connection_close_ok = wait_on_continuations
393
+ end
380
394
  end
381
395
  end
382
396
 
@@ -392,16 +406,11 @@ module Bunny
392
406
  @last_connection_error = instantiate_connection_level_exception(method)
393
407
  @continuations.push(method)
394
408
 
395
- raise @last_connection_error
409
+ @origin_thread.raise(@last_connection_error)
396
410
  when AMQ::Protocol::Connection::CloseOk then
397
411
  @last_connection_close_ok = method
398
412
  begin
399
413
  @continuations.clear
400
-
401
- reader_loop.stop
402
- @reader_loop = nil
403
-
404
- @transport.close
405
414
  rescue StandardError => e
406
415
  @logger.error e.class.name
407
416
  @logger.error e.message
@@ -615,7 +624,27 @@ module Bunny
615
624
 
616
625
  # @private
617
626
  def maybe_shutdown_reader_loop
618
- @reader_loop.stop if @reader_loop
627
+ if @reader_loop
628
+ @reader_loop.stop
629
+ # We don't need to kill the loop but
630
+ # this is the easiest way to wait until the loop
631
+ # is guaranteed to have terminated
632
+ @reader_loop.kill
633
+ end
634
+
635
+ @reader_loop = nil
636
+ end
637
+
638
+ # @private
639
+ def close_transport
640
+ begin
641
+ @transport.close
642
+ rescue StandardError => e
643
+ @logger.error "Exception when closing transport:"
644
+ @logger.error e.class.name
645
+ @logger.error e.message
646
+ @logger.error e.backtrace
647
+ end
619
648
  end
620
649
 
621
650
  # @private
@@ -1,5 +1,6 @@
1
1
  require "socket"
2
2
  require "thread"
3
+ require "monitor"
3
4
 
4
5
  begin
5
6
  require "openssl"
@@ -49,7 +50,7 @@ module Bunny
49
50
  @connect_timeout = nil if @connect_timeout == 0
50
51
  @disconnect_timeout = @read_write_timeout || @connect_timeout
51
52
 
52
- @writes_mutex = Mutex.new
53
+ @writes_mutex = @session.mutex_impl.new
53
54
 
54
55
  maybe_initialize_socket
55
56
  prepare_tls_context if @tls_enabled
@@ -311,14 +312,24 @@ module Bunny
311
312
  end
312
313
 
313
314
  def initialize_tls_context(ctx)
314
- ctx.cert = OpenSSL::X509::Certificate.new(@tls_certificate)
315
- ctx.key = OpenSSL::PKey::RSA.new(@tls_key)
315
+ ctx.cert = OpenSSL::X509::Certificate.new(@tls_certificate) if @tls_certificate
316
+ ctx.key = OpenSSL::PKey::RSA.new(@tls_key) if @tls_key
316
317
  ctx.cert_store = if @tls_certificate_store
317
318
  @tls_certificate_store
318
319
  else
319
320
  initialize_tls_certificate_store(@tls_ca_certificates)
320
321
  end
321
322
 
323
+ if !@tls_certificate
324
+ @logger.warn <<-MSG
325
+ Using TLS but no client certificate is provided! If RabbitMQ is configured to verify peer
326
+ certificate, connection upgrade will fail!
327
+ MSG
328
+ end
329
+ if @tls_certificate && !@tls_key
330
+ @logger.warn "Using TLS but no client private key is provided!"
331
+ end
332
+
322
333
  # setting TLS/SSL version only works correctly when done
323
334
  # vis set_params. MK.
324
335
  ctx.set_params(:ssl_version => @opts.fetch(:tls_protocol, DEFAULT_TLS_PROTOCOL))
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "1.0.0.pre1"
5
+ VERSION = "1.0.0.pre2"
6
6
  end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ describe Bunny::Session do
4
+ it "can be closed" do
5
+ c = Bunny.new(:automatically_recover => false)
6
+ c.start
7
+ ch = c.create_channel
8
+
9
+ c.should be_connected
10
+ c.stop
11
+ c.should be_closed
12
+ end
13
+ end
@@ -2,7 +2,7 @@
2
2
  require "spec_helper"
3
3
 
4
4
  unless ENV["CI"]
5
- describe "TLS connection to RabbitMQ" do
5
+ describe "TLS connection to RabbitMQ with client certificates" do
6
6
  let(:connection) do
7
7
  c = Bunny.new(:user => "bunny_gem",
8
8
  :password => "bunny_password",
@@ -44,4 +44,46 @@ unless ENV["CI"]
44
44
  ch.close
45
45
  end
46
46
  end
47
+
48
+
49
+ describe "TLS connection to RabbitMQ without client certificates" do
50
+ let(:connection) do
51
+ c = Bunny.new(:user => "bunny_gem",
52
+ :password => "bunny_password",
53
+ :vhost => "bunny_testbed",
54
+ :tls => true,
55
+ :tls_ca_certificates => ["./spec/tls/cacert.pem"])
56
+ c.start
57
+ c
58
+ end
59
+
60
+ after :all do
61
+ connection.close
62
+ end
63
+
64
+ it "provides the same API as a regular connection" do
65
+ ch = connection.create_channel
66
+
67
+ q = ch.queue("", :exclusive => true)
68
+ x = ch.default_exchange
69
+
70
+ x.publish("xyzzy", :routing_key => q.name).
71
+ publish("xyzzy", :routing_key => q.name).
72
+ publish("xyzzy", :routing_key => q.name).
73
+ publish("xyzzy", :routing_key => q.name)
74
+
75
+ sleep 0.5
76
+ q.message_count.should == 4
77
+
78
+ i = 0
79
+ q.subscribe do |delivery_info, _, payload|
80
+ i += 1
81
+ end
82
+ sleep 1.0
83
+ i.should == 4
84
+ q.message_count.should == 0
85
+
86
+ ch.close
87
+ end
88
+ end
47
89
  end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ unless RUBY_ENGINE == "jruby"
4
+ describe Bunny::Session do
5
+ 4000.times do |i|
6
+ it "can be closed (take #{i})" do
7
+ c = Bunny.new(:automatically_recover => false)
8
+ c.start
9
+ ch = c.create_channel
10
+
11
+ c.should be_connected
12
+ c.stop
13
+ c.should be_closed
14
+ end
15
+ end
16
+ end
17
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre1
4
+ version: 1.0.0.pre2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Duncan
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-07-25 00:00:00.000000000 Z
15
+ date: 2013-07-26 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol
@@ -137,6 +137,7 @@ files:
137
137
  - spec/higher_level_api/integration/channel_open_spec.rb
138
138
  - spec/higher_level_api/integration/confirm_select_spec.rb
139
139
  - spec/higher_level_api/integration/connection_spec.rb
140
+ - spec/higher_level_api/integration/connection_stop_spec.rb
140
141
  - spec/higher_level_api/integration/consistent_hash_exchange_spec.rb
141
142
  - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
142
143
  - spec/higher_level_api/integration/dead_lettering_spec.rb
@@ -172,6 +173,7 @@ files:
172
173
  - spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb
173
174
  - spec/stress/concurrent_consumers_stress_spec.rb
174
175
  - spec/stress/concurrent_publishers_stress_spec.rb
176
+ - spec/stress/connection_open_close_spec.rb
175
177
  - spec/stress/long_running_consumer_spec.rb
176
178
  - spec/tls/cacert.pem
177
179
  - spec/tls/client_cert.pem
@@ -223,6 +225,7 @@ test_files:
223
225
  - spec/higher_level_api/integration/channel_open_spec.rb
224
226
  - spec/higher_level_api/integration/confirm_select_spec.rb
225
227
  - spec/higher_level_api/integration/connection_spec.rb
228
+ - spec/higher_level_api/integration/connection_stop_spec.rb
226
229
  - spec/higher_level_api/integration/consistent_hash_exchange_spec.rb
227
230
  - spec/higher_level_api/integration/consumer_cancellation_notification_spec.rb
228
231
  - spec/higher_level_api/integration/dead_lettering_spec.rb
@@ -258,6 +261,7 @@ test_files:
258
261
  - spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb
259
262
  - spec/stress/concurrent_consumers_stress_spec.rb
260
263
  - spec/stress/concurrent_publishers_stress_spec.rb
264
+ - spec/stress/connection_open_close_spec.rb
261
265
  - spec/stress/long_running_consumer_spec.rb
262
266
  - spec/tls/cacert.pem
263
267
  - spec/tls/client_cert.pem