amqp 0.9.9 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -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(&block)
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
@@ -221,7 +221,11 @@ module AMQP
221
221
  #
222
222
  # @api public
223
223
  def once_declared(&block)
224
- @declaration_deferrable.callback(&block)
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
- if self.server_named?
285
- @channel.once_open do
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
- super(exchange, (opts[:key] || opts[:routing_key] || AMQ::Protocol::EMPTY_STRING), opts[:arguments], &block)
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
- super(opts.fetch(:if_unused, false), opts.fetch(:if_empty, false), opts.fetch(:nowait, false), &block)
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
- super(opts.fetch(:nowait, false), &block)
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
- # see AMQ::Client::Queue#get in amq-client
481
- self.get(!opts.fetch(:ack, false), &shim)
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 { self.get(!opts.fetch(:ack, false)) }
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.once_declared do
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.once_declared do
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
- # we do not use self.declare here to avoid caching of @passive since that will cause unexpected side-effects during automatic
816
- # recovery process. MK.
817
- @connection.send_frame(AMQ::Protocol::Queue::Declare.encode(@channel.id, @name, true, @opts[:durable], @opts[:exclusive], @opts[:auto_delete], false, @opts[:arguments]))
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
- self.append_callback(:declare, &shim)
820
- @channel.queues_awaiting_declare_ok.push(self)
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.
@@ -6,5 +6,5 @@ module AMQP
6
6
  #
7
7
  # @see AMQ::Protocol::VERSION
8
8
  # @return [String] AMQP gem version
9
- VERSION = '0.9.9'
9
+ VERSION = '0.9.10'
10
10
  end
@@ -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
- number_of_received_messages = 0
65
- expected_number_of_messages = 300
64
+ @channel.default_exchange.publish(@dispatched_data, :routing_key => @queue.name)
66
65
 
67
- expected_number_of_messages.times do |i|
68
- @exchange.publish(@dispatched_data + "_#{i}", :key => @queue_name)
66
+ @queue.pop do |headers, payload|
67
+ payload.should == @dispatched_data
69
68
  end
70
69
 
71
- @queue.status do |number_of_messages, number_of_consumers|
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(2.5) {
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(2.5) {
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
- if RUBY_VERSION =~ /^1.9/
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
- expected_number_of_messages.times do
64
- @exchange.publish(dispatched_data, :routing_key => @queue_name)
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
- # 6 seconds are for Rubinius, it is surprisingly slow on this workload
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 # do
69
+ end
70
70
 
71
- done(0.4) {
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.2) {
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.2) {
184
+ done(0.5) {
184
185
  received_messages.should == expected_messages
185
186
 
186
187
  @sports_queue.unsubscribe
@@ -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 => 5672}
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
@@ -29,7 +29,8 @@ describe AMQP, 'class object' do
29
29
  :ssl => false,
30
30
  :broker => nil,
31
31
  :frame_max => 131072,
32
- :heartbeat => 0
32
+ :heartbeat => 0,
33
+ :auth_mechanism => "PLAIN"
33
34
  }
34
35
  end
35
36
 
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.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-02-21 00:00:00.000000000 Z
14
+ date: 2013-03-05 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: eventmachine