amqp 0.9.9 → 0.9.10
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.
- 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
|