bunny 1.0.0.pre1 → 1.0.0.pre2

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: 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