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.
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
@@ -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
@@ -1 +1 @@
1
- 1.9.3@amqp
1
+ 2.0@amqp
@@ -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
- - 1.8.7
6
- - 1.9.2
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
@@ -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
- # declare default direct exchange which is bound to all queues
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