bunny 0.9.0.pre10 → 0.9.0.pre11
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 +7 -0
- data/.ruby-version +1 -1
- data/.travis.yml +4 -7
- data/ChangeLog.md +79 -0
- data/Gemfile +3 -1
- data/README.md +1 -1
- data/benchmarks/basic_publish/with_128K_messages.rb +35 -0
- data/benchmarks/basic_publish/with_1k_messages.rb +35 -0
- data/benchmarks/basic_publish/with_4K_messages.rb +35 -0
- data/benchmarks/basic_publish/with_64K_messages.rb +35 -0
- data/benchmarks/channel_open.rb +28 -0
- data/benchmarks/queue_declare.rb +29 -0
- data/benchmarks/queue_declare_and_bind.rb +29 -0
- data/benchmarks/queue_declare_bind_and_delete.rb +29 -0
- data/benchmarks/write_vs_write_nonblock.rb +49 -0
- data/bunny.gemspec +3 -3
- data/lib/bunny.rb +2 -0
- data/lib/bunny/channel.rb +31 -35
- data/lib/bunny/concurrent/continuation_queue.rb +10 -0
- data/lib/bunny/concurrent/linked_continuation_queue.rb +4 -2
- data/lib/bunny/exceptions.rb +5 -2
- data/lib/bunny/heartbeat_sender.rb +6 -4
- data/lib/bunny/queue.rb +3 -0
- data/lib/bunny/{main_loop.rb → reader_loop.rb} +5 -8
- data/lib/bunny/session.rb +93 -48
- data/lib/bunny/socket.rb +37 -3
- data/lib/bunny/test_kit.rb +26 -0
- data/lib/bunny/transport.rb +39 -33
- data/lib/bunny/version.rb +1 -1
- data/profiling/basic_publish/with_4K_messages.rb +33 -0
- data/spec/higher_level_api/integration/consistent_hash_exchange_spec.rb +10 -11
- data/spec/higher_level_api/integration/queue_declare_spec.rb +55 -13
- data/spec/issues/issue100_spec.rb +29 -27
- data/spec/issues/issue78_spec.rb +54 -52
- data/spec/stress/channel_open_stress_with_single_threaded_connection_spec.rb +0 -22
- data/spec/stress/concurrent_consumers_stress_spec.rb +2 -1
- data/spec/stress/concurrent_publishers_stress_spec.rb +7 -10
- data/spec/stress/long_running_consumer_spec.rb +83 -0
- data/spec/unit/concurrent/condition_spec.rb +7 -5
- data/spec/unit/concurrent/linked_continuation_queue_spec.rb +35 -0
- metadata +48 -44
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 83dfd362f0b0debb9919c5970dca6755de1d0604
|
4
|
+
data.tar.gz: cc5877189c28c76efd33bc6a04cb09801b144220
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2ea6375cc2de43483d8f50ad1fcc7395ebd54b0fd590d262bf8962a2872874bd7688790fd84d65b0a71ef027a6edc44d80193232d8b13d2c7179ffbbc3108e12
|
7
|
+
data.tar.gz: 97ec376b03d8cacfaa805c689da8ee5eb7f82bfce40a7f900fc055317fb314ca73c8e4f57c7d256f3ff95aa094bdfd700a35471ac19c7f13c0642ef386a3b071
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0@amqp
|
data/.travis.yml
CHANGED
@@ -2,13 +2,12 @@ bundler_args: --without development
|
|
2
2
|
before_script: "./bin/ci/before_build.sh"
|
3
3
|
script: "bundle exec rspec -c spec"
|
4
4
|
rvm:
|
5
|
-
-
|
6
|
-
- 1.9.
|
7
|
-
- 1.9.3
|
8
|
-
- ruby-head
|
9
|
-
- rbx-18mode
|
5
|
+
- "2.0"
|
6
|
+
- "1.9.3"
|
10
7
|
- rbx-19mode
|
11
8
|
- jruby-19mode
|
9
|
+
- "1.9.2"
|
10
|
+
- "1.8.7"
|
12
11
|
notifications:
|
13
12
|
email: michael@defprotocol.org
|
14
13
|
services:
|
@@ -16,6 +15,4 @@ services:
|
|
16
15
|
branches:
|
17
16
|
only:
|
18
17
|
- master
|
19
|
-
- 0.8.x-stable
|
20
18
|
- 0.9.x-stable
|
21
|
-
- migrate_to_amq_protocol
|
data/ChangeLog.md
CHANGED
@@ -1,7 +1,86 @@
|
|
1
|
+
## Changes between Bunny 0.9.0.pre10 and 0.9.0.pre11
|
2
|
+
|
3
|
+
### Bunny::Session#create_channel Now Accepts Consumer Work Pool Size
|
4
|
+
|
5
|
+
`Bunny::Session#create_channel` now accepts consumer work pool size as
|
6
|
+
the second argument:
|
7
|
+
|
8
|
+
``` ruby
|
9
|
+
# nil means channel id will be allocated by Bunny.
|
10
|
+
# 8 is the number of threads in the consumer work pool this channel will use.
|
11
|
+
ch = conn.create_channel(nil, 8)
|
12
|
+
```
|
13
|
+
|
14
|
+
### Heartbeat Fix For Long Running Consumers
|
15
|
+
|
16
|
+
Long running consumers that don't send any data will no longer
|
17
|
+
suffer from connections closed by RabbitMQ because of skipped
|
18
|
+
heartbeats.
|
19
|
+
|
20
|
+
Activity tracking now takes sent frames into account.
|
21
|
+
|
22
|
+
|
23
|
+
### Time-bound continuations
|
24
|
+
|
25
|
+
If a network loop exception causes "main" session thread to never
|
26
|
+
receive a response, methods such as `Bunny::Channel#queue` will simply time out
|
27
|
+
and raise Timeout::Error now, which can be handled.
|
28
|
+
|
29
|
+
It will not start automatic recovery for two reasons:
|
30
|
+
|
31
|
+
* It will be started in the network activity loop anyway
|
32
|
+
* It may do more damage than good
|
33
|
+
|
34
|
+
Kicking off network recovery manually is a matter of calling
|
35
|
+
`Bunny::Session#handle_network_failure`.
|
36
|
+
|
37
|
+
The main benefit of this implementation is that it will never
|
38
|
+
block the main app/session thread forever, and it is really
|
39
|
+
efficient on JRuby thanks to a j.u.c. blocking queue.
|
40
|
+
|
41
|
+
Fixes #112.
|
42
|
+
|
43
|
+
|
44
|
+
### Logging Support
|
45
|
+
|
46
|
+
Every Bunny connection now has a logger. By default, Bunny will use STDOUT
|
47
|
+
as logging device. This is configurable using the `:log_file` option:
|
48
|
+
|
49
|
+
``` ruby
|
50
|
+
require "bunny"
|
51
|
+
|
52
|
+
conn = Bunny.new(:log_level => :warn)
|
53
|
+
```
|
54
|
+
|
55
|
+
or the `BUNNY_LOG_LEVEL` environment variable that can take one of the following
|
56
|
+
values:
|
57
|
+
|
58
|
+
* `debug` (very verbose)
|
59
|
+
* `info`
|
60
|
+
* `warn`
|
61
|
+
* `error`
|
62
|
+
* `fatal` (least verbose)
|
63
|
+
|
64
|
+
Severity is set to `warn` by default. To disable logging completely, set the level
|
65
|
+
to `fatal`.
|
66
|
+
|
67
|
+
To redirect logging to a file or any other object that can act as an I/O entity,
|
68
|
+
pass it to the `:log_file` option.
|
69
|
+
|
70
|
+
|
1
71
|
## Changes between Bunny 0.9.0.pre9 and 0.9.0.pre10
|
2
72
|
|
3
73
|
This release contains a **breaking API change**.
|
4
74
|
|
75
|
+
### Concurrency Improvements On JRuby
|
76
|
+
|
77
|
+
On JRuby, Bunny now will use `java.util.concurrent`-backed implementations
|
78
|
+
of some of the concurrency primitives. This both improves client stability
|
79
|
+
(JDK concurrency primitives has been around for 9 years and have
|
80
|
+
well-defined, documented semantics) and opens the door to solving
|
81
|
+
some tricky failure handling problems in the future.
|
82
|
+
|
83
|
+
|
5
84
|
### Explicitly Closed Sockets
|
6
85
|
|
7
86
|
Bunny now will correctly close the socket previous connection had
|
data/Gemfile
CHANGED
@@ -26,7 +26,9 @@ gem "effin_utf8"
|
|
26
26
|
|
27
27
|
group :development do
|
28
28
|
gem "yard"
|
29
|
+
|
29
30
|
gem "redcarpet", :platform => :mri
|
31
|
+
gem "ruby-prof", :platform => :mri
|
30
32
|
end
|
31
33
|
|
32
34
|
group :test do
|
@@ -40,7 +42,7 @@ gemspec
|
|
40
42
|
def custom_gem(name, options = Hash.new)
|
41
43
|
local_path = File.expand_path("../vendor/#{name}", __FILE__)
|
42
44
|
if File.exist?(local_path)
|
43
|
-
puts "Using #{name} from #{local_path}..."
|
45
|
+
# puts "Using #{name} from #{local_path}..."
|
44
46
|
gem name, options.merge(:path => local_path).delete_if { |key, _| [:git, :branch].include?(key) }
|
45
47
|
else
|
46
48
|
gem name, options
|
data/README.md
CHANGED
@@ -62,7 +62,7 @@ ch = conn.create_channel
|
|
62
62
|
# declare a queue
|
63
63
|
q = ch.queue("test1")
|
64
64
|
|
65
|
-
#
|
65
|
+
# default direct exchange is automatically bound to all queues
|
66
66
|
e = ch.default_exchange
|
67
67
|
|
68
68
|
# publish a message to the exchange which then gets routed to the queue
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
|
11
|
+
puts
|
12
|
+
puts "-" * 80
|
13
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
14
|
+
|
15
|
+
n = 50_000
|
16
|
+
ch = conn.create_channel
|
17
|
+
x = ch.default_exchange
|
18
|
+
s = "z" * (1024 * 128)
|
19
|
+
|
20
|
+
# warm up the JIT, etc
|
21
|
+
puts "Doing a warmup run..."
|
22
|
+
16000.times { x.publish(s, :routing_key => "anything") }
|
23
|
+
|
24
|
+
# give OS, the server and so on some time to catch
|
25
|
+
# up
|
26
|
+
sleep 2.0
|
27
|
+
|
28
|
+
t = Benchmark.realtime do
|
29
|
+
n.times { x.publish(s, :routing_key => "anything") }
|
30
|
+
end
|
31
|
+
r = (n.to_f/t.to_f)
|
32
|
+
|
33
|
+
puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz"
|
34
|
+
puts
|
35
|
+
puts "-" * 80
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
|
11
|
+
puts
|
12
|
+
puts "-" * 80
|
13
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
14
|
+
|
15
|
+
n = 50_000
|
16
|
+
ch = conn.create_channel
|
17
|
+
x = ch.default_exchange
|
18
|
+
s = "z" * 1024
|
19
|
+
|
20
|
+
# warm up the JIT, etc
|
21
|
+
puts "Doing a warmup run..."
|
22
|
+
16000.times { x.publish(s, :routing_key => "anything") }
|
23
|
+
|
24
|
+
# give OS, the server and so on some time to catch
|
25
|
+
# up
|
26
|
+
sleep 2.0
|
27
|
+
|
28
|
+
t = Benchmark.realtime do
|
29
|
+
n.times { x.publish(s, :routing_key => "anything") }
|
30
|
+
end
|
31
|
+
r = (n.to_f/t.to_f)
|
32
|
+
|
33
|
+
puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz"
|
34
|
+
puts
|
35
|
+
puts "-" * 80
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
|
11
|
+
puts
|
12
|
+
puts "-" * 80
|
13
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
14
|
+
|
15
|
+
n = 50_000
|
16
|
+
ch = conn.create_channel
|
17
|
+
x = ch.default_exchange
|
18
|
+
s = "z" * 4096
|
19
|
+
|
20
|
+
# warm up the JIT, etc
|
21
|
+
puts "Doing a warmup run..."
|
22
|
+
16000.times { x.publish(s, :routing_key => "anything") }
|
23
|
+
|
24
|
+
# give OS, the server and so on some time to catch
|
25
|
+
# up
|
26
|
+
sleep 2.0
|
27
|
+
|
28
|
+
t = Benchmark.realtime do
|
29
|
+
n.times { x.publish(s, :routing_key => "anything") }
|
30
|
+
end
|
31
|
+
r = (n.to_f/t.to_f)
|
32
|
+
|
33
|
+
puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz"
|
34
|
+
puts
|
35
|
+
puts "-" * 80
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
|
11
|
+
puts
|
12
|
+
puts "-" * 80
|
13
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
14
|
+
|
15
|
+
n = 50_000
|
16
|
+
ch = conn.create_channel
|
17
|
+
x = ch.default_exchange
|
18
|
+
s = "z" * (1024 * 64)
|
19
|
+
|
20
|
+
# warm up the JIT, etc
|
21
|
+
puts "Doing a warmup run..."
|
22
|
+
16000.times { x.publish(s, :routing_key => "anything") }
|
23
|
+
|
24
|
+
# give OS, the server and so on some time to catch
|
25
|
+
# up
|
26
|
+
sleep 2.0
|
27
|
+
|
28
|
+
t = Benchmark.realtime do
|
29
|
+
n.times { x.publish(s, :routing_key => "anything") }
|
30
|
+
end
|
31
|
+
r = (n.to_f/t.to_f)
|
32
|
+
|
33
|
+
puts "Publishing rate with #{s.bytesize} bytes/msg: #{(r / 1000).round(2)} KGHz"
|
34
|
+
puts
|
35
|
+
puts "-" * 80
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
|
11
|
+
puts
|
12
|
+
puts "-" * 80
|
13
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
14
|
+
|
15
|
+
n = 500
|
16
|
+
|
17
|
+
# warm up the JIT, etc
|
18
|
+
puts "Doing a warmup run..."
|
19
|
+
1000.times { conn.create_channel }
|
20
|
+
|
21
|
+
t = Benchmark.realtime do
|
22
|
+
n.times { conn.create_channel }
|
23
|
+
end
|
24
|
+
r = (n.to_f/t.to_f)
|
25
|
+
|
26
|
+
puts "channel.open rate: #{(r / 1000).round(2)} KGHz"
|
27
|
+
puts
|
28
|
+
puts "-" * 80
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
ch = conn.create_channel
|
11
|
+
|
12
|
+
puts
|
13
|
+
puts "-" * 80
|
14
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
15
|
+
|
16
|
+
n = 4000
|
17
|
+
|
18
|
+
# warm up the JIT, etc
|
19
|
+
puts "Doing a warmup run..."
|
20
|
+
n.times { ch.queue("", :exclusive => true) }
|
21
|
+
|
22
|
+
t = Benchmark.realtime do
|
23
|
+
n.times { ch.queue("", :exclusive => true) }
|
24
|
+
end
|
25
|
+
r = (n.to_f/t.to_f)
|
26
|
+
|
27
|
+
puts "queue.declare (server-named, exclusive = true) rate: #{(r / 1000).round(2)} KGHz"
|
28
|
+
puts
|
29
|
+
puts "-" * 80
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
ch = conn.create_channel
|
11
|
+
|
12
|
+
puts
|
13
|
+
puts "-" * 80
|
14
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
15
|
+
|
16
|
+
n = 4000
|
17
|
+
|
18
|
+
# warm up the JIT, etc
|
19
|
+
puts "Doing a warmup run..."
|
20
|
+
n.times { ch.queue("", :exclusive => true).bind("amq.fanout") }
|
21
|
+
|
22
|
+
t = Benchmark.realtime do
|
23
|
+
n.times { ch.queue("", :exclusive => true).bind("amq.fanout") }
|
24
|
+
end
|
25
|
+
r = (n.to_f/t.to_f)
|
26
|
+
|
27
|
+
puts "queue.declare + queue.bind rate: #{(r / 1000).round(2)} KGHz"
|
28
|
+
puts
|
29
|
+
puts "-" * 80
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "bunny"
|
6
|
+
require "benchmark"
|
7
|
+
|
8
|
+
conn = Bunny.new
|
9
|
+
conn.start
|
10
|
+
ch = conn.create_channel
|
11
|
+
|
12
|
+
puts
|
13
|
+
puts "-" * 80
|
14
|
+
puts "Benchmarking on #{RUBY_DESCRIPTION}"
|
15
|
+
|
16
|
+
n = 4000
|
17
|
+
|
18
|
+
# warm up the JIT, etc
|
19
|
+
puts "Doing a warmup run..."
|
20
|
+
n.times { ch.queue("", :exclusive => true).bind("amq.fanout").delete }
|
21
|
+
|
22
|
+
t = Benchmark.realtime do
|
23
|
+
n.times { ch.queue("", :exclusive => true).bind("amq.fanout").delete }
|
24
|
+
end
|
25
|
+
r = (n.to_f/t.to_f)
|
26
|
+
|
27
|
+
puts "queue.declare + queue.bind + queue.delete rate: #{(r / 1000).round(2)} KGHz"
|
28
|
+
puts
|
29
|
+
puts "-" * 80
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "benchmark"
|
4
|
+
|
5
|
+
# This tests demonstrates throughput difference of
|
6
|
+
# IO#write and IO#write_nonblock. Note that the two
|
7
|
+
# may not be equivalent depending on your
|
8
|
+
|
9
|
+
r, w = IO.pipe
|
10
|
+
|
11
|
+
# buffer size
|
12
|
+
b = 65536
|
13
|
+
|
14
|
+
read_loop = Thread.new do
|
15
|
+
loop do
|
16
|
+
begin
|
17
|
+
r.read_nonblock(b)
|
18
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN => e
|
19
|
+
IO.select([r])
|
20
|
+
retry
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
n = 10_000
|
26
|
+
# 7 KB
|
27
|
+
s = "a" * (7 * 1024)
|
28
|
+
Benchmark.bm do |meter|
|
29
|
+
meter.report("write:") do
|
30
|
+
n.times { w.write(s.dup) }
|
31
|
+
end
|
32
|
+
meter.report("write + flush:") do
|
33
|
+
n.times { w.write(s.dup); w.flush }
|
34
|
+
end
|
35
|
+
meter.report("write_nonblock:") do
|
36
|
+
n.times do
|
37
|
+
s2 = s.dup
|
38
|
+
begin
|
39
|
+
while !s2.empty?
|
40
|
+
written = w.write_nonblock(s2)
|
41
|
+
s2.slice!(0, written)
|
42
|
+
end
|
43
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
44
|
+
IO.select([], [w])
|
45
|
+
retry
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|