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