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