amqp 0.9.9 → 0.9.10
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/amqp/channel.rb +20 -3
- data/lib/amqp/queue.rb +46 -22
- data/lib/amqp/version.rb +1 -1
- data/spec/integration/basic_get_spec.rb +80 -24
- data/spec/integration/fanout_exchange_routing_spec.rb +1 -1
- data/spec/integration/store_and_forward_spec.rb +6 -9
- data/spec/integration/topic_subscription_spec.rb +5 -4
- data/spec/spec_helper.rb +8 -2
- data/spec/unit/amqp/connection_spec.rb +2 -1
- metadata +2 -2
data/lib/amqp/channel.rb
CHANGED
@@ -263,6 +263,9 @@ module AMQP
|
|
263
263
|
def auto_recover
|
264
264
|
return unless auto_recovering?
|
265
265
|
|
266
|
+
@channel_is_open_deferrable.fail
|
267
|
+
@channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
|
268
|
+
|
266
269
|
self.open do
|
267
270
|
@channel_is_open_deferrable.succeed
|
268
271
|
|
@@ -286,6 +289,9 @@ module AMQP
|
|
286
289
|
@id = self.class.next_channel_id
|
287
290
|
self.class.release_channel_id(old_id)
|
288
291
|
|
292
|
+
@channel_is_open_deferrable.fail
|
293
|
+
@channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
|
294
|
+
|
289
295
|
self.open do
|
290
296
|
@channel_is_open_deferrable.succeed
|
291
297
|
|
@@ -929,16 +935,27 @@ module AMQP
|
|
929
935
|
#
|
930
936
|
# @api public
|
931
937
|
def once_open(&block)
|
932
|
-
@channel_is_open_deferrable.callback
|
938
|
+
@channel_is_open_deferrable.callback do
|
939
|
+
# guards against cases when deferred operations
|
940
|
+
# don't complete before the channel is closed
|
941
|
+
block.call if open?
|
942
|
+
end
|
933
943
|
end # once_open(&block)
|
934
944
|
alias once_opened once_open
|
935
945
|
|
946
|
+
# @return [Boolean]
|
947
|
+
# @api public
|
948
|
+
def closing?
|
949
|
+
self.status == :closing
|
950
|
+
end
|
951
|
+
|
936
952
|
# Closes AMQP channel.
|
937
953
|
#
|
938
954
|
# @api public
|
939
955
|
def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
|
956
|
+
self.status = :closing
|
940
957
|
r = super(reply_code, reply_text, class_id, method_id, &block)
|
941
|
-
|
958
|
+
|
942
959
|
r
|
943
960
|
end
|
944
961
|
|
@@ -1122,7 +1139,7 @@ module AMQP
|
|
1122
1139
|
self.class.error(method.reply_text)
|
1123
1140
|
self.class.release_channel_id(@id)
|
1124
1141
|
end
|
1125
|
-
|
1142
|
+
|
1126
1143
|
# Overrides AMQ::Client::Channel version to also release the channel id
|
1127
1144
|
#
|
1128
1145
|
# @private
|
data/lib/amqp/queue.rb
CHANGED
@@ -221,7 +221,11 @@ module AMQP
|
|
221
221
|
#
|
222
222
|
# @api public
|
223
223
|
def once_declared(&block)
|
224
|
-
@declaration_deferrable.callback
|
224
|
+
@declaration_deferrable.callback do
|
225
|
+
# guards against cases when deferred operations
|
226
|
+
# don't complete before the channel is closed
|
227
|
+
block.call if @channel.open?
|
228
|
+
end
|
225
229
|
end # once_declared(&block)
|
226
230
|
|
227
231
|
|
@@ -281,14 +285,8 @@ module AMQP
|
|
281
285
|
# @api public
|
282
286
|
# @see Queue#unbind
|
283
287
|
def bind(exchange, opts = {}, &block)
|
284
|
-
|
285
|
-
|
286
|
-
@declaration_deferrable.callback do
|
287
|
-
super(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), (opts[:nowait] || block.nil?), opts[:arguments], &block)
|
288
|
-
end
|
289
|
-
end
|
290
|
-
else
|
291
|
-
@channel.once_open do
|
288
|
+
@channel.once_open do
|
289
|
+
self.once_name_is_available do
|
292
290
|
super(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), (opts[:nowait] || block.nil?), opts[:arguments], &block)
|
293
291
|
end
|
294
292
|
end
|
@@ -357,7 +355,9 @@ module AMQP
|
|
357
355
|
# @see Queue#bind
|
358
356
|
def unbind(exchange, opts = {}, &block)
|
359
357
|
@channel.once_open do
|
360
|
-
|
358
|
+
self.once_name_is_available do
|
359
|
+
super(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), opts[:arguments], &block)
|
360
|
+
end
|
361
361
|
end
|
362
362
|
end
|
363
363
|
|
@@ -391,7 +391,9 @@ module AMQP
|
|
391
391
|
# @see Queue#unbind
|
392
392
|
def delete(opts = {}, &block)
|
393
393
|
@channel.once_open do
|
394
|
-
|
394
|
+
self.once_name_is_available do
|
395
|
+
super(opts.fetch(:if_unused, false), opts.fetch(:if_empty, false), opts.fetch(:nowait, false), &block)
|
396
|
+
end
|
395
397
|
end
|
396
398
|
|
397
399
|
# backwards compatibility
|
@@ -416,7 +418,9 @@ module AMQP
|
|
416
418
|
# @see Queue#unbind
|
417
419
|
def purge(opts = {}, &block)
|
418
420
|
@channel.once_open do
|
419
|
-
|
421
|
+
self.once_declared do
|
422
|
+
super(opts.fetch(:nowait, false), &block)
|
423
|
+
end
|
420
424
|
end
|
421
425
|
|
422
426
|
# backwards compatibility
|
@@ -477,11 +481,17 @@ module AMQP
|
|
477
481
|
}
|
478
482
|
|
479
483
|
@channel.once_open do
|
480
|
-
|
481
|
-
|
484
|
+
self.once_name_is_available do
|
485
|
+
# see AMQ::Client::Queue#get in amq-client
|
486
|
+
self.get(!opts.fetch(:ack, false), &shim)
|
487
|
+
end
|
482
488
|
end
|
483
489
|
else
|
484
|
-
@channel.once_open
|
490
|
+
@channel.once_open do
|
491
|
+
self.once_name_is_available do
|
492
|
+
self.get(!opts.fetch(:ack, false))
|
493
|
+
end
|
494
|
+
end
|
485
495
|
end
|
486
496
|
end
|
487
497
|
|
@@ -719,7 +729,9 @@ module AMQP
|
|
719
729
|
opts[:nowait] = false if (@on_confirm_subscribe = opts[:confirm])
|
720
730
|
|
721
731
|
@channel.once_open do
|
722
|
-
self.
|
732
|
+
self.once_name_is_available do
|
733
|
+
# guards against a pathological case race condition when a channel
|
734
|
+
# is opened and closed before delayed operations are completed.
|
723
735
|
self.consume(!opts[:ack], opts[:exclusive], (opts[:nowait] || block.nil?), opts[:no_local], nil, &opts[:confirm])
|
724
736
|
|
725
737
|
self.on_delivery(&block)
|
@@ -785,7 +797,7 @@ module AMQP
|
|
785
797
|
# @api public
|
786
798
|
def unsubscribe(opts = {}, &block)
|
787
799
|
@channel.once_open do
|
788
|
-
self.
|
800
|
+
self.once_name_is_available do
|
789
801
|
if @default_consumer
|
790
802
|
@default_consumer.cancel(opts.fetch(:nowait, true), &block); @default_consumer = nil
|
791
803
|
end
|
@@ -812,12 +824,14 @@ module AMQP
|
|
812
824
|
shim = Proc.new { |q, declare_ok| block.call(declare_ok.message_count, declare_ok.consumer_count) }
|
813
825
|
|
814
826
|
@channel.once_open do
|
815
|
-
|
816
|
-
|
817
|
-
|
827
|
+
self.once_name_is_available do
|
828
|
+
# we do not use self.declare here to avoid caching of @passive since that will cause unexpected side-effects during automatic
|
829
|
+
# recovery process. MK.
|
830
|
+
@connection.send_frame(AMQ::Protocol::Queue::Declare.encode(@channel.id, @name, true, @opts[:durable], @opts[:exclusive], @opts[:auto_delete], false, @opts[:arguments]))
|
818
831
|
|
819
|
-
|
820
|
-
|
832
|
+
self.append_callback(:declare, &shim)
|
833
|
+
@channel.queues_awaiting_declare_ok.push(self)
|
834
|
+
end
|
821
835
|
end
|
822
836
|
|
823
837
|
self
|
@@ -883,6 +897,16 @@ module AMQP
|
|
883
897
|
{ :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
|
884
898
|
end
|
885
899
|
|
900
|
+
def once_name_is_available(&block)
|
901
|
+
if server_named?
|
902
|
+
self.once_declared do
|
903
|
+
block.call
|
904
|
+
end
|
905
|
+
else
|
906
|
+
block.call
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
886
910
|
private
|
887
911
|
|
888
912
|
# Default direct exchange that we use to publish messages directly to this queue.
|
data/lib/amqp/version.rb
CHANGED
@@ -61,36 +61,92 @@ describe AMQP::Queue, "#pop" do
|
|
61
61
|
|
62
62
|
context "when THERE ARE messages in the queue" do
|
63
63
|
it "yields message payload to the callback" do
|
64
|
-
|
65
|
-
expected_number_of_messages = 300
|
64
|
+
@channel.default_exchange.publish(@dispatched_data, :routing_key => @queue.name)
|
66
65
|
|
67
|
-
|
68
|
-
|
66
|
+
@queue.pop do |headers, payload|
|
67
|
+
payload.should == @dispatched_data
|
69
68
|
end
|
70
69
|
|
71
|
-
|
72
|
-
expected_number_of_messages.times do
|
73
|
-
@queue.pop do |headers, payload|
|
74
|
-
payload.should_not be_nil
|
75
|
-
number_of_received_messages += 1
|
76
|
-
headers.message_count.should == (expected_number_of_messages - number_of_received_messages)
|
77
|
-
|
78
|
-
if RUBY_VERSION =~ /^1.9/
|
79
|
-
payload.force_encoding("UTF-8").should =~ /#{@dispatched_data}/
|
80
|
-
else
|
81
|
-
payload.should =~ /#{@dispatched_data}/
|
82
|
-
end
|
83
|
-
end # pop
|
84
|
-
end # do
|
85
|
-
end
|
86
|
-
|
87
|
-
delayed(1.3) {
|
70
|
+
delayed(0.5) {
|
88
71
|
# Queue.Get doesn't qualify for subscription, hence, manual deletion is required
|
89
72
|
@queue.delete
|
90
73
|
}
|
91
|
-
done(
|
92
|
-
number_of_received_messages.should == expected_number_of_messages
|
93
|
-
}
|
74
|
+
done(0.8)
|
94
75
|
end # it
|
95
76
|
end # context
|
77
|
+
|
78
|
+
|
79
|
+
context "with manual acknowledgements" do
|
80
|
+
default_timeout 4
|
81
|
+
|
82
|
+
let(:queue_name) { "amqpgem.integration.basic.get.acks.manual#{rand}" }
|
83
|
+
|
84
|
+
it "does not remove messages from the queue unless ack-ed" do
|
85
|
+
ch1 = AMQP::Channel.new
|
86
|
+
ch2 = AMQP::Channel.new
|
87
|
+
|
88
|
+
ch1.on_error do |ch, close_ok|
|
89
|
+
puts "Channel error: #{close_ok.reply_code} #{close_ok.reply_text}"
|
90
|
+
end
|
91
|
+
|
92
|
+
q = ch1.queue(queue_name, :exclusive => true)
|
93
|
+
x = ch1.default_exchange
|
94
|
+
|
95
|
+
q.purge
|
96
|
+
delayed(0.2) { x.publish(@dispatched_data, :routing_key => q.name) }
|
97
|
+
|
98
|
+
delayed(0.5) {
|
99
|
+
q.pop(:ack => true) do |meta, payload|
|
100
|
+
# never ack
|
101
|
+
end
|
102
|
+
|
103
|
+
ch1.close
|
104
|
+
|
105
|
+
EventMachine.add_timer(0.7) {
|
106
|
+
ch2.queue(queue_name, :exclusive => true).status do |number_of_messages, number_of_consumers|
|
107
|
+
number_of_messages.should == 1
|
108
|
+
done
|
109
|
+
end
|
110
|
+
}
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
context "with automatic acknowledgements" do
|
118
|
+
default_timeout 4
|
119
|
+
|
120
|
+
let(:queue_name) { "amqpgem.integration.basic.get.acks.automatic#{rand}" }
|
121
|
+
|
122
|
+
it "does remove messages from the queue after delivery" do
|
123
|
+
ch1 = AMQP::Channel.new
|
124
|
+
ch2 = AMQP::Channel.new
|
125
|
+
|
126
|
+
ch1.on_error do |ch, close_ok|
|
127
|
+
puts "Channel error: #{close_ok.reply_code} #{close_ok.reply_text}"
|
128
|
+
end
|
129
|
+
|
130
|
+
q = ch1.queue(queue_name, :exclusive => true)
|
131
|
+
x = ch1.default_exchange
|
132
|
+
|
133
|
+
q.purge
|
134
|
+
x.publish(@dispatched_data, :routing_key => q.name)
|
135
|
+
|
136
|
+
delayed(0.5) {
|
137
|
+
q.pop(:ack => false) do |meta, payload|
|
138
|
+
# never ack
|
139
|
+
end
|
140
|
+
|
141
|
+
ch1.close
|
142
|
+
|
143
|
+
EventMachine.add_timer(0.5) {
|
144
|
+
ch2.queue(queue_name, :exclusive => true).status do |number_of_messages, number_of_consumers|
|
145
|
+
number_of_messages.should == 0
|
146
|
+
done
|
147
|
+
end
|
148
|
+
}
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
96
152
|
end # describe
|
@@ -77,7 +77,7 @@ describe AMQP::Exchange, "of type fanout" do
|
|
77
77
|
end
|
78
78
|
|
79
79
|
# for Rubinius, it is surprisingly slow on this workload
|
80
|
-
done(
|
80
|
+
done(1.5) {
|
81
81
|
[@queue1, @queue2, @queue3].each do |q|
|
82
82
|
@received_messages[q.name].size.should == @expected_number_of_messages[q.name]
|
83
83
|
|
@@ -53,19 +53,16 @@ describe "Store-and-forward routing" do
|
|
53
53
|
@queue.subscribe(:ack => false) do |payload|
|
54
54
|
payload.should_not be_nil
|
55
55
|
number_of_received_messages += 1
|
56
|
-
|
57
|
-
payload.force_encoding("UTF-8").should == dispatched_data
|
58
|
-
else
|
59
|
-
payload.should == dispatched_data
|
60
|
-
end
|
56
|
+
payload.should == dispatched_data
|
61
57
|
end # subscribe
|
62
58
|
|
63
|
-
|
64
|
-
|
59
|
+
delayed(0.3) do
|
60
|
+
expected_number_of_messages.times do
|
61
|
+
@exchange.publish(dispatched_data, :routing_key => @queue_name)
|
62
|
+
end
|
65
63
|
end
|
66
64
|
|
67
|
-
|
68
|
-
done(6.0) {
|
65
|
+
done(4.0) {
|
69
66
|
number_of_received_messages.should == expected_number_of_messages
|
70
67
|
@queue.unsubscribe
|
71
68
|
}
|
@@ -66,9 +66,9 @@ describe "Topic-based subscription" do
|
|
66
66
|
# publish some messages none of our queues should be receiving
|
67
67
|
3.times do
|
68
68
|
@exchange.publish(626 + rand(1000)/400.0, :key => "nasdaq.goog")
|
69
|
-
end
|
69
|
+
end
|
70
70
|
|
71
|
-
done(0.
|
71
|
+
done(0.5) {
|
72
72
|
received_messages.should == expected_messages
|
73
73
|
@aapl_queue.unsubscribe
|
74
74
|
@amzn_queue.unsubscribe
|
@@ -117,7 +117,7 @@ describe "Topic-based subscription" do
|
|
117
117
|
@exchange.publish("Blatche, Wall lead Wizards over Jazz 108-101", :key => "sports.nba.jazz")
|
118
118
|
@exchange.publish("Deron Williams Receives NBA Cares Community Assist Award", :key => "sports.nba.jazz")
|
119
119
|
|
120
|
-
done(0.
|
120
|
+
done(0.6) {
|
121
121
|
received_messages.should == expected_messages
|
122
122
|
|
123
123
|
@nba_queue.unsubscribe
|
@@ -151,6 +151,7 @@ describe "Topic-based subscription" do
|
|
151
151
|
@celtics_queue.name => 3
|
152
152
|
}
|
153
153
|
|
154
|
+
|
154
155
|
@sports_queue.bind(@exchange, :key => "sports.#").subscribe do |payload|
|
155
156
|
received_messages[@sports_queue.name] += 1
|
156
157
|
end
|
@@ -180,7 +181,7 @@ describe "Topic-based subscription" do
|
|
180
181
|
@exchange.publish("Philadelphia's Daniel Briere has been named as an All-Star replacement for Jarome Iginla.", :key => "sports.nhl.allstargame")
|
181
182
|
@exchange.publish("Devils blank Sid- and Malkin-less Penguins 2-0", :key => "sports.nhl.penguins")
|
182
183
|
|
183
|
-
done(0.
|
184
|
+
done(0.5) {
|
184
185
|
received_messages.should == expected_messages
|
185
186
|
|
186
187
|
@sports_queue.unsubscribe
|
data/spec/spec_helper.rb
CHANGED
@@ -15,6 +15,12 @@ puts "Using Ruby #{RUBY_VERSION}, amq-client #{AMQ::Client::VERSION} and amq-pro
|
|
15
15
|
|
16
16
|
amqp_config = File.dirname(__FILE__) + '/amqp.yml'
|
17
17
|
|
18
|
+
port = if ENV["TRACER"]
|
19
|
+
5673
|
20
|
+
else
|
21
|
+
5672
|
22
|
+
end
|
23
|
+
|
18
24
|
if File.exists? amqp_config
|
19
25
|
class Hash
|
20
26
|
def symbolize_keys
|
@@ -28,7 +34,7 @@ if File.exists? amqp_config
|
|
28
34
|
end
|
29
35
|
AMQP_OPTS = YAML::load_file(amqp_config).symbolize_keys[:test]
|
30
36
|
else
|
31
|
-
AMQP_OPTS = {:host => 'localhost', :port =>
|
37
|
+
AMQP_OPTS = {:host => 'localhost', :port => port}
|
32
38
|
end
|
33
39
|
|
34
40
|
# puts "AMQP_OPTS = #{AMQP_OPTS.inspect}"
|
@@ -82,4 +88,4 @@ module PlatformDetection
|
|
82
88
|
def rubinius?
|
83
89
|
defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'rbx')
|
84
90
|
end
|
85
|
-
end
|
91
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amqp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-
|
14
|
+
date: 2013-03-05 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: eventmachine
|