bunny 0.9.0.pre10 → 0.9.0.pre11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +4 -7
  4. data/ChangeLog.md +79 -0
  5. data/Gemfile +3 -1
  6. data/README.md +1 -1
  7. data/benchmarks/basic_publish/with_128K_messages.rb +35 -0
  8. data/benchmarks/basic_publish/with_1k_messages.rb +35 -0
  9. data/benchmarks/basic_publish/with_4K_messages.rb +35 -0
  10. data/benchmarks/basic_publish/with_64K_messages.rb +35 -0
  11. data/benchmarks/channel_open.rb +28 -0
  12. data/benchmarks/queue_declare.rb +29 -0
  13. data/benchmarks/queue_declare_and_bind.rb +29 -0
  14. data/benchmarks/queue_declare_bind_and_delete.rb +29 -0
  15. data/benchmarks/write_vs_write_nonblock.rb +49 -0
  16. data/bunny.gemspec +3 -3
  17. data/lib/bunny.rb +2 -0
  18. data/lib/bunny/channel.rb +31 -35
  19. data/lib/bunny/concurrent/continuation_queue.rb +10 -0
  20. data/lib/bunny/concurrent/linked_continuation_queue.rb +4 -2
  21. data/lib/bunny/exceptions.rb +5 -2
  22. data/lib/bunny/heartbeat_sender.rb +6 -4
  23. data/lib/bunny/queue.rb +3 -0
  24. data/lib/bunny/{main_loop.rb → reader_loop.rb} +5 -8
  25. data/lib/bunny/session.rb +93 -48
  26. data/lib/bunny/socket.rb +37 -3
  27. data/lib/bunny/test_kit.rb +26 -0
  28. data/lib/bunny/transport.rb +39 -33
  29. data/lib/bunny/version.rb +1 -1
  30. data/profiling/basic_publish/with_4K_messages.rb +33 -0
  31. data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +10 -11
  32. data/spec/higher_level_api/integration/queue_declare_spec.rb +55 -13
  33. data/spec/issues/issue100_spec.rb +29 -27
  34. data/spec/issues/issue78_spec.rb +54 -52
  35. data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -22
  36. data/spec/stress/concurrent_consumers_stress_spec.rb +2 -1
  37. data/spec/stress/concurrent_publishers_stress_spec.rb +7 -10
  38. data/spec/stress/long_running_consumer_spec.rb +83 -0
  39. data/spec/unit/concurrent/condition_spec.rb +7 -5
  40. data/spec/unit/concurrent/linked_continuation_queue_spec.rb +35 -0
  41. metadata +48 -44
@@ -1,73 +1,75 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Bunny::Queue, "#subscribe" do
4
- let(:connection1) do
5
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
6
- c.start
7
- c
8
- end
9
- let(:connection2) do
10
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
11
- c.start
12
- c
13
- end
3
+ unless ENV["CI"]
4
+ describe Bunny::Queue, "#subscribe" do
5
+ let(:connection1) do
6
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
7
+ c.start
8
+ c
9
+ end
10
+ let(:connection2) do
11
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
12
+ c.start
13
+ c
14
+ end
14
15
 
15
- after :all do
16
- connection1.close if connection1.open?
17
- connection2.close if connection2.open?
18
- end
16
+ after :all do
17
+ connection1.close if connection1.open?
18
+ connection2.close if connection2.open?
19
+ end
19
20
 
20
21
 
21
- context "with an empty queue" do
22
- it "consumes messages" do
23
- delivered_data = []
22
+ context "with an empty queue" do
23
+ it "consumes messages" do
24
+ delivered_data = []
24
25
 
25
- ch1 = connection1.create_channel
26
- ch2 = connection1.create_channel
26
+ ch1 = connection1.create_channel
27
+ ch2 = connection1.create_channel
27
28
 
28
- q = ch1.queue("", :exclusive => true)
29
- q.subscribe(:ack => false, :block => false) do |delivery_info, properties, payload|
30
- delivered_data << payload
31
- end
32
- sleep 0.5
29
+ q = ch1.queue("", :exclusive => true)
30
+ q.subscribe(:ack => false, :block => false) do |delivery_info, properties, payload|
31
+ delivered_data << payload
32
+ end
33
+ sleep 0.5
33
34
 
34
- x = ch2.default_exchange
35
- x.publish("abc", :routing_key => q.name)
36
- sleep 0.7
35
+ x = ch2.default_exchange
36
+ x.publish("abc", :routing_key => q.name)
37
+ sleep 0.7
37
38
 
38
- delivered_data.should == ["abc"]
39
+ delivered_data.should == ["abc"]
39
40
 
40
- ch1.close
41
- ch2.close
41
+ ch1.close
42
+ ch2.close
43
+ end
42
44
  end
43
- end
44
45
 
45
- context "with a non-empty queue" do
46
- let(:queue_name) { "queue#{rand}" }
46
+ context "with a non-empty queue" do
47
+ let(:queue_name) { "queue#{rand}" }
47
48
 
48
- it "consumes messages" do
49
- delivered_data = []
49
+ it "consumes messages" do
50
+ delivered_data = []
50
51
 
51
- ch1 = connection1.create_channel
52
- ch2 = connection1.create_channel
52
+ ch1 = connection1.create_channel
53
+ ch2 = connection1.create_channel
53
54
 
54
- q = ch1.queue(queue_name, :exclusive => true)
55
- x = ch2.default_exchange
56
- 3.times do |i|
57
- x.publish("data#{i}", :routing_key => queue_name)
58
- end
59
- sleep 0.7
60
- q.message_count.should == 3
55
+ q = ch1.queue(queue_name, :exclusive => true)
56
+ x = ch2.default_exchange
57
+ 3.times do |i|
58
+ x.publish("data#{i}", :routing_key => queue_name)
59
+ end
60
+ sleep 0.7
61
+ q.message_count.should == 3
61
62
 
62
- q.subscribe(:ack => false, :block => false) do |delivery_info, properties, payload|
63
- delivered_data << payload
64
- end
65
- sleep 0.7
63
+ q.subscribe(:ack => false, :block => false) do |delivery_info, properties, payload|
64
+ delivered_data << payload
65
+ end
66
+ sleep 0.7
66
67
 
67
- delivered_data.should == ["data0", "data1", "data2"]
68
+ delivered_data.should == ["data0", "data1", "data2"]
68
69
 
69
- ch1.close
70
- ch2.close
70
+ ch1.close
71
+ ch2.close
72
+ end
71
73
  end
72
74
  end
73
75
  end
@@ -24,27 +24,5 @@ unless ENV["CI"]
24
24
  end
25
25
  end
26
26
  end
27
-
28
- context "in a multi-threaded scenario" do
29
- # actually, on MRI values greater than ~100 will eventually cause write
30
- # operations to fail with a timeout (1 second is not enough)
31
- # which will cause recovery to re-acquire @channel_mutex in Session.
32
- # Because Ruby's mutexes are not re-entrant, it will raise a ThreadError.
33
- #
34
- # But this already demonstrates that within these platform constraints,
35
- # Bunny is safe to use in such scenarios.
36
- let(:n) { 20 }
37
-
38
- it "works correctly" do
39
- n.times do
40
- t = Thread.new do
41
- ch = connection.create_channel
42
-
43
- ch.close
44
- end
45
- t.abort_on_exception = true
46
- end
47
- end
48
- end
49
27
  end
50
28
  end
@@ -4,7 +4,8 @@ require "spec_helper"
4
4
  unless ENV["CI"]
5
5
  describe "Concurrent consumers sharing a connection" do
6
6
  let(:connection) do
7
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false)
7
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed",
8
+ :automatic_recovery => false, :continuation_timeout => 6000)
8
9
  c.start
9
10
  c
10
11
  end
@@ -4,7 +4,7 @@ require "spec_helper"
4
4
  unless ENV["CI"]
5
5
  describe "Concurrent publishers sharing a connection" do
6
6
  let(:connection) do
7
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatically_recover => false)
7
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatically_recover => false, :continuation_timeout => 20.0)
8
8
  c.start
9
9
  c
10
10
  end
@@ -13,8 +13,8 @@ unless ENV["CI"]
13
13
  connection.close
14
14
  end
15
15
 
16
- let(:n) { 32 }
17
- let(:m) { 1000 }
16
+ let(:concurrency) { 24 }
17
+ let(:rate) { 5_000 }
18
18
 
19
19
  it "successfully finish publishing" do
20
20
  ch = connection.create_channel
@@ -26,22 +26,22 @@ unless ENV["CI"]
26
26
  sleep 0.25
27
27
 
28
28
  chs = {}
29
- n.times do |i|
29
+ concurrency.times do |i|
30
30
  chs[i] = connection.create_channel
31
31
  end
32
32
 
33
33
  ts = []
34
34
 
35
- n.times do |i|
35
+ concurrency.times do |i|
36
36
  t = Thread.new do
37
37
  cht = chs[i]
38
38
  x = ch.default_exchange
39
39
 
40
40
  5.times do |i|
41
- m.times do
41
+ rate.times do
42
42
  x.publish(body, :routing_key => q.name)
43
43
  end
44
- puts "Published #{(i + 1) * m} messages..."
44
+ puts "Published #{(i + 1) * rate} messages..."
45
45
  end
46
46
  end
47
47
  t.abort_on_exception = true
@@ -52,9 +52,6 @@ unless ENV["CI"]
52
52
  ts.each do |t|
53
53
  t.join
54
54
  end
55
-
56
- sleep 4.0
57
- q.message_count.should == 5 * n * m
58
55
  end
59
56
  end
60
57
  end
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ unless ENV["CI"]
5
+ require "bunny/concurrent/condition"
6
+ require "bunny/test_kit"
7
+
8
+ describe "Long running [relatively to heartbeat interval] consumer that never publishes" do
9
+ let(:connection) do
10
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false, :heartbeat_interval => 6)
11
+ c.start
12
+ c
13
+ end
14
+
15
+ after :all do
16
+ connection.close
17
+ end
18
+
19
+ let(:target) { 512 * 1024 * 1024 }
20
+ let(:queue) { "bunny.stress.long_running_consumer.#{Time.now.to_i}" }
21
+
22
+ let(:rate) { 50 }
23
+ let(:s) { 4.0 }
24
+
25
+
26
+
27
+ it "does not skip heartbeats" do
28
+ finished = Bunny::Concurrent::Condition.new
29
+
30
+ ct = Thread.new do
31
+ t = 0
32
+ ch = connection.create_channel(nil, 6)
33
+ begin
34
+ q = ch.queue(queue, :exclusive => true)
35
+
36
+ q.bind("amq.fanout").subscribe do |_, _, payload|
37
+ t += payload.bytesize
38
+
39
+ if t >= target
40
+ puts "Hit the target, done with the test..."
41
+
42
+ finished.notify_all
43
+ else
44
+ puts "Consumed #{(t.to_f / target.to_f).round(3) * 100}% of data"
45
+ end
46
+ end
47
+ rescue Interrupt => e
48
+ ch.maybe_kill_consumer_work_pool!
49
+ ch.close
50
+ end
51
+ end
52
+ ct.abort_on_exception = true
53
+
54
+ pt = Thread.new do
55
+ t = 0
56
+ ch = connection.create_channel
57
+ begin
58
+ x = ch.fanout("amq.fanout")
59
+
60
+ loop do
61
+ break if t >= target
62
+
63
+ rate.times do |i|
64
+ msg = Bunny::TestKit.message_in_kb(96, 8192, i)
65
+ x.publish(msg)
66
+ t += msg.bytesize
67
+ end
68
+
69
+ sleep (s * rand)
70
+ end
71
+ rescue Interrupt => e
72
+ ch.close
73
+ end
74
+ end
75
+ pt.abort_on_exception = true
76
+
77
+ finished.wait
78
+
79
+ ct.raise Interrupt.new
80
+ pt.raise Interrupt.new
81
+ end
82
+ end
83
+ end
@@ -22,18 +22,19 @@ describe Bunny::Concurrent::Condition do
22
22
 
23
23
  describe "#notify" do
24
24
  it "notifies a single thread waiting on the latch" do
25
+ mutex = Mutex.new
25
26
  condition = described_class.new
26
27
  xs = []
27
28
 
28
29
  t1 = Thread.new do
29
30
  condition.wait
30
- xs << :notified1
31
+ mutex.synchronize { xs << :notified1 }
31
32
  end
32
33
  t1.abort_on_exception = true
33
34
 
34
35
  t2 = Thread.new do
35
36
  condition.wait
36
- xs << :notified2
37
+ mutex.synchronize { xs << :notified2 }
37
38
  end
38
39
  t2.abort_on_exception = true
39
40
 
@@ -45,16 +46,17 @@ describe Bunny::Concurrent::Condition do
45
46
  end
46
47
 
47
48
  describe "#notify_all" do
48
- let(:n) { 20 }
49
+ let(:n) { 120 }
49
50
 
50
51
  it "notifies all the threads waiting on the latch" do
52
+ mutex = Mutex.new
51
53
  condition = described_class.new
52
- @xs = []
54
+ @xs = []
53
55
 
54
56
  n.times do |i|
55
57
  t = Thread.new do
56
58
  condition.wait
57
- @xs << "notified#{i + 1}".to_sym
59
+ mutex.synchronize { @xs << "notified#{i + 1}".to_sym }
58
60
  end
59
61
  t.abort_on_exception = true
60
62
  end
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ if defined?(JRUBY_VERSION)
4
+ require "bunny/concurrent/linked_continuation_queue"
5
+
6
+ describe Bunny::Concurrent::LinkedContinuationQueue do
7
+ describe "#poll with a timeout that is never reached" do
8
+ it "blocks until the value is available, then returns it" do
9
+ # force subject evaluation
10
+ cq = subject
11
+ t = Thread.new do
12
+ cq.push(10)
13
+ end
14
+ t.abort_on_exception = true
15
+
16
+ v = subject.poll(500)
17
+ v.should == 10
18
+ end
19
+ end
20
+
21
+ describe "#poll with a timeout that is reached" do
22
+ it "raises an exception" do
23
+ # force subject evaluation
24
+ cq = subject
25
+ t = Thread.new do
26
+ sleep 1.5
27
+ cq.push(10)
28
+ end
29
+ t.abort_on_exception = true
30
+
31
+ lambda { subject.poll(500) }.should raise_error(::Timeout::Error)
32
+ end
33
+ end
34
+ end
35
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0.pre10
5
- prerelease: 6
4
+ version: 0.9.0.pre11
6
5
  platform: ruby
7
6
  authors:
8
7
  - Chris Duncan
@@ -10,53 +9,55 @@ authors:
10
9
  - Jakub Stastny aka botanicus
11
10
  - Michael S. Klishin
12
11
  - Stefan Kaes
13
- autorequire:
12
+ autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
- date: 2013-05-02 00:00:00.000000000 Z
15
+ date: 2013-05-14 00:00:00.000000000 Z
17
16
  dependencies:
18
17
  - !ruby/object:Gem::Dependency
19
18
  name: amq-protocol
20
- version_requirements: !ruby/object:Gem::Requirement
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: 1.4.0
25
- none: false
26
19
  requirement: !ruby/object:Gem::Requirement
27
20
  requirements:
28
- - - ">="
21
+ - - '>='
29
22
  - !ruby/object:Gem::Version
30
- version: 1.4.0
31
- none: false
32
- prerelease: false
23
+ version: 1.6.0
33
24
  type: :runtime
34
- description: Easy to use synchronous Ruby client for RabbitMQ
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - '>='
29
+ - !ruby/object:Gem::Version
30
+ version: 1.6.0
31
+ description: Popular easy to use Ruby client for RabbitMQ
35
32
  email:
36
- - !binary |-
37
- Y2VsbGRlZUBnbWFpbC5jb20=
38
- - !binary |-
39
- ZXJpY0A1c3RvcHMuY29t
40
- - !binary |-
41
- c3Rhc3RueUAxMDFpZGVhcy5jeg==
42
- - !binary |-
43
- bWljaGFlbEBub3ZlbWJlcmFpbi5jb20=
44
- - !binary |-
45
- c2thZXNAcmFpbHNleHByZXNzLmRl
33
+ - celldee@gmail.com
34
+ - eric@5stops.com
35
+ - stastny@101ideas.cz
36
+ - michael@novemberain.com
37
+ - skaes@railsexpress.de
46
38
  executables: []
47
39
  extensions: []
48
40
  extra_rdoc_files:
49
41
  - README.md
50
42
  files:
51
- - ".gitignore"
52
- - ".rspec"
53
- - ".ruby-version"
54
- - ".travis.yml"
55
- - ".yardopts"
43
+ - .gitignore
44
+ - .rspec
45
+ - .ruby-version
46
+ - .travis.yml
47
+ - .yardopts
56
48
  - ChangeLog.md
57
49
  - Gemfile
58
50
  - LICENSE
59
51
  - README.md
52
+ - benchmarks/basic_publish/with_128K_messages.rb
53
+ - benchmarks/basic_publish/with_1k_messages.rb
54
+ - benchmarks/basic_publish/with_4K_messages.rb
55
+ - benchmarks/basic_publish/with_64K_messages.rb
56
+ - benchmarks/channel_open.rb
57
+ - benchmarks/queue_declare.rb
58
+ - benchmarks/queue_declare_and_bind.rb
59
+ - benchmarks/queue_declare_bind_and_delete.rb
60
+ - benchmarks/write_vs_write_nonblock.rb
60
61
  - bin/ci/before_build.sh
61
62
  - bunny.gemspec
62
63
  - examples/connection/authentication_failure.rb
@@ -104,16 +105,18 @@ files:
104
105
  - lib/bunny/exchange.rb
105
106
  - lib/bunny/framing.rb
106
107
  - lib/bunny/heartbeat_sender.rb
107
- - lib/bunny/main_loop.rb
108
108
  - lib/bunny/message_properties.rb
109
109
  - lib/bunny/queue.rb
110
+ - lib/bunny/reader_loop.rb
110
111
  - lib/bunny/return_info.rb
111
112
  - lib/bunny/session.rb
112
113
  - lib/bunny/socket.rb
113
114
  - lib/bunny/ssl_socket.rb
114
115
  - lib/bunny/system_timer.rb
116
+ - lib/bunny/test_kit.rb
115
117
  - lib/bunny/transport.rb
116
118
  - lib/bunny/version.rb
119
+ - profiling/basic_publish/with_4K_messages.rb
117
120
  - spec/compatibility/queue_declare_spec.rb
118
121
  - spec/higher_level_api/integration/basic_ack_spec.rb
119
122
  - spec/higher_level_api/integration/basic_cancel_spec.rb
@@ -162,36 +165,35 @@ files:
162
165
  - spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb
163
166
  - spec/stress/concurrent_consumers_stress_spec.rb
164
167
  - spec/stress/concurrent_publishers_stress_spec.rb
168
+ - spec/stress/long_running_consumer_spec.rb
165
169
  - spec/unit/bunny_spec.rb
166
170
  - spec/unit/concurrent/condition_spec.rb
171
+ - spec/unit/concurrent/linked_continuation_queue_spec.rb
167
172
  - spec/unit/transport_spec.rb
168
173
  homepage: http://github.com/ruby-amqp/bunny
169
174
  licenses:
170
175
  - MIT
171
- post_install_message:
176
+ metadata: {}
177
+ post_install_message:
172
178
  rdoc_options: []
173
179
  require_paths:
174
180
  - lib
175
181
  required_ruby_version: !ruby/object:Gem::Requirement
176
182
  requirements:
177
- - - ">="
183
+ - - '>='
178
184
  - !ruby/object:Gem::Version
179
- version: !binary |-
180
- MA==
181
- none: false
185
+ version: '0'
182
186
  required_rubygems_version: !ruby/object:Gem::Requirement
183
187
  requirements:
184
- - - !binary |-
185
- Pg==
188
+ - - '>'
186
189
  - !ruby/object:Gem::Version
187
190
  version: 1.3.1
188
- none: false
189
191
  requirements: []
190
- rubyforge_project:
191
- rubygems_version: 1.8.24
192
- signing_key:
193
- specification_version: 3
194
- summary: Easy to use synchronous Ruby client for RabbitMQ
192
+ rubyforge_project:
193
+ rubygems_version: 2.0.3
194
+ signing_key:
195
+ specification_version: 4
196
+ summary: Popular easy to use Ruby client for RabbitMQ
195
197
  test_files:
196
198
  - spec/compatibility/queue_declare_spec.rb
197
199
  - spec/higher_level_api/integration/basic_ack_spec.rb
@@ -241,7 +243,9 @@ test_files:
241
243
  - spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb
242
244
  - spec/stress/concurrent_consumers_stress_spec.rb
243
245
  - spec/stress/concurrent_publishers_stress_spec.rb
246
+ - spec/stress/long_running_consumer_spec.rb
244
247
  - spec/unit/bunny_spec.rb
245
248
  - spec/unit/concurrent/condition_spec.rb
249
+ - spec/unit/concurrent/linked_continuation_queue_spec.rb
246
250
  - spec/unit/transport_spec.rb
247
251
  has_rdoc: true