amqp 0.8.0.rc13 → 0.8.0.rc14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/.rspec +2 -1
  2. data/.travis.yml +8 -2
  3. data/.yardopts +1 -0
  4. data/CHANGELOG +9 -0
  5. data/Gemfile +17 -11
  6. data/README.md +26 -16
  7. data/amqp.gemspec +2 -2
  8. data/bin/ci/before_build.sh +21 -0
  9. data/docs/08Migration.textile +199 -5
  10. data/docs/AMQP091ModelExplained.textile +322 -0
  11. data/docs/Bindings.textile +24 -4
  12. data/docs/Clustering.textile +1 -1
  13. data/docs/ConnectingToTheBroker.textile +98 -82
  14. data/docs/ConnectionEncryptionWithTLS.textile +65 -5
  15. data/docs/DocumentationGuidesIndex.textile +93 -13
  16. data/docs/Durability.textile +1 -1
  17. data/docs/ErrorHandling.textile +458 -94
  18. data/docs/Exchanges.textile +901 -87
  19. data/docs/GettingStarted.textile +278 -143
  20. data/docs/PatternsAndUseCases.textile +420 -0
  21. data/docs/Queues.textile +730 -178
  22. data/docs/RabbitMQVersions.textile +18 -3
  23. data/docs/RunningTests.textile +1 -1
  24. data/docs/TestingWithEventedSpec.textile +121 -0
  25. data/docs/Troubleshooting.textile +15 -1
  26. data/docs/VendorSpecificExtensions.textile +1 -1
  27. data/docs/diagrams/001_hello_world_example_routing.png +0 -0
  28. data/docs/diagrams/002_blabbr_example_routing.png +0 -0
  29. data/docs/diagrams/003_weathr_example_routing.png +0 -0
  30. data/docs/diagrams/004_fanout_exchange.png +0 -0
  31. data/docs/diagrams/005_direct_exchange.png +0 -0
  32. data/docs/diagrams/redhat/direct_exchange.png +0 -0
  33. data/docs/diagrams/redhat/fanout_exchange.png +0 -0
  34. data/docs/diagrams/redhat/topic_exchange.png +0 -0
  35. data/examples/error_handling/automatic_recovery_of_channel_and_queues.rb +50 -0
  36. data/examples/error_handling/automatically_recovering_hello_world_consumer.rb +51 -0
  37. data/examples/error_handling/automatically_recovering_hello_world_consumer_that_uses_a_server_named_queue.rb +51 -0
  38. data/examples/error_handling/basic_connection_failover.rb +22 -0
  39. data/examples/error_handling/channel_level_exception.rb +9 -2
  40. data/examples/error_handling/connection_level_exception.rb +8 -1
  41. data/examples/error_handling/connection_level_exception_with_objects.rb +49 -0
  42. data/examples/error_handling/connection_loss_handler.rb +1 -5
  43. data/examples/error_handling/hello_world_producer.rb +43 -0
  44. data/examples/error_handling/insufficient_permissions.rb +54 -0
  45. data/examples/error_handling/manual_connection_and_channel_recovery.rb +71 -0
  46. data/examples/error_handling/queue_exclusivity_violation.rb +41 -0
  47. data/examples/error_handling/queue_name_violation.rb +31 -0
  48. data/examples/exchanges/autodeletion_of_exchanges.rb +1 -4
  49. data/examples/guides/queues/01a_declaring_a_server_named_queue_using_queue_constructor.rb +7 -8
  50. data/examples/guides/queues/01b_declaring_a_queue_using_queue_constructor.rb +7 -8
  51. data/examples/guides/queues/02a_declaring_a_durable_shared_queue.rb +5 -8
  52. data/examples/guides/queues/02b_declaring_a_durable_shared_queue.rb +5 -8
  53. data/examples/guides/queues/03a_declaring_a_temporary_exclusive_queue.rb +7 -8
  54. data/examples/guides/queues/04_bind_a_queue_using_exchange_instance.rb +9 -10
  55. data/examples/guides/queues/05_bind_a_queue_using_exchange_name.rb +8 -10
  56. data/examples/guides/queues/06_subscribe_to_receive_messages.rb +10 -12
  57. data/examples/guides/queues/07_fetch_a_message_from_the_queue.rb +14 -14
  58. data/examples/guides/queues/08_unsubscribing_a_consumer.rb +13 -16
  59. data/examples/guides/queues/09_unbinding_from_exchange.rb +16 -22
  60. data/examples/guides/queues/10_purge_a_queue.rb +13 -18
  61. data/examples/guides/queues/11_deleting_a_queue.rb +14 -19
  62. data/examples/guides/queues/12_objects_that_consume_messages.rb +69 -0
  63. data/examples/guides/queues/13_objects_that_consume_messages_take_two.rb +89 -0
  64. data/examples/hello_world.rb +1 -3
  65. data/examples/hello_world_with_an_empty_string.rb +5 -6
  66. data/examples/inspecting_server_information.rb +45 -0
  67. data/examples/issues/issue_93.rb +23 -0
  68. data/examples/issues/issue_94.rb +23 -0
  69. data/examples/patterns/command/consumer.rb +45 -0
  70. data/examples/patterns/command/producer.rb +26 -0
  71. data/examples/patterns/request_reply/client.rb +29 -0
  72. data/examples/patterns/request_reply/server.rb +26 -0
  73. data/examples/publishing/publishing_a_one_off_message.rb +6 -4
  74. data/examples/publishing/returned_messages.rb +2 -10
  75. data/examples/queues/accessing_message_metadata.rb +15 -13
  76. data/examples/queues/queue_status.rb +12 -15
  77. data/examples/routing/fanout_routing.rb +33 -0
  78. data/examples/routing/headers_routing.rb +17 -15
  79. data/examples/routing/round_robin_with_direct_exchange.rb +39 -0
  80. data/examples/routing/round_robin_with_the_default_exchange.rb +38 -0
  81. data/examples/routing/unroutable_mandatory_message_is_returned.rb +33 -0
  82. data/examples/routing/weather_updates.rb +15 -20
  83. data/examples/tls/using_tls.rb +41 -0
  84. data/lib/amqp/bit_set.rb +80 -0
  85. data/lib/amqp/broker.rb +72 -0
  86. data/lib/amqp/channel.rb +93 -13
  87. data/lib/amqp/client.rb +11 -22
  88. data/lib/amqp/compatibility/ruby187_patchlevel_check.rb +2 -0
  89. data/lib/amqp/connection.rb +2 -3
  90. data/lib/amqp/consumer.rb +208 -0
  91. data/lib/amqp/deprecated/fork.rb +2 -0
  92. data/lib/amqp/deprecated/mq.rb +2 -0
  93. data/lib/amqp/exchange.rb +6 -4
  94. data/lib/amqp/extensions/rabbitmq.rb +3 -1
  95. data/lib/amqp/header.rb +76 -14
  96. data/lib/amqp/int_allocator.rb +96 -0
  97. data/lib/amqp/logger.rb +2 -0
  98. data/lib/amqp/queue.rb +242 -86
  99. data/lib/amqp/rpc.rb +2 -0
  100. data/lib/amqp/session.rb +169 -9
  101. data/lib/amqp/utilities/event_loop_helper.rb +2 -0
  102. data/lib/amqp/utilities/server_type.rb +2 -0
  103. data/lib/amqp/version.rb +2 -2
  104. data/lib/mq.rb +4 -2
  105. data/lib/mq/logger.rb +3 -1
  106. data/lib/mq/rpc.rb +3 -1
  107. data/spec/integration/authentication_spec.rb +17 -10
  108. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +1 -1
  109. data/spec/integration/automatic_recovery_predicate_spec.rb +68 -0
  110. data/spec/integration/basic_get_spec.rb +2 -1
  111. data/spec/integration/{extensions/basic_return_spec.rb → basic_return_spec.rb} +2 -1
  112. data/spec/integration/channel_level_exception_handling_spec.rb +53 -0
  113. data/spec/integration/connection_level_exception_handling_spec.rb +49 -0
  114. data/spec/integration/declare_and_immediately_bind_a_server_named_queue_spec.rb +38 -17
  115. data/spec/integration/declare_one_hundred_server_named_queues_spec.rb +44 -0
  116. data/spec/integration/direct_exchange_routing_spec.rb +125 -0
  117. data/spec/integration/exchange_declaration_spec.rb +75 -46
  118. data/spec/integration/extensions/rabbitmq/publisher_confirmations_spec.rb +180 -0
  119. data/spec/integration/{workload_distribution_spec.rb → fanout_exchange_routing_spec.rb} +10 -9
  120. data/spec/integration/headers_exchange_routing_spec.rb +269 -0
  121. data/spec/integration/hello_world_spec.rb +77 -0
  122. data/spec/integration/immediate_messages_spec.rb +59 -0
  123. data/spec/integration/mandatory_messages_spec.rb +52 -0
  124. data/spec/integration/message_metadata_access_spec.rb +106 -0
  125. data/spec/integration/multiple_consumers_per_queue_spec.rb +319 -0
  126. data/spec/integration/ordering_of_published_messages_spec.rb +96 -0
  127. data/spec/integration/queue_declaration_spec.rb +8 -8
  128. data/spec/integration/queue_status_spec.rb +66 -0
  129. data/spec/integration/recovery/per_channel_automatic_recovery_on_graceful_broker_shutdown_spec.rb +76 -0
  130. data/spec/integration/recovery/per_channel_automatic_recovery_spec.rb +72 -0
  131. data/spec/integration/redelivery_of_unacknowledged_messages_spec.rb +96 -0
  132. data/spec/integration/regressions/concurrent_publishing_on_the_same_channel_spec.rb +91 -0
  133. data/spec/integration/regressions/empty_message_body_spec.rb +56 -0
  134. data/spec/integration/regressions/issue66_spec.rb +2 -1
  135. data/spec/integration/reply_queue_communication_spec.rb +2 -1
  136. data/spec/integration/store_and_forward_spec.rb +4 -3
  137. data/spec/integration/topic_subscription_spec.rb +2 -1
  138. data/spec/integration/tx_commit_spec.rb +124 -0
  139. data/spec/integration/tx_rollback_spec.rb +167 -0
  140. data/spec/spec_helper.rb +44 -71
  141. data/spec/unit/amqp/bit_set_spec.rb +127 -0
  142. data/spec/unit/amqp/channel_id_allocation_spec.rb +40 -0
  143. data/spec/unit/amqp/connection_spec.rb +4 -2
  144. data/spec/unit/amqp/int_allocator_spec.rb +116 -0
  145. metadata +92 -26
  146. data/CONTRIBUTORS +0 -29
  147. data/docs/Routing.textile +0 -30
  148. data/examples/real-world/task-queue/README.textile +0 -3
  149. data/examples/real-world/task-queue/consumer.rb +0 -27
  150. data/examples/real-world/task-queue/producer.rb +0 -22
  151. data/spec/unit/amqp/basic_spec.rb +0 -39
  152. data/tasks.rb +0 -4
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "100 AMQP messages" do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include EventedSpec::AMQPSpec
12
+ default_options AMQP_OPTS
13
+ default_timeout 5
14
+
15
+
16
+ before :all do
17
+ @list = Range.new(0, 100, true).to_a
18
+ end
19
+
20
+
21
+ context "published and received on the same channel" do
22
+ amqp_before do
23
+ @channel = AMQP::Channel.new
24
+ @channel.on_error do |ch, close|
25
+ raise "Channel-level error!: #{close.inspect}"
26
+ end
27
+
28
+ @queue = @channel.queue("amqpgem.tests.integration.queue1", :auto_delete => true)
29
+ end
30
+
31
+ #
32
+ # Examples
33
+ #
34
+
35
+ it "are received on the same channel in the order of publishing" do
36
+ received = []
37
+
38
+ @queue.subscribe do |metadata, payload|
39
+ received << payload.to_i
40
+ end
41
+
42
+ EventMachine.add_timer(0.3) do
43
+ @list.each { |i| @channel.default_exchange.publish(i.to_s, :routing_key => @queue.name) }
44
+ end
45
+
46
+ done(3.5) {
47
+ received.size.should == 100
48
+ received.first.should == 0
49
+ received.last.should == 99
50
+
51
+ received.should == @list
52
+ }
53
+ end
54
+ end
55
+
56
+
57
+ context "published on two different channels" do
58
+ amqp_before do
59
+ @channel1 = AMQP::Channel.new
60
+ @channel2 = AMQP::Channel.new
61
+
62
+ @channel1.on_error do |ch, close|
63
+ raise "Channel-level error!: #{close.inspect}"
64
+ end
65
+ @channel2.on_error do |ch, close|
66
+ raise "Channel-level error!: #{close.inspect}"
67
+ end
68
+
69
+ @queue = @channel1.queue("amqpgem.tests.integration.queue1", :auto_delete => true)
70
+ end
71
+
72
+ #
73
+ # Examples
74
+ #
75
+
76
+ it "are received on the same channel in the order of publishing" do
77
+ received = []
78
+
79
+ @queue.subscribe do |metadata, payload|
80
+ received << payload.to_i
81
+ end
82
+
83
+ EventMachine.add_timer(0.3) do
84
+ @list.each { |i| @channel2.default_exchange.publish(i.to_s, :routing_key => @queue.name) }
85
+ end
86
+
87
+ done(3.5) {
88
+ received.size.should == 100
89
+ received.first.should == 0
90
+ received.last.should == 99
91
+
92
+ received.should == @list
93
+ }
94
+ end
95
+ end
96
+ end # describe
@@ -18,7 +18,7 @@ describe AMQP do
18
18
 
19
19
  after(:all) do
20
20
  AMQP.cleanup_state
21
- done(0.3)
21
+ done
22
22
  end
23
23
 
24
24
  #
@@ -32,13 +32,13 @@ describe AMQP do
32
32
  it "declares a new queue with that name" do
33
33
  queue = @channel.queue(name)
34
34
  queue.name.should == name
35
- done(0.3)
35
+ done
36
36
  end
37
37
 
38
38
  it "caches that queue" do
39
39
  queue = @channel.queue(name)
40
40
  @channel.queue(name).object_id.should == queue.object_id
41
- done(0.3)
41
+ done
42
42
  end
43
43
  end # context
44
44
 
@@ -48,7 +48,7 @@ describe AMQP do
48
48
  @channel.queue("") do |queue, *args|
49
49
  queue.name.should_not be_empty
50
50
  queue.delete
51
- done(0.3)
51
+ done
52
52
  end
53
53
  end
54
54
  end
@@ -93,7 +93,7 @@ describe AMQP do
93
93
  @channel.queue(name, different_options)
94
94
  }.to raise_error(AMQP::IncompatibleOptionsError)
95
95
  @queue.delete
96
- done(0.3)
96
+ done
97
97
  end
98
98
  end
99
99
  end
@@ -108,7 +108,7 @@ describe AMQP do
108
108
 
109
109
  queue.should == original_queue
110
110
 
111
- done(0.3)
111
+ done
112
112
  end # it
113
113
  end
114
114
 
@@ -120,7 +120,7 @@ describe AMQP do
120
120
  exchange = @channel.queue("queue declared at #{Time.now.to_i}", :passive => true)
121
121
  }.to raise_error
122
122
 
123
- done(0.3)
123
+ done
124
124
  end # it
125
125
  end # context
126
126
  end # context
@@ -136,7 +136,7 @@ describe AMQP do
136
136
  @channel.queue("previously.declared.durable.queue", :durable => false)
137
137
  }.to raise_error(AMQP::IncompatibleOptionsError)
138
138
 
139
- done(0.3)
139
+ done
140
140
  end # it
141
141
  end # context
142
142
  end # describe
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe AMQP::Queue do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include EventedSpec::AMQPSpec
12
+
13
+ default_timeout 5
14
+
15
+
16
+
17
+ #
18
+ # Examples
19
+ #
20
+
21
+ describe "#status" do
22
+ it "yields # of messages & consumers to the callback" do
23
+ events = []
24
+ channel = AMQP::Channel.new
25
+
26
+ queue1 = channel.queue("", :auto_delete => true)
27
+ queue2 = channel.queue("amqpgem.tests.a.named.queue", :auto_delete => true)
28
+
29
+ EventMachine.add_timer(1.0) do
30
+ queue1.status do |m, c|
31
+ events << :queue1_declare_ok
32
+ end
33
+ queue2.status do |m, c|
34
+ events << :queue2_declare_ok
35
+ end
36
+ end
37
+
38
+ done(2.0) {
39
+ events.should include(:queue1_declare_ok)
40
+ events.should include(:queue2_declare_ok)
41
+ }
42
+ end
43
+
44
+
45
+ it "yields # of messages & consumers to the callback in pseudo-synchronous code" do
46
+ events = []
47
+ channel = AMQP::Channel.new
48
+
49
+ queue1 = channel.queue("", :auto_delete => true)
50
+ queue2 = channel.queue("amqpgem.tests.a.named.queue", :auto_delete => true)
51
+
52
+ queue1.status do |m, c|
53
+ events << :queue1_declare_ok
54
+ end
55
+ queue2.status do |m, c|
56
+ events << :queue2_declare_ok
57
+ end
58
+
59
+ done(2.0) {
60
+ queue1.name.should =~ /^amq\..+/
61
+ events.should include(:queue1_declare_ok)
62
+ events.should include(:queue2_declare_ok)
63
+ }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ unless ENV["CI"]
5
+ describe "Per-channel automatic channel recovery" do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include RabbitMQ::Control
12
+
13
+ include EventedSpec::AMQPSpec
14
+ default_timeout 20.0
15
+
16
+ amqp_before do
17
+ @channel = AMQP::Channel.new(AMQP.connection, :auto_recovery => true)
18
+ end
19
+
20
+
21
+ after :all do
22
+ start_rabbitmq unless rabbitmq_pid
23
+ end
24
+
25
+ # ...
26
+
27
+
28
+ #
29
+ # Examples
30
+ #
31
+
32
+ it "can be used when broker is shut down gracefully" do
33
+ AMQP.connection.on_error do |conn, connection_close|
34
+ puts "[connection.close] Reply code = #{connection_close.reply_code}, reply text = #{connection_close.reply_text}"
35
+ if connection_close.reply_code == 320
36
+ puts "[connection.close] Setting up a periodic reconnection timer..."
37
+ conn.periodically_reconnect(1)
38
+ end
39
+ end
40
+
41
+ pid = rabbitmq_pid
42
+ puts "rabbitmq pid = #{pid.inspect}"
43
+
44
+ stop_rabbitmq
45
+ rabbitmq_pid.should be_nil
46
+
47
+ # 2 seconds later, check that we are reconnecting
48
+ EventMachine.add_timer(2.0) do
49
+ AMQP.connection.should_not be_connected
50
+ AMQP.connection.should be_reconnecting
51
+ end
52
+
53
+
54
+ # 4 seconds later, start RabbitMQ
55
+ EventMachine.add_timer(4.0) do
56
+ start_rabbitmq
57
+ rabbitmq_pid.should_not be_nil
58
+ end
59
+
60
+ # 10 seconds later, use the (now recovered) connection. Note that depending
61
+ # on # of plugins used it may take 5-10 seconds to start up RabbitMQ and initialize it,
62
+ # then open a new AMQP connection. That's why we wait. MK.
63
+ EventMachine.add_timer(10.0) do
64
+ AMQP.connection.should be_connected
65
+ AMQP.connection.should_not be_reconnecting
66
+
67
+ @channel.queue("amqpgem.tests.a.queue", :auto_delete => true).subscribe do |metadata, payload|
68
+ puts "Got a message: #{payload.inspect}"
69
+ done
70
+ end
71
+
72
+ EventMachine.add_timer(1.5) { @channel.default_exchange.publish("", :routing_key => "amqpgem.tests.a.queue") }
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ unless ENV["CI"]
5
+ describe "Per-channel automatic channel recovery" do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include RabbitMQ::Control
12
+
13
+ include EventedSpec::AMQPSpec
14
+ default_timeout 20.0
15
+
16
+ amqp_before do
17
+ @channel = AMQP::Channel.new(AMQP.connection, :auto_recovery => true)
18
+ end
19
+
20
+ after :all do
21
+ start_rabbitmq unless rabbitmq_pid
22
+ end
23
+
24
+ # ...
25
+
26
+
27
+ #
28
+ # Examples
29
+ #
30
+
31
+ it "kicks in when broker goes down" do
32
+ AMQP.connection.on_tcp_connection_loss do |conn, settings|
33
+ puts "[network failure] Trying to reconnect..."
34
+ conn.reconnect(false, 1)
35
+ end
36
+
37
+ pid = rabbitmq_pid
38
+ puts "rabbitmq pid = #{pid.inspect}"
39
+
40
+ kill_rabbitmq
41
+ rabbitmq_pid.should be_nil
42
+
43
+ # 2 seconds later, check that we are reconnecting
44
+ EventMachine.add_timer(2.0) do
45
+ AMQP.connection.should_not be_connected
46
+ AMQP.connection.should be_reconnecting
47
+ end
48
+
49
+
50
+ # 4 seconds later, start RabbitMQ
51
+ EventMachine.add_timer(4.0) do
52
+ start_rabbitmq
53
+ rabbitmq_pid.should_not be_nil
54
+ end
55
+
56
+ # 12 seconds later, use the (now recovered) connection. Note that depending
57
+ # on # of plugins used it may take 5-10 seconds to start up RabbitMQ and initialize it,
58
+ # then open a new AMQP connection. That's why we wait. MK.
59
+ EventMachine.add_timer(12.0) do
60
+ AMQP.connection.should be_connected
61
+ AMQP.connection.should_not be_reconnecting
62
+
63
+ @channel.queue("amqpgem.tests.a.queue", :auto_delete => true).subscribe do |metadata, payload|
64
+ puts "Got a message"
65
+ done
66
+ end
67
+
68
+ EventMachine.add_timer(1.5) { @channel.default_exchange.publish("Hi", :routing_key => "amqpgem.tests.a.queue") }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Unacknowledged messages" do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include EventedSpec::AMQPSpec
12
+
13
+ default_timeout 5
14
+
15
+ amqp_before do
16
+ @connection1 = AMQP.connect
17
+ @connection2 = AMQP.connect
18
+ @connection3 = AMQP.connect
19
+
20
+ @channel1 = AMQP::Channel.new(@connection1)
21
+ @channel2 = AMQP::Channel.new(@connection2)
22
+ @channel3 = AMQP::Channel.new(@connection3)
23
+
24
+ [@channel1, @channel2, @channel3].each { |ch| ch.on_error { fail } }
25
+
26
+ @channel1.prefetch(3)
27
+ @channel2.prefetch(1)
28
+ end
29
+
30
+ after(:all) do
31
+ AMQP.cleanup_state
32
+ done
33
+ end
34
+
35
+
36
+
37
+ #
38
+ # Examples
39
+ #
40
+
41
+ # this is a spec example based on one of the Working With Queues doc guides.
42
+ # It is somewhat hairy since it imitates 3 apps in a single process
43
+ # but demonstrates redeliveries pretty well. MK.
44
+
45
+ it "are redelivered to alternate consumers when the 'primary' one disconnects" do
46
+ number_of_messages_app2_received = 0
47
+ expected_number_of_deliveries = 21
48
+ redelivery_values = Array.new
49
+
50
+ exchange = @channel3.direct("amq.direct")
51
+
52
+ queue1 = @channel1.queue("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
53
+ # purge the queue so that we don't get any redeliveries from previous runs
54
+ queue1.purge
55
+ queue1.bind(exchange).subscribe(:ack => true) do |metadata, payload|
56
+ # acknowledge some messages, they will be removed from the queue
57
+ if metadata.headers["i"] < 10
58
+ @channel1.acknowledge(metadata.delivery_tag, false)
59
+ else
60
+ # some messages are not ack-ed and will remain in the queue for redelivery
61
+ # when app #1 connection is closed (either properly or due to a crash)
62
+ end
63
+ end
64
+
65
+ queue2 = @channel2.queue!("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
66
+ queue2.subscribe(:ack => true) do |metadata, payload|
67
+ redelivery_values << metadata.redelivered?
68
+
69
+ # app 2 always acks messages
70
+ metadata.ack
71
+
72
+ number_of_messages_app2_received += 1
73
+ end
74
+
75
+ EventMachine.add_timer(2.0) {
76
+ # app1 quits/crashes
77
+ @connection1.close
78
+ }
79
+
80
+
81
+ # 0.5 seconds later, publish a bunch of messages
82
+ EventMachine.add_timer(0.5) {
83
+ 30.times do |i|
84
+ exchange.publish("Message ##{i}", :headers => { :i => i })
85
+ i += 1
86
+ end
87
+ }
88
+
89
+
90
+ done(4.8) {
91
+ number_of_messages_app2_received.should be >= expected_number_of_deliveries
92
+ # 3 last messages are redeliveries
93
+ redelivery_values.last(7).should == [false, false, false, false, true, true, true]
94
+ }
95
+ end
96
+ end