beetle 3.4.0 → 3.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28836e3040ba8ef4415a6aae49f3ae805384e61b45cd76993a29d7589c23b7bf
4
- data.tar.gz: bed46b58fe787d79a21b1fb3413a0359d948e79e70da0de92bfb717ad5e4e7be
3
+ metadata.gz: 2dcaf90eaf1b11b7f6350bb34e17d73591616790ede7b43b6e98cef363dd3770
4
+ data.tar.gz: 55348c9ce61cc2f43869f9c12cd26bbc5dc033b0e0a0506c0a821d6a6e24862c
5
5
  SHA512:
6
- metadata.gz: 5b92b7588bcdd74a13a211d76997bae5f2255f729bb62b3c673ef00ec0fbda9a69e609055ad1a65797350c8151da6aa059093af3a27016069f22f1fa77c347d6
7
- data.tar.gz: 7baa29a222dea24a7ce3f90fbeb90634d7b816197aa34c7fb5401e0d4754ab339f36c733d67aeccbeaf61524e9f74a59f07e697e931af96d94343d9977209dea
6
+ metadata.gz: 6daabfea72ab76b0fa6944d35cee52139678f9a02a272e86a32af6045750496ee81b96ff685eee1bbe9617030b981df3718b7569efabc98df59f894127da29d1
7
+ data.tar.gz: 04d599595b4c39e62a78b496b284a67bed1a545e759d9a094facc757e0334ed16e4bdd7bab4f7bfabb7b6f63da0e590351e86d0da55328ab6db898e9ceaaef7f
data/RELEASE_NOTES.rdoc CHANGED
@@ -1,5 +1,22 @@
1
1
  = Release Notes
2
2
 
3
+ == Version 3.5.1
4
+ * remove blank entries from server list strings
5
+ * make sure not to subscribe to any server twice
6
+
7
+ == Version 3.5.0
8
+ * expose publisher method to setup queues/policies ahead of use
9
+
10
+ == Version 3.4.3
11
+ * optimize declaration of queues with many bindings
12
+
13
+ == Version 3.4.2
14
+ * Updated amq-protocol gem to version 2.3.2.
15
+ * Fixed a rare race condition on message handler timeouts.
16
+
17
+ == Version 3.4.1
18
+ * Updated amq-protocol gem to version 2.3.1.
19
+
3
20
  == Version 3.4.0
4
21
  * Require redis gem version 4.2.1. This version changes the exists to check for the
5
22
  existence of multiple keys, return the number of keys in the list that exist. This
data/beetle.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.add_runtime_dependency "bunny", "~> 0.7.12"
27
27
  s.add_runtime_dependency "redis", ">= 4.2.1"
28
28
  s.add_runtime_dependency "hiredis", ">= 0.4.5"
29
- s.add_runtime_dependency "amq-protocol", "= 2.3.0"
29
+ s.add_runtime_dependency "amq-protocol", "= 2.3.2"
30
30
  s.add_runtime_dependency "amqp", "= 1.8.0"
31
31
  s.add_runtime_dependency "activesupport", ">= 2.3.4"
32
32
 
data/lib/beetle/base.rb CHANGED
@@ -67,23 +67,25 @@ module Beetle
67
67
  logger.debug("Beetle: binding queue #{name} with internal name #{opts[:amqp_name]} on server #{@server}")
68
68
  queue_name = opts[:amqp_name]
69
69
  creation_options = opts.slice(*QUEUE_CREATION_KEYS)
70
- the_queue = nil
70
+
71
+ the_queue = declare_queue!(queue_name, creation_options)
71
72
  @client.bindings[name].each do |binding_options|
72
73
  exchange_name = binding_options[:exchange]
73
74
  binding_options = binding_options.slice(*QUEUE_BINDING_KEYS)
74
- the_queue = bind_queue!(queue_name, creation_options, exchange_name, binding_options)
75
+ logger.debug("Beetle: binding queue #{queue_name} to #{exchange_name} with opts: #{binding_options.inspect}")
76
+ bind_queue!(the_queue, exchange_name, binding_options)
75
77
  end
76
78
  the_queue
77
79
  end
78
80
  end
79
81
 
80
- def bind_dead_letter_queue!(channel, target_queue, creation_keys = {})
82
+ def bind_dead_letter_queue!(channel, target_queue, creation_options = {})
81
83
  policy_options = @client.queues[target_queue].slice(:dead_lettering, :lazy, :dead_lettering_msg_ttl)
82
84
  policy_options[:message_ttl] = policy_options.delete(:dead_lettering_msg_ttl)
83
85
  dead_letter_queue_name = "#{target_queue}_dead_letter"
84
86
  if policy_options[:dead_lettering]
85
- logger.debug("Beetle: creating dead letter queue #{dead_letter_queue_name} with opts: #{creation_keys.inspect}")
86
- channel.queue(dead_letter_queue_name, creation_keys)
87
+ logger.debug("Beetle: creating dead letter queue #{dead_letter_queue_name} with opts: #{creation_options.inspect}")
88
+ channel.queue(dead_letter_queue_name, creation_options)
87
89
  end
88
90
  return {
89
91
  :queue_name => target_queue,
@@ -93,7 +95,7 @@ module Beetle
93
95
  }.merge(policy_options)
94
96
  end
95
97
 
96
- # called by <tt>bind_queue!</tt>
98
+ # called by <tt>declare_queue!</tt>
97
99
  def publish_policy_options(options)
98
100
  # avoid endless recursion
99
101
  return if options[:queue_name] == @client.config.beetle_policy_updates_queue_name
data/lib/beetle/client.rb CHANGED
@@ -280,6 +280,13 @@ module Beetle
280
280
  publisher.throttled?
281
281
  end
282
282
 
283
+ # set up queues and policies for all configured queues. Otherwise this will
284
+ # happen on first use of an exchange, which can be undesired for latency
285
+ # sensitive endpoints. Only needs to be called once.
286
+ def setup_queues_and_policies
287
+ publisher.setup_queues_and_policies
288
+ end
289
+
283
290
  # traces queues without consuming them. useful for debugging message flow.
284
291
  def trace(queue_names=self.queues.keys, tracer=nil, &block)
285
292
  queues_to_trace = self.queues.slice(*queue_names)
@@ -378,8 +385,12 @@ module Beetle
378
385
  end
379
386
 
380
387
  def load_brokers_from_config
381
- @servers = config.servers.split(/ *, */)
382
- @additional_subscription_servers = config.additional_subscription_servers.split(/ *, */)
388
+ @servers = parse_server_list(config.servers)
389
+ @additional_subscription_servers = parse_server_list(config.additional_subscription_servers) - @servers
390
+ end
391
+
392
+ def parse_server_list(s)
393
+ s.split(/ *, */).uniq.reject(&:blank?)
383
394
  end
384
395
  end
385
396
  end
@@ -18,6 +18,8 @@ module Beetle
18
18
  # forcefully abort a running handler after this many seconds.
19
19
  # can be overriden when registering a handler.
20
20
  DEFAULT_HANDLER_TIMEOUT = 600.seconds
21
+ # How much extra time on top of the handler timeout we add before considering a handler timed out
22
+ TIMEOUT_GRACE_PERIOD = 10.seconds
21
23
  # how many times we should try to run a handler before giving up
22
24
  DEFAULT_HANDLER_EXECUTION_ATTEMPTS = 1
23
25
  # how many seconds we should wait before retrying handler execution
@@ -167,8 +169,8 @@ module Beetle
167
169
  end
168
170
 
169
171
  # handler timed out?
170
- def timed_out?
171
- (t = @store.get(msg_id, :timeout)) && t.to_i < now
172
+ def timed_out?(t = nil)
173
+ (t ||= @store.get(msg_id, :timeout)) && (t.to_i + TIMEOUT_GRACE_PERIOD) < now
172
174
  end
173
175
 
174
176
  # reset handler timeout in the deduplication store
@@ -187,8 +189,8 @@ module Beetle
187
189
  end
188
190
 
189
191
  # whether we should wait before running the handler
190
- def delayed?
191
- (t = @store.get(msg_id, :delay)) && t.to_i > now
192
+ def delayed?(t = nil)
193
+ (t ||= @store.get(msg_id, :delay)) && t.to_i > now
192
194
  end
193
195
 
194
196
  # store delay value in the deduplication store
@@ -207,8 +209,8 @@ module Beetle
207
209
  end
208
210
 
209
211
  # whether we have already tried running the handler as often as specified when the handler was registered
210
- def attempts_limit_reached?
211
- (limit = @store.get(msg_id, :attempts)) && limit.to_i >= attempts_limit
212
+ def attempts_limit_reached?(attempts = nil)
213
+ (attempts ||= @store.get(msg_id, :attempts)) && attempts.to_i >= attempts_limit
212
214
  end
213
215
 
214
216
  # increment number of exception occurences in the deduplication store
@@ -217,8 +219,8 @@ module Beetle
217
219
  end
218
220
 
219
221
  # whether the number of exceptions has exceeded the limit set when the handler was registered
220
- def exceptions_limit_reached?
221
- @store.get(msg_id, :exceptions).to_i > exceptions_limit
222
+ def exceptions_limit_reached?(exceptions = nil)
223
+ (exceptions ||= @store.get(msg_id, :exceptions)) && exceptions.to_i > exceptions_limit
222
224
  end
223
225
 
224
226
  def exception_accepted?
@@ -306,17 +308,17 @@ module Beetle
306
308
  if status == "completed"
307
309
  ack!
308
310
  RC::OK
309
- elsif delay && delay.to_i > now
311
+ elsif delay && delayed?(delay)
310
312
  logger.warn "Beetle: ignored delayed message (#{msg_id})!"
311
313
  RC::Delayed
312
- elsif !(timeout && timeout.to_i < now)
314
+ elsif !(timeout && timed_out?(timeout))
313
315
  RC::HandlerNotYetTimedOut
314
- elsif attempts.to_i >= attempts_limit
316
+ elsif attempts && attempts_limit_reached?(attempts)
315
317
  completed!
316
318
  ack!
317
319
  logger.warn "Beetle: reached the handler execution attempts limit: #{attempts_limit} on #{msg_id}"
318
320
  RC::AttemptsLimitReached
319
- elsif exceptions.to_i > exceptions_limit
321
+ elsif exceptions && exceptions_limit_reached?(exceptions)
320
322
  completed!
321
323
  ack!
322
324
  logger.warn "Beetle: reached the handler exceptions limit: #{exceptions_limit} on #{msg_id}"
@@ -169,10 +169,14 @@ module Beetle
169
169
  end
170
170
  end
171
171
 
172
- def setup_queues_and_policies(queue_names) #:nodoc:
172
+ def setup_queues_and_policies
173
173
  each_server do
174
- queue_names.each do |name|
175
- queue(name, create_policies: true)
174
+ begin
175
+ @client.queues.keys.each do |name|
176
+ queue(name)
177
+ end
178
+ rescue => e
179
+ logger.warn "Beetle: failed setting up queues and policies on #{@server}: #{e}"
176
180
  end
177
181
  end
178
182
  end
@@ -247,17 +251,19 @@ module Beetle
247
251
  @exchanges_with_bound_queues[exchange_name] = true
248
252
  end
249
253
 
250
- # TODO: Refactor, fetch the keys and stuff itself
251
- def bind_queue!(queue_name, creation_keys, exchange_name, binding_keys)
252
- logger.debug("Beetle: creating queue with opts: #{creation_keys.inspect}")
253
- queue = bunny.queue(queue_name, creation_keys)
254
- logger.debug("Beetle: binding queue #{queue_name} to #{exchange_name} with opts: #{binding_keys.inspect}")
255
- queue.bind(exchange(exchange_name), binding_keys)
256
- policy_options = bind_dead_letter_queue!(bunny, queue_name, creation_keys)
254
+ def declare_queue!(queue_name, creation_options)
255
+ logger.debug("Beetle: creating queue with opts: #{creation_options.inspect}")
256
+ queue = bunny.queue(queue_name, creation_options)
257
+
258
+ policy_options = bind_dead_letter_queue!(bunny, queue_name, creation_options)
257
259
  publish_policy_options(policy_options)
258
260
  queue
259
261
  end
260
262
 
263
+ def bind_queue!(queue, exchange_name, binding_options)
264
+ queue.bind(exchange(exchange_name), binding_options)
265
+ end
266
+
261
267
  def stop!(exception=nil)
262
268
  return unless bunny?
263
269
  timeout = @client.config.publishing_timeout + @client.config.publisher_connect_timeout + 1
@@ -214,18 +214,20 @@ module Beetle
214
214
  channel.__send__(opts[:type], name, opts.slice(*EXCHANGE_CREATION_KEYS))
215
215
  end
216
216
 
217
- def bind_queue!(queue_name, creation_keys, exchange_name, binding_keys)
218
- queue = channel.queue(queue_name, creation_keys)
217
+ def declare_queue!(queue_name, creation_options)
218
+ queue = channel.queue(queue_name, creation_options)
219
219
  unless tracing?
220
220
  # we don't want to create dead-letter queues for tracing
221
- policy_options = bind_dead_letter_queue!(channel, queue_name, creation_keys)
221
+ policy_options = bind_dead_letter_queue!(channel, queue_name, creation_options)
222
222
  publish_policy_options(policy_options)
223
223
  end
224
- exchange = exchange(exchange_name)
225
- queue.bind(exchange, binding_keys)
226
224
  queue
227
225
  end
228
226
 
227
+ def bind_queue!(queue, exchange_name, binding_options)
228
+ queue.bind(exchange(exchange_name), binding_options)
229
+ end
230
+
229
231
  def connection_settings
230
232
  {
231
233
  :host => current_host, :port => current_port, :logging => false,
@@ -1,3 +1,3 @@
1
1
  module Beetle
2
- VERSION = "3.4.0"
2
+ VERSION = "3.5.1"
3
3
  end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'time'
4
+
5
+ counts = Hash.new(0)
6
+ expiries = Hash.new{|h,k| h[k] = Hash.new(0)}
7
+ t = Time.now.to_i
8
+
9
+ File.open(ARGV[0]).each_line do |l|
10
+ parts = l.split(':')
11
+ queue = parts[1]
12
+ counts[queue] += 1
13
+ expiry = parts[4].to_i
14
+ expires_in = ((expiry - t)/(3600.0)).ceil
15
+ expiries[queue][expires_in] += 1
16
+ end
17
+
18
+ counts.to_a.sort_by{|_,v| -v}.each do |q,v|
19
+ puts "------------------------------------------------------------------"
20
+ puts "#{q}: #{v}"
21
+ puts "------------------------------------------------------------------"
22
+ expiries[q].to_a.sort_by{|k,_| -k}.each do |expiry, count|
23
+ printf "%3dh: %6d\n", expiry, count
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ queue_counts = Hash.new { |h,k| h[k] = 0 }
3
+ File.open(ARGV[0]).each_line do |l|
4
+ next if l == "lastgc" || l == "clients-last-seen"
5
+ a = l.split(':')
6
+ if a[0] == "msgid"
7
+ queue_counts[a[1]] += 1
8
+ else
9
+ queue_counts["none"] += 1
10
+ end
11
+ end
12
+ sorted_queues = queue_counts.to_a.sort_by{|a| -a[1]}
13
+ sorted_queues.each do |q,c|
14
+ puts "#{c}:#{q}"
15
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require "redis"
3
+
4
+ redis = Redis.new(:host => "beetle-1.redis.ams2.xing.com", :port => 6379, :db => 0)
5
+
6
+ File.open(ARGV[0]).each_line.each_slice(10) do |s|
7
+ redis.pipelined do
8
+ s.each do |l|
9
+ l.chomp!
10
+ next if l =~ /^beetle:.*$/
11
+ redis.expire(l, 600)
12
+ end
13
+ end
14
+ end
@@ -282,6 +282,12 @@ module Beetle
282
282
  client.stop_publishing
283
283
  end
284
284
 
285
+ test "should delegate setup_queues_and_policies to the publisher instance" do
286
+ client = Client.new
287
+ client.send(:publisher).expects(:setup_queues_and_policies)
288
+ client.setup_queues_and_policies
289
+ end
290
+
285
291
  test "should delegate queue purging to the publisher instance" do
286
292
  client = Client.new
287
293
  client.register_queue(:queue)
@@ -337,17 +343,17 @@ module Beetle
337
343
 
338
344
  test "trying to listen to an unknown queue should raise an exception" do
339
345
  client = Client.new
340
- assert_raises(UnknownQueue) { Client.new.listen_queues([:foobar])}
346
+ assert_raises(UnknownQueue) { client.listen_queues([:foobar])}
341
347
  end
342
348
 
343
349
  test "trying to pause listening on an unknown queue should raise an exception" do
344
350
  client = Client.new
345
- assert_raises(UnknownQueue) { Client.new.pause_listening(:foobar)}
351
+ assert_raises(UnknownQueue) { client.pause_listening(:foobar)}
346
352
  end
347
353
 
348
354
  test "trying to resume listening on an unknown queue should raise an exception" do
349
355
  client = Client.new
350
- assert_raises(UnknownQueue) { Client.new.pause_listening(:foobar)}
356
+ assert_raises(UnknownQueue) { client.pause_listening(:foobar)}
351
357
  end
352
358
 
353
359
  test "should delegate stop_listening to the subscriber instance" do
@@ -451,4 +457,39 @@ module Beetle
451
457
  msg_stub
452
458
  end
453
459
  end
460
+
461
+ class ServerDeduplicationTest < Minitest::Test
462
+ def setup
463
+ @config = Configuration.new
464
+ @config.servers = "localhost:5672,localhost:5672"
465
+ @config.additional_subscription_servers = @config.servers + ",localhost:1234,localhost:1234"
466
+ @client = Client.new(@config)
467
+ end
468
+
469
+ test "duplicates are removed from the server list" do
470
+ assert_equal ["localhost:5672"], @client.servers
471
+ end
472
+
473
+ test "duplicates and servers in the server list are removed from the additional subscription server list" do
474
+ assert_equal ["localhost:1234"], @client.additional_subscription_servers
475
+ end
476
+ end
477
+
478
+ class EmptyServerStringTest < Minitest::Test
479
+ def setup
480
+ @config = Configuration.new
481
+ @config.servers = "localhost:5672,\t,localhost:5672, "
482
+ @config.additional_subscription_servers = @config.servers + ",localhost:1234, , ,,"
483
+ @client = Client.new(@config)
484
+ end
485
+
486
+ test "empty strings are removed from the server list" do
487
+ assert_equal ["localhost:5672"], @client.servers
488
+ end
489
+
490
+ test "empty strings are removed from the additional subscription server list" do
491
+ assert_equal ["localhost:1234"], @client.additional_subscription_servers
492
+ end
493
+ end
494
+
454
495
  end
@@ -75,9 +75,9 @@ module Beetle
75
75
  message.expects(:now).returns(1)
76
76
  message.set_timeout!
77
77
  assert_equal "2", @store.get(message.msg_id, :timeout)
78
- message.expects(:now).returns(2)
78
+ message.expects(:now).returns(2 + Message::TIMEOUT_GRACE_PERIOD)
79
79
  assert !message.timed_out?
80
- message.expects(:now).returns(3)
80
+ message.expects(:now).returns(3 + Message::TIMEOUT_GRACE_PERIOD)
81
81
  assert message.timed_out?
82
82
  end
83
83
 
@@ -86,9 +86,9 @@ module Beetle
86
86
  message.expects(:now).returns(0)
87
87
  message.set_timeout!
88
88
  assert_equal "#{Message::DEFAULT_HANDLER_TIMEOUT}", @store.get(message.msg_id, :timeout)
89
- message.expects(:now).returns(message.timeout)
89
+ message.expects(:now).returns(message.timeout + Message::TIMEOUT_GRACE_PERIOD)
90
90
  assert !message.timed_out?
91
- message.expects(:now).returns(Message::DEFAULT_HANDLER_TIMEOUT + 1)
91
+ message.expects(:now).returns(message.timeout + Message::TIMEOUT_GRACE_PERIOD + 1)
92
92
  assert message.timed_out?
93
93
  end
94
94
 
@@ -304,7 +304,9 @@ module Beetle
304
304
  @client.register_queue('test_queue_1', :exchange => 'test_exchange')
305
305
  @client.register_queue('test_queue_2', :exchange => 'test_exchange')
306
306
  @client.register_queue('test_queue_3', :exchange => 'test_exchange_2')
307
- @pub.expects(:bind_queue!).returns(1).times(3)
307
+ queue = mock("queue")
308
+ queue.expects(:bind).times(3)
309
+ @pub.expects(:declare_queue!).returns(queue).times(3)
308
310
  @pub.send(:bind_queues_for_exchange, 'test_exchange')
309
311
  @pub.send(:bind_queues_for_exchange, 'test_exchange_2')
310
312
  end
@@ -312,8 +314,19 @@ module Beetle
312
314
  test "should not rebind the defined queues for the used exchanges if they already have been bound" do
313
315
  @client.register_queue('test_queue_1', :exchange => 'test_exchange')
314
316
  @client.register_queue('test_queue_2', :exchange => 'test_exchange')
315
- @pub.expects(:bind_queue!).twice
317
+ queue = mock("queue")
318
+ queue.expects(:bind).twice
319
+ @pub.expects(:declare_queue!).returns(queue).twice
320
+ @pub.send(:bind_queues_for_exchange, 'test_exchange')
316
321
  @pub.send(:bind_queues_for_exchange, 'test_exchange')
322
+ end
323
+
324
+ test "should declare queues only once even with many bindings" do
325
+ @client.register_queue('test_queue', :exchange => 'test_exchange')
326
+ @client.register_binding('test_queue', :exchange => 'test_exchange', :key => 'sir-message-a-lot')
327
+ queue = mock("queue")
328
+ queue.expects(:bind).twice
329
+ @pub.expects(:declare_queue!).returns(queue).once
317
330
  @pub.send(:bind_queues_for_exchange, 'test_exchange')
318
331
  end
319
332
 
@@ -342,14 +355,34 @@ module Beetle
342
355
  end
343
356
 
344
357
  test "setting up queues and policies should iterate over all servers" do
345
- @pub.servers = %w(a b)
346
- queue = mock("queue")
358
+ client = Client.new
359
+ client.register_queue("queue")
360
+ pub = Publisher.new(client)
361
+ pub.servers = %w(a b)
362
+
347
363
  s = sequence("setup")
348
- @pub.expects(:set_current_server).with("a").in_sequence(s)
349
- @pub.expects(:queue).with("queue", :create_policies => true).returns(queue).in_sequence(s)
350
- @pub.expects(:set_current_server).with("b").in_sequence(s)
351
- @pub.expects(:queue).with("queue", :create_policies => true).returns(queue).in_sequence(s)
352
- @pub.setup_queues_and_policies(["queue"])
364
+ pub.expects(:set_current_server).with("a").in_sequence(s)
365
+ pub.expects(:queue).with(client.config.beetle_policy_updates_queue_name).in_sequence(s)
366
+ pub.expects(:queue).with("queue").in_sequence(s)
367
+ pub.expects(:set_current_server).with("b").in_sequence(s)
368
+ pub.expects(:queue).with(client.config.beetle_policy_updates_queue_name).in_sequence(s)
369
+ pub.expects(:queue).with("queue").in_sequence(s)
370
+
371
+ pub.setup_queues_and_policies()
372
+ end
373
+
374
+ test "setting up queues and policies should handle ephemeral errors" do
375
+ client = Client.new
376
+ pub = Publisher.new(client)
377
+ client.register_queue("queue")
378
+ pub.servers = %w(a b)
379
+ pub.stubs(:queue).raises(StandardError)
380
+
381
+ s = sequence("setup")
382
+ pub.expects(:set_current_server).with("a").in_sequence(s)
383
+ pub.expects(:set_current_server).with("b").in_sequence(s)
384
+
385
+ pub.setup_queues_and_policies()
353
386
  end
354
387
 
355
388
  test "reports whether it has been throttled" do
@@ -127,7 +127,7 @@ module Beetle
127
127
  @sub = @client.send(:subscriber)
128
128
  end
129
129
 
130
- test "subscribers server list should contain addtional subcription hosts" do
130
+ test "subscribers server list should contain additional subcription hosts" do
131
131
  assert_equal ["localhost:5672", "localhost:1234"], @sub.servers
132
132
  end
133
133
  end
@@ -168,6 +168,17 @@ module Beetle
168
168
  @sub.send(:bind_queues, %W(x y))
169
169
  end
170
170
 
171
+ test "binding queues with many bindings should create it only once" do
172
+ @client.register_queue(:x, :exchange => 'test_exchange')
173
+ @client.register_binding(:x, :exchange => 'test_exchange', :key => 'sir-message-a-lot')
174
+ @client.register_handler(%w(x)){}
175
+ @sub.stubs(:exchange)
176
+ queue = mock("queue")
177
+ queue.expects(:bind).twice
178
+ @sub.expects(:declare_queue!).returns(queue).once
179
+ @sub.send(:bind_queues, %W(x))
180
+ end
181
+
171
182
  test "subscribing to queues should subscribe on all queues" do
172
183
  @client.register_queue(:x)
173
184
  @client.register_queue(:y)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beetle
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 3.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Kaes
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2020-06-21 00:00:00.000000000 Z
15
+ date: 2021-06-01 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: bunny
@@ -62,14 +62,14 @@ dependencies:
62
62
  requirements:
63
63
  - - '='
64
64
  - !ruby/object:Gem::Version
65
- version: 2.3.0
65
+ version: 2.3.2
66
66
  type: :runtime
67
67
  prerelease: false
68
68
  version_requirements: !ruby/object:Gem::Requirement
69
69
  requirements:
70
70
  - - '='
71
71
  - !ruby/object:Gem::Version
72
- version: 2.3.0
72
+ version: 2.3.2
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: amqp
75
75
  requirement: !ruby/object:Gem::Requirement
@@ -337,7 +337,10 @@ files:
337
337
  - lib/beetle/redis_ext.rb
338
338
  - lib/beetle/subscriber.rb
339
339
  - lib/beetle/version.rb
340
+ - script/analyze-expiries
341
+ - script/analyze-redis-keys
340
342
  - script/console
343
+ - script/expire-keys
341
344
  - script/start_rabbit
342
345
  - test/beetle/amqp_gem_behavior_test.rb
343
346
  - test/beetle/base_test.rb
@@ -375,7 +378,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
375
378
  - !ruby/object:Gem::Version
376
379
  version: 1.3.7
377
380
  requirements: []
378
- rubygems_version: 3.0.8
381
+ rubygems_version: 3.2.16
379
382
  signing_key:
380
383
  specification_version: 3
381
384
  summary: High Availability AMQP Messaging with Redundant Queues