beetle 3.4.0 → 3.5.1

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