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