beetle 3.0.0.rc1 → 3.0.0.rc2
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 +4 -4
- data/RELEASE_NOTES.rdoc +10 -1
- data/features/step_definitions/redis_auto_failover_steps.rb +1 -1
- data/features/support/beetle_handler +10 -1
- data/lib/beetle/base.rb +32 -11
- data/lib/beetle/client.rb +16 -5
- data/lib/beetle/configuration.rb +11 -0
- data/lib/beetle/dead_lettering.rb +26 -41
- data/lib/beetle/publisher.rb +3 -4
- data/lib/beetle/subscriber.rb +8 -10
- data/lib/beetle/version.rb +1 -1
- data/test/beetle/base_test.rb +50 -0
- data/test/beetle/client_test.rb +13 -14
- data/test/beetle/dead_lettering_test.rb +20 -60
- data/test/beetle/publisher_test.rb +4 -36
- data/test/beetle/subscriber_test.rb +12 -22
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb0489f8580ec11b65b79718c9e76c0f4b26a14af2fbbf471cf4b630fca5fa7d
|
4
|
+
data.tar.gz: 0f3ef313bfaf103eecfd1c1714c5d28b6ec10bc7ce0313e55f0cee6e00f2086a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51aa8c7058c8149daa6c2d69c1ae8a26731957290f06e4bde26a06613e260030f9cb16d286415958db74afcb38bdc74a9ccacc58643ceb25227662a800fada22
|
7
|
+
data.tar.gz: ae1eed22b04f81c612aff1ad88836e57df2db997a923da8f1819d8675093a9533f377e6429928049f9665b5a7548cdbbfb48172ddfbb361bac2e2adbcfba507b
|
data/RELEASE_NOTES.rdoc
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
= Release Notes
|
2
2
|
|
3
|
+
== Version 3.0.0.rc2
|
4
|
+
* change policy setup to be asynchronous, on demand in publisher and
|
5
|
+
consumer. Users have to run a processors to listen to messages and
|
6
|
+
call setup_queue_policies! with the parses JSON paylod of the
|
7
|
+
message.
|
8
|
+
* added dump_expiries command to beetle to dump dediplication store
|
9
|
+
expiry times.
|
10
|
+
* added delete_queue_keys command to beetle to allow deletion of
|
11
|
+
excess dedup store entries for a given queue.
|
3
12
|
|
4
13
|
== Version 3.0.0.rc1
|
5
14
|
* provide client method to setup queues and queue policies.
|
6
15
|
Setting up queue policies on demand in publisher and subscriber is OK
|
7
16
|
for a small number of queues, publishers and subscribers. But the HTTP
|
8
17
|
API of RabbitMQ doesn't scale all that well, so that a large number
|
9
|
-
of
|
18
|
+
of HTTP calls to set up queue policies can in fact crash a server.
|
10
19
|
|
11
20
|
== Version 2.3.2
|
12
21
|
* config server: fixed a race condition when accessing server state.
|
@@ -144,7 +144,7 @@ Then /^the redis master of the beetle handler should be "([^\"]*)"$/ do |redis_n
|
|
144
144
|
Beetle.config.servers = "127.0.0.1:5672" # rabbitmq
|
145
145
|
Beetle.config.logger.level = Logger::INFO
|
146
146
|
client = Beetle::Client.new.configure :auto_delete => true do |config|
|
147
|
-
config.queue(:echo)
|
147
|
+
config.queue(:echo, :lazy => true, :dead_lettering => true)
|
148
148
|
config.message(:echo)
|
149
149
|
end
|
150
150
|
assert_match /#{TestDaemons::Redis[redis_name].ip_with_port}/, client.rpc(:echo, 'nil').second
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "rubygems"
|
4
4
|
require "daemons"
|
5
5
|
require "optparse"
|
6
|
+
require 'json'
|
6
7
|
require File.expand_path("../../lib/beetle", File.dirname(__FILE__))
|
7
8
|
|
8
9
|
tmp_path = File.expand_path("../../tmp", File.dirname(__FILE__))
|
@@ -22,7 +23,7 @@ Daemons.run_proc("beetle_handler", :log_output => true, :dir_mode => :normal, :d
|
|
22
23
|
Beetle.config.logger.level = Logger::INFO
|
23
24
|
|
24
25
|
client = Beetle::Client.new.configure :auto_delete => true do |config|
|
25
|
-
config.queue(:echo)
|
26
|
+
config.queue(:echo, :lazy => true, :dead_lettering => true)
|
26
27
|
config.message(:echo)
|
27
28
|
config.handler(:echo) do |message|
|
28
29
|
begin
|
@@ -32,6 +33,14 @@ Daemons.run_proc("beetle_handler", :log_output => true, :dir_mode => :normal, :d
|
|
32
33
|
"no redis master: exception: #{$!.class}(#{$!}), master_file: '#{master_file_content}'"
|
33
34
|
end
|
34
35
|
end
|
36
|
+
config.handler(Beetle.config.beetle_policy_updates_queue_name) do |message|
|
37
|
+
begin
|
38
|
+
Beetle.config.logger.info "received beetle policy update message': #{message.data}"
|
39
|
+
client.set_queue_policies!(JSON.parse(message.data))
|
40
|
+
rescue => e
|
41
|
+
Beetle.config.logger.error("#{e}:#{e.backtrace.join("\n")}")
|
42
|
+
end
|
43
|
+
end
|
35
44
|
end
|
36
45
|
client.listen do
|
37
46
|
puts "Started beetle handler for system: #{Beetle.config.system_name}"
|
data/lib/beetle/base.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module Beetle
|
2
4
|
# Abstract base class shared by Publisher and Subscriber
|
3
5
|
class Base
|
@@ -12,7 +14,6 @@ module Beetle
|
|
12
14
|
@server = @servers[rand @servers.size]
|
13
15
|
@exchanges = {}
|
14
16
|
@queues = {}
|
15
|
-
@dead_lettering = DeadLettering.new(@client)
|
16
17
|
end
|
17
18
|
|
18
19
|
private
|
@@ -54,13 +55,7 @@ module Beetle
|
|
54
55
|
@queues[@server] ||= {}
|
55
56
|
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
def queue(name, create_policies: false)
|
60
|
-
info = queues[name]
|
61
|
-
if info && create_policies && !info.create_policies
|
62
|
-
queues.delete(name)
|
63
|
-
end
|
58
|
+
def queue(name)
|
64
59
|
queues[name] ||=
|
65
60
|
begin
|
66
61
|
opts = @client.queues[name]
|
@@ -72,11 +67,37 @@ module Beetle
|
|
72
67
|
@client.bindings[name].each do |binding_options|
|
73
68
|
exchange_name = binding_options[:exchange]
|
74
69
|
binding_options = binding_options.slice(*QUEUE_BINDING_KEYS)
|
75
|
-
the_queue = bind_queue!(queue_name, creation_options, exchange_name, binding_options
|
70
|
+
the_queue = bind_queue!(queue_name, creation_options, exchange_name, binding_options)
|
76
71
|
end
|
77
|
-
|
72
|
+
the_queue
|
78
73
|
end
|
79
|
-
|
74
|
+
end
|
75
|
+
|
76
|
+
def bind_dead_letter_queue!(channel, target_queue, creation_keys = {})
|
77
|
+
policy_options = @client.queues[target_queue].slice(:dead_lettering, :lazy)
|
78
|
+
dead_letter_queue_name = "#{target_queue}_dead_letter"
|
79
|
+
if policy_options[:dead_lettering]
|
80
|
+
logger.debug("Beetle: creating dead letter queue #{dead_letter_queue_name} with opts: #{creation_keys.inspect}")
|
81
|
+
channel.queue(dead_letter_queue_name, creation_keys)
|
82
|
+
end
|
83
|
+
return {
|
84
|
+
:queue_name => target_queue,
|
85
|
+
:dead_letter_queue_name => dead_letter_queue_name,
|
86
|
+
:message_ttl => @client.config.dead_lettering_msg_ttl,
|
87
|
+
}.merge(policy_options)
|
88
|
+
end
|
89
|
+
|
90
|
+
# called by <tt>bind_queue!</tt>
|
91
|
+
def publish_policy_options(options)
|
92
|
+
# avoid endless recursion
|
93
|
+
return if options[:queue_name] == @client.config.beetle_policy_updates_queue_name
|
94
|
+
payload = options.merge(:server => @server)
|
95
|
+
logger.debug("Beetle: publishing policy options on #{@server}: #{payload.inspect}")
|
96
|
+
# make sure to declare the queue, so the message does not get lost
|
97
|
+
queue(@client.config.beetle_policy_updates_queue_name)
|
98
|
+
data = payload.to_json
|
99
|
+
opts = Message.publishing_options(:key => @client.config.beetle_policy_updates_routing_key, :persistent => true, :redundant => false)
|
100
|
+
exchange(@client.config.beetle_policy_exchange_name).publish(data, opts)
|
80
101
|
end
|
81
102
|
|
82
103
|
end
|
data/lib/beetle/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module Beetle
|
2
4
|
# This class provides the interface through which messaging is configured for both
|
3
5
|
# message producers and consumers. It keeps references to an instance of a
|
@@ -53,7 +55,17 @@ module Beetle
|
|
53
55
|
@messages = {}
|
54
56
|
@bindings = {}
|
55
57
|
@deduplication_store = DeduplicationStore.new(config)
|
58
|
+
@dead_lettering = DeadLettering.new(config)
|
56
59
|
load_brokers_from_config
|
60
|
+
register_exchange(config.beetle_policy_exchange_name)
|
61
|
+
# make sure dead lettering is false for the policy update queue
|
62
|
+
register_queue(
|
63
|
+
config.beetle_policy_updates_queue_name,
|
64
|
+
:exchange => config.beetle_policy_exchange_name,
|
65
|
+
:key => config.beetle_policy_updates_routing_key,
|
66
|
+
:dead_lettering => false,
|
67
|
+
:lazy => false,
|
68
|
+
)
|
57
69
|
end
|
58
70
|
|
59
71
|
# register an exchange with the given _name_ and a set of _options_:
|
@@ -209,11 +221,6 @@ module Beetle
|
|
209
221
|
publisher.purge(queues)
|
210
222
|
end
|
211
223
|
|
212
|
-
# declares all queues, binds them and creates/updates all policies
|
213
|
-
def setup_queues_and_policies(queues)
|
214
|
-
publisher.setup_queues_and_policies(queues)
|
215
|
-
end
|
216
|
-
|
217
224
|
# start listening to all registered queues. Calls #listen_queues internally
|
218
225
|
# runs the given block before entering the eventmachine loop.
|
219
226
|
def listen(_deprecated_messages=nil, &block)
|
@@ -292,6 +299,10 @@ module Beetle
|
|
292
299
|
@subscriber = nil
|
293
300
|
end
|
294
301
|
|
302
|
+
def set_queue_policies!(message_payload)
|
303
|
+
@dead_lettering.set_queue_policies!(message_payload)
|
304
|
+
end
|
305
|
+
|
295
306
|
private
|
296
307
|
|
297
308
|
def determine_queue_names(queues)
|
data/lib/beetle/configuration.rb
CHANGED
@@ -5,6 +5,14 @@ module Beetle
|
|
5
5
|
class Configuration
|
6
6
|
# system name (used for redis cluster partitioning) (defaults to <tt>system</tt>)
|
7
7
|
attr_accessor :system_name
|
8
|
+
# system_exchange_name is the name of the exchange on which to publish system internal messages, such as
|
9
|
+
# messages to set up queue policies. Whenever a queue is declared in the client, either by publisher or
|
10
|
+
# consumer, a message with the queue policy parameters is sent to this exachange. (defaults to <tt>beetle</tt>)
|
11
|
+
attr_accessor :beetle_policy_exchange_name
|
12
|
+
# Name of the policy update queue
|
13
|
+
attr_accessor :beetle_policy_updates_queue_name
|
14
|
+
# Name of the policy update routing key
|
15
|
+
attr_accessor :beetle_policy_updates_routing_key
|
8
16
|
# default logger (defaults to <tt>Logger.new(log_file)</tt>)
|
9
17
|
attr_accessor :logger
|
10
18
|
# defaults to <tt>STDOUT</tt>
|
@@ -120,6 +128,9 @@ module Beetle
|
|
120
128
|
|
121
129
|
def initialize #:nodoc:
|
122
130
|
self.system_name = "system"
|
131
|
+
self.beetle_policy_exchange_name = "beetle-policies"
|
132
|
+
self.beetle_policy_updates_queue_name = "beetle-policy-updates"
|
133
|
+
self.beetle_policy_updates_routing_key = "beetle.policy.update"
|
123
134
|
|
124
135
|
self.gc_threshold = 1.hour.to_i
|
125
136
|
self.redis_server = "localhost:6379"
|
@@ -5,56 +5,44 @@ module Beetle
|
|
5
5
|
class DeadLettering
|
6
6
|
class FailedRabbitRequest < StandardError; end
|
7
7
|
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :config
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@config = client.config
|
10
|
+
def initialize(config)
|
11
|
+
@config = config
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
def set_queue_policies!(options)
|
15
|
+
# logger.debug "Setting queue policies: #{options.inspect}"
|
16
|
+
options = options.symbolize_keys
|
17
|
+
server = options[:server]
|
18
|
+
target_queue = options[:queue_name]
|
19
|
+
dead_letter_queue_name = options[:dead_letter_queue_name]
|
20
|
+
policy_options = options.slice(:lazy, :dead_lettering)
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
set_queue_policies!(servers, target_queue, policy_options)
|
27
|
-
|
28
|
-
if policy_options[:dead_lettering]
|
29
|
-
logger.info("Beetle: setting #{target_queue} as dead letter queue of #{dead_letter_queue_name} on all servers")
|
30
|
-
set_queue_policies!(
|
31
|
-
servers,
|
32
|
-
dead_letter_queue_name,
|
33
|
-
{ :message_ttl => config.dead_lettering_msg_ttl,
|
34
|
-
:routing_key => target_queue
|
35
|
-
}.merge(policy_options)
|
36
|
-
)
|
37
|
-
end
|
38
|
-
end
|
22
|
+
target_queue_options = policy_options.merge(:routing_key => dead_letter_queue_name)
|
23
|
+
set_queue_policy!(server, target_queue, target_queue_options)
|
39
24
|
|
40
|
-
|
41
|
-
|
25
|
+
dead_letter_queue_options = policy_options.merge(:routing_key => target_queue, :message_ttl => options[:message_ttl])
|
26
|
+
set_queue_policy!(server, dead_letter_queue_name, dead_letter_queue_options)
|
42
27
|
end
|
43
28
|
|
44
29
|
def set_queue_policy!(server, queue_name, options={})
|
30
|
+
logger.info "Setting queue policy: #{server}, #{queue_name}, #{options.inspect}"
|
31
|
+
|
45
32
|
raise ArgumentError.new("server missing") if server.blank?
|
46
33
|
raise ArgumentError.new("queue name missing") if queue_name.blank?
|
47
34
|
|
48
35
|
return unless options[:dead_lettering] || options[:lazy]
|
49
36
|
|
50
37
|
vhost = CGI.escape(config.vhost)
|
38
|
+
# no need to worry that the server has the port 5672. Net:HTTP will take care of this. See below.
|
51
39
|
request_url = URI("http://#{server}/api/policies/#{vhost}/#{queue_name}_policy")
|
52
40
|
request = Net::HTTP::Put.new(request_url)
|
53
41
|
|
54
42
|
# set up queue policy
|
55
43
|
definition = {}
|
56
44
|
if options[:dead_lettering]
|
57
|
-
definition["dead-letter-routing-key"] =
|
45
|
+
definition["dead-letter-routing-key"] = options[:routing_key]
|
58
46
|
definition["dead-letter-exchange"] = ""
|
59
47
|
definition["message-ttl"] = options[:message_ttl] if options[:message_ttl]
|
60
48
|
end
|
@@ -80,19 +68,15 @@ module Beetle
|
|
80
68
|
:ok
|
81
69
|
end
|
82
70
|
|
83
|
-
def dead_letter_routing_key(queue_name, options)
|
84
|
-
options.fetch(:routing_key) { dead_letter_queue_name(queue_name) }
|
85
|
-
end
|
86
|
-
|
87
|
-
def dead_letter_queue_name(queue_name)
|
88
|
-
"#{queue_name}_dead_letter"
|
89
|
-
end
|
90
|
-
|
91
71
|
def run_rabbit_http_request(uri, request, &block)
|
92
72
|
request.basic_auth(config.user, config.password)
|
93
73
|
request["Content-Type"] = "application/json"
|
94
|
-
Net::HTTP.
|
95
|
-
|
74
|
+
http = Net::HTTP.new(uri.hostname, config.api_port)
|
75
|
+
http.read_timeout = config.dead_lettering_read_timeout
|
76
|
+
# don't do this in production:
|
77
|
+
# http.set_debug_output(logger.instance_eval{ @logdev.dev })
|
78
|
+
http.start do |instance|
|
79
|
+
block.call(instance) if block_given?
|
96
80
|
end
|
97
81
|
end
|
98
82
|
|
@@ -103,7 +87,8 @@ module Beetle
|
|
103
87
|
end
|
104
88
|
|
105
89
|
def logger
|
106
|
-
config.logger
|
90
|
+
@config.logger
|
107
91
|
end
|
92
|
+
|
108
93
|
end
|
109
94
|
end
|
data/lib/beetle/publisher.rb
CHANGED
@@ -222,14 +222,13 @@ module Beetle
|
|
222
222
|
end
|
223
223
|
|
224
224
|
# TODO: Refactor, fetch the keys and stuff itself
|
225
|
-
def bind_queue!(queue_name, creation_keys, exchange_name, binding_keys
|
225
|
+
def bind_queue!(queue_name, creation_keys, exchange_name, binding_keys)
|
226
226
|
logger.debug("Beetle: creating queue with opts: #{creation_keys.inspect}")
|
227
227
|
queue = bunny.queue(queue_name, creation_keys)
|
228
|
-
if create_policies
|
229
|
-
@dead_lettering.bind_dead_letter_queues!(bunny, @client.servers, queue_name, creation_keys)
|
230
|
-
end
|
231
228
|
logger.debug("Beetle: binding queue #{queue_name} to #{exchange_name} with opts: #{binding_keys.inspect}")
|
232
229
|
queue.bind(exchange(exchange_name), binding_keys)
|
230
|
+
policy_options = bind_dead_letter_queue!(bunny, queue_name, creation_keys)
|
231
|
+
publish_policy_options(policy_options)
|
233
232
|
queue
|
234
233
|
end
|
235
234
|
|
data/lib/beetle/subscriber.rb
CHANGED
@@ -144,19 +144,19 @@ module Beetle
|
|
144
144
|
callback = create_subscription_callback(queue_name, amqp_queue_name, handler, opts)
|
145
145
|
keys = opts.slice(*SUBSCRIPTION_KEYS).merge(:key => "#", :ack => true)
|
146
146
|
logger.debug "Beetle: subscribing to queue #{amqp_queue_name} with key # on server #{@server}"
|
147
|
-
queues[queue_name].
|
147
|
+
queues[queue_name].subscribe(keys, &callback)
|
148
148
|
subscriptions[queue_name] = [keys, callback]
|
149
149
|
end
|
150
150
|
|
151
151
|
def pause(queue_name)
|
152
|
-
return unless queues[queue_name].
|
153
|
-
queues[queue_name].
|
152
|
+
return unless queues[queue_name].subscribed?
|
153
|
+
queues[queue_name].unsubscribe
|
154
154
|
end
|
155
155
|
|
156
156
|
def resume(queue_name)
|
157
|
-
return if queues[queue_name].
|
157
|
+
return if queues[queue_name].subscribed?
|
158
158
|
keys, callback = subscriptions[queue_name]
|
159
|
-
queues[queue_name].
|
159
|
+
queues[queue_name].subscribe(keys, &callback)
|
160
160
|
end
|
161
161
|
|
162
162
|
def create_subscription_callback(queue_name, amqp_queue_name, handler, opts)
|
@@ -204,12 +204,10 @@ module Beetle
|
|
204
204
|
channel.__send__(opts[:type], name, opts.slice(*EXCHANGE_CREATION_KEYS))
|
205
205
|
end
|
206
206
|
|
207
|
-
def bind_queue!(queue_name, creation_keys, exchange_name, binding_keys
|
207
|
+
def bind_queue!(queue_name, creation_keys, exchange_name, binding_keys)
|
208
208
|
queue = channel.queue(queue_name, creation_keys)
|
209
|
-
|
210
|
-
|
211
|
-
@dead_lettering.bind_dead_letter_queues!(channel, target_servers, queue_name, creation_keys)
|
212
|
-
end
|
209
|
+
policy_options = bind_dead_letter_queue!(channel, queue_name, creation_keys)
|
210
|
+
publish_policy_options(policy_options)
|
213
211
|
exchange = exchange(exchange_name)
|
214
212
|
queue.bind(exchange, binding_keys)
|
215
213
|
queue
|
data/lib/beetle/version.rb
CHANGED
data/test/beetle/base_test.rb
CHANGED
@@ -53,4 +53,54 @@ module Beetle
|
|
53
53
|
assert_equal "goofy:123", @bs.send(:server_from_settings, {:host => "goofy", :port => 123})
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
class BindDeadLetterQueuesTest < Minitest::Test
|
58
|
+
def setup
|
59
|
+
@queue_name = "QUEUE_NAME"
|
60
|
+
@config = Configuration.new
|
61
|
+
@client = Client.new @config
|
62
|
+
@bs = Base.new(@client)
|
63
|
+
@config.logger = Logger.new("/dev/null")
|
64
|
+
end
|
65
|
+
|
66
|
+
test "it does not call out to rabbit if neither dead lettering nor lazy queues are enabled" do
|
67
|
+
@client.register_queue(@queue_name, :dead_lettering => false, :lazy => false)
|
68
|
+
channel = stub('channel')
|
69
|
+
expected_options = {
|
70
|
+
:queue_name => "QUEUE_NAME",
|
71
|
+
:dead_letter_queue_name=>"QUEUE_NAME_dead_letter",
|
72
|
+
:message_ttl => 1000,
|
73
|
+
:dead_lettering => false,
|
74
|
+
:lazy => false
|
75
|
+
}
|
76
|
+
assert_equal expected_options, @bs.__send__(:bind_dead_letter_queue!, channel, @queue_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
test "creates and connects the dead letter queue via policies when enabled" do
|
80
|
+
@client.register_queue(@queue_name, :dead_lettering => true, :lazy => true)
|
81
|
+
|
82
|
+
channel = stub('channel')
|
83
|
+
channel.expects(:queue).with("#{@queue_name}_dead_letter", {})
|
84
|
+
|
85
|
+
expected_options = {
|
86
|
+
:queue_name => "QUEUE_NAME",
|
87
|
+
:dead_letter_queue_name=>"QUEUE_NAME_dead_letter",
|
88
|
+
:message_ttl => 1000,
|
89
|
+
:dead_lettering => true,
|
90
|
+
:lazy => true
|
91
|
+
}
|
92
|
+
assert_equal expected_options, @bs.__send__(:bind_dead_letter_queue!, channel, @queue_name)
|
93
|
+
end
|
94
|
+
|
95
|
+
test "publish_policy_options declares the beetle policy updates queue and publishes the options" do
|
96
|
+
options = { :lazy => true, :dead_lettering => true }
|
97
|
+
@bs.logger.stubs(:debug)
|
98
|
+
@bs.expects(:queue).with(@client.config.beetle_policy_updates_queue_name)
|
99
|
+
exchange = mock("exchange")
|
100
|
+
exchange.expects(:publish)
|
101
|
+
@bs.expects(:exchange).with(@client.config.beetle_policy_exchange_name).returns(exchange)
|
102
|
+
@bs.__send__(:publish_policy_options, options)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
56
106
|
end
|
data/test/beetle/client_test.rb
CHANGED
@@ -15,20 +15,20 @@ module Beetle
|
|
15
15
|
assert_equal [], @client.additional_subscription_servers
|
16
16
|
end
|
17
17
|
|
18
|
-
test "should have
|
19
|
-
|
18
|
+
test "should have only the system exchange" do
|
19
|
+
assert_equal ["beetle-policies"], @client.exchanges.keys
|
20
20
|
end
|
21
21
|
|
22
|
-
test "should have
|
23
|
-
|
22
|
+
test "should have only the beetle policy queue" do
|
23
|
+
assert_equal ["beetle-policy-updates"], @client.queues.keys
|
24
24
|
end
|
25
25
|
|
26
26
|
test "should have no messages" do
|
27
|
-
|
27
|
+
assert_equal [], @client.messages.keys
|
28
28
|
end
|
29
29
|
|
30
|
-
test "should have
|
31
|
-
|
30
|
+
test "should have only one binding for the beetle policy updates" do
|
31
|
+
assert_equal ["beetle-policy-updates"], @client.bindings.keys
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -308,13 +308,6 @@ module Beetle
|
|
308
308
|
assert_equal "ha!", client.purge(:queue1, :queue2)
|
309
309
|
end
|
310
310
|
|
311
|
-
test "should delegate queue setup to the publisher instance" do
|
312
|
-
client = Client.new
|
313
|
-
client.register_queue(:queue)
|
314
|
-
client.send(:publisher).expects(:setup_queues_and_policies).with(["queue"]).returns("ha!")
|
315
|
-
assert_equal "ha!", client.setup_queues_and_policies(["queue"])
|
316
|
-
end
|
317
|
-
|
318
311
|
test "should delegate rpc calls to the publisher instance" do
|
319
312
|
client = Client.new
|
320
313
|
client.register_message("deadletter")
|
@@ -363,6 +356,12 @@ module Beetle
|
|
363
356
|
client.stop_listening
|
364
357
|
end
|
365
358
|
|
359
|
+
test "should delegate set_queue_policies! to the dead lettering instance" do
|
360
|
+
client = Client.new
|
361
|
+
client.instance_variable_get(:@dead_lettering).expects(:set_queue_policies!)
|
362
|
+
client.set_queue_policies!({})
|
363
|
+
end
|
364
|
+
|
366
365
|
test "should delegate pause_listening to the subscriber instance" do
|
367
366
|
client = Client.new
|
368
367
|
client.register_queue(:test)
|
@@ -1,34 +1,13 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
2
|
|
3
3
|
module Beetle
|
4
|
-
class SetDeadLetteringsTest < Minitest::Test
|
5
|
-
def setup
|
6
|
-
@config = Configuration.new
|
7
|
-
@client = Client.new @config
|
8
|
-
@dead_lettering = DeadLettering.new(@client)
|
9
|
-
@config.dead_lettering_enabled = true
|
10
|
-
end
|
11
|
-
|
12
|
-
test "creates a dead letter queue for each server" do
|
13
|
-
servers = %w(a b)
|
14
|
-
|
15
|
-
@dead_lettering.expects(:set_queue_policy!).
|
16
|
-
with("a", "QUEUE_NAME", :message_ttl => 10000)
|
17
|
-
@dead_lettering.expects(:set_queue_policy!).
|
18
|
-
with("b", "QUEUE_NAME", :message_ttl => 10000)
|
19
|
-
|
20
|
-
@dead_lettering.set_queue_policies!(servers, "QUEUE_NAME", :message_ttl => 10000)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
4
|
class SetDeadLetterPolicyTest < Minitest::Test
|
25
5
|
def setup
|
26
6
|
@server = "localhost:15672"
|
27
7
|
@queue_name = "QUEUE_NAME"
|
28
8
|
@config = Configuration.new
|
29
|
-
@client = Client.new @config
|
30
9
|
@config.logger = Logger.new("/dev/null")
|
31
|
-
@dead_lettering = DeadLettering.new(@
|
10
|
+
@dead_lettering = DeadLettering.new(@config)
|
32
11
|
end
|
33
12
|
|
34
13
|
test "raises exception when queue name wasn't specified" do
|
@@ -43,6 +22,22 @@ module Beetle
|
|
43
22
|
end
|
44
23
|
end
|
45
24
|
|
25
|
+
test "set_queue_policies! calls set_queue_policy for both target queue and dead letter queue" do
|
26
|
+
options = {
|
27
|
+
:server => "server", :lazy => true, :dead_lettering => true,
|
28
|
+
:queue_name => "QUEUE_NAME", :dead_letter_queue_name => "QUEUE_NAME_dead_letter",
|
29
|
+
:message_ttl => 10000
|
30
|
+
}
|
31
|
+
@dead_lettering.expects(:set_queue_policy!).with("server", "QUEUE_NAME",
|
32
|
+
:lazy => true, :dead_lettering => true,
|
33
|
+
:routing_key => "QUEUE_NAME_dead_letter")
|
34
|
+
@dead_lettering.expects(:set_queue_policy!).with("server", "QUEUE_NAME_dead_letter",
|
35
|
+
:lazy => true, :dead_lettering => true,
|
36
|
+
:routing_key => "QUEUE_NAME",
|
37
|
+
:message_ttl => 10000)
|
38
|
+
@dead_lettering.set_queue_policies!(options)
|
39
|
+
end
|
40
|
+
|
46
41
|
test "creates a policy by posting to the rabbitmq if dead lettering is enabled" do
|
47
42
|
stub_request(:put, "http://localhost:15672/api/policies/%2F/QUEUE_NAME_policy")
|
48
43
|
.with(basic_auth: ['guest', 'guest'])
|
@@ -56,7 +51,7 @@ module Beetle
|
|
56
51
|
}}.to_json)
|
57
52
|
.to_return(:status => 204)
|
58
53
|
|
59
|
-
@dead_lettering.set_queue_policy!(@server, @queue_name, :lazy => false, :dead_lettering => true)
|
54
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name, :lazy => false, :dead_lettering => true, :routing_key => "QUEUE_NAME_dead_letter")
|
60
55
|
end
|
61
56
|
|
62
57
|
test "creates a policy by posting to the rabbitmq if lazy queues are enabled" do
|
@@ -98,7 +93,7 @@ module Beetle
|
|
98
93
|
}}.to_json)
|
99
94
|
.to_return(:status => 204)
|
100
95
|
|
101
|
-
@dead_lettering.set_queue_policy!(@server, @queue_name, :dead_lettering => true, :message_ttl => 10000)
|
96
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name, :dead_lettering => true, :message_ttl => 10000, :routing_key => "QUEUE_NAME_dead_letter")
|
102
97
|
end
|
103
98
|
|
104
99
|
test "properly encodes the vhost from the configuration" do
|
@@ -116,42 +111,7 @@ module Beetle
|
|
116
111
|
|
117
112
|
@config.vhost = "foo/"
|
118
113
|
|
119
|
-
@dead_lettering.set_queue_policy!(@server, @queue_name, :dead_lettering => true)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
class BindDeadLetterQueuesTest < Minitest::Test
|
124
|
-
def setup
|
125
|
-
@queue_name = "QUEUE_NAME"
|
126
|
-
@config = Configuration.new
|
127
|
-
@client = Client.new @config
|
128
|
-
@config.logger = Logger.new("/dev/null")
|
129
|
-
@dead_lettering = DeadLettering.new(@client)
|
130
|
-
@servers = ["localhost:55672"]
|
131
|
-
end
|
132
|
-
|
133
|
-
test "is does not call out to rabbit if neither dead lettering nor lazy queues are enabled" do
|
134
|
-
@client.register_queue(@queue_name, :dead_lettering => false, :lazy => false)
|
135
|
-
channel = stub('channel')
|
136
|
-
@dead_lettering.expects(:run_rabbit_http_request).never
|
137
|
-
@dead_lettering.bind_dead_letter_queues!(channel, @servers, @queue_name)
|
138
|
-
end
|
139
|
-
|
140
|
-
test "creates and connects the dead letter queue via policies when enabled" do
|
141
|
-
@client.register_queue(@queue_name, :dead_lettering => true, :lazy => false)
|
142
|
-
|
143
|
-
channel = stub('channel')
|
144
|
-
|
145
|
-
channel.expects(:queue).with("#{@queue_name}_dead_letter", {})
|
146
|
-
@dead_lettering.expects(:set_queue_policies!).with(@servers, @queue_name, :dead_lettering => true, :lazy => false)
|
147
|
-
@dead_lettering.expects(:set_queue_policies!).with(@servers, "#{@queue_name}_dead_letter",
|
148
|
-
:routing_key => @queue_name,
|
149
|
-
:message_ttl => 1000,
|
150
|
-
:dead_lettering => true,
|
151
|
-
:lazy => false
|
152
|
-
)
|
153
|
-
|
154
|
-
@dead_lettering.bind_dead_letter_queues!(channel, @servers, @queue_name)
|
114
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name, :dead_lettering => true, :routing_key => "QUEUE_NAME_dead_letter")
|
155
115
|
end
|
156
116
|
end
|
157
117
|
end
|
@@ -263,7 +263,8 @@ module Beetle
|
|
263
263
|
|
264
264
|
class PublisherQueueManagementTest < Minitest::Test
|
265
265
|
def setup
|
266
|
-
@
|
266
|
+
@config = Configuration.new
|
267
|
+
@client = Client.new(@config)
|
267
268
|
@pub = Publisher.new(@client)
|
268
269
|
end
|
269
270
|
|
@@ -275,48 +276,15 @@ module Beetle
|
|
275
276
|
test "binding a queue should create it using the config and bind it to the exchange with the name specified" do
|
276
277
|
@client.register_queue("some_queue", :exchange => "some_exchange", :key => "haha.#", :arguments => {"foo" => "fighter"})
|
277
278
|
@pub.expects(:exchange).with("some_exchange").returns(:the_exchange)
|
278
|
-
|
279
|
-
q.expects(:bind).with(:the_exchange, {:key => "haha.#"})
|
280
|
-
m = mock("Bunny")
|
281
|
-
m.expects(:queue).with("some_queue", :durable => true, :passive => false, :auto_delete => false, :exclusive => false, :arguments => {"foo" => "fighter"}).returns(q)
|
282
|
-
@pub.expects(:bunny).returns(m)
|
283
|
-
|
284
|
-
@pub.send(:queue, "some_queue")
|
285
|
-
assert_equal Base::QueueInfo.new(q, false), @pub.send(:queues)["some_queue"]
|
286
|
-
end
|
287
|
-
|
288
|
-
test "binding a queue should create it using the config and bind it to the exchange with the name specified and setup policies if requested" do
|
289
|
-
@client.register_queue("some_queue", :exchange => "some_exchange", :key => "haha.#", :arguments => {"foo" => "fighter"})
|
290
|
-
@pub.expects(:exchange).with("some_exchange").returns(:the_exchange)
|
279
|
+
@pub.expects(:publish_policy_options)
|
291
280
|
q = mock("queue")
|
292
281
|
q.expects(:bind).with(:the_exchange, {:key => "haha.#"})
|
293
282
|
m = mock("Bunny")
|
294
283
|
m.expects(:queue).with("some_queue", :durable => true, :passive => false, :auto_delete => false, :exclusive => false, :arguments => {"foo" => "fighter"}).returns(q)
|
295
284
|
@pub.expects(:bunny).returns(m).twice
|
296
285
|
|
297
|
-
@pub.send(:queue, "some_queue", create_policies: true)
|
298
|
-
assert_equal Base::QueueInfo.new(q, true), @pub.send(:queues)["some_queue"]
|
299
|
-
end
|
300
|
-
|
301
|
-
test "binding a queue with create_plocies: true after having already declared it with create_plocies: false creates the policies" do
|
302
|
-
@client.register_queue("some_queue", :exchange => "some_exchange", :key => "haha.#", :arguments => {"foo" => "fighter"})
|
303
|
-
@pub.expects(:exchange).with("some_exchange").returns(:the_exchange)
|
304
|
-
q = mock("queue")
|
305
|
-
q.expects(:bind).with(:the_exchange, {:key => "haha.#"})
|
306
|
-
m = mock("Bunny")
|
307
|
-
m.expects(:queue).with("some_queue", :durable => true, :passive => false, :auto_delete => false, :exclusive => false, :arguments => {"foo" => "fighter"}).returns(q)
|
308
|
-
@pub.expects(:bunny).returns(m)
|
309
|
-
|
310
286
|
@pub.send(:queue, "some_queue")
|
311
|
-
assert_equal
|
312
|
-
|
313
|
-
@pub.expects(:exchange).with("some_exchange").returns(:the_exchange)
|
314
|
-
q.expects(:bind).with(:the_exchange, {:key => "haha.#"})
|
315
|
-
m.expects(:queue).with("some_queue", :durable => true, :passive => false, :auto_delete => false, :exclusive => false, :arguments => {"foo" => "fighter"}).returns(q)
|
316
|
-
@pub.expects(:bunny).returns(m).twice
|
317
|
-
|
318
|
-
@pub.send(:queue, "some_queue", create_policies: true)
|
319
|
-
assert_equal Base::QueueInfo.new(q, true), @pub.send(:queues)["some_queue"]
|
287
|
+
assert_equal q, @pub.send(:queues)["some_queue"]
|
320
288
|
end
|
321
289
|
|
322
290
|
test "should bind the defined queues for the used exchanges when publishing" do
|
@@ -89,7 +89,7 @@ module Beetle
|
|
89
89
|
q = mock("queue a")
|
90
90
|
q.expects(:subscribed?).returns(true)
|
91
91
|
q.expects(:unsubscribe)
|
92
|
-
@sub.stubs(:queues).returns(
|
92
|
+
@sub.stubs(:queues).returns("a" => q)
|
93
93
|
@sub.__send__(:pause, "a")
|
94
94
|
end
|
95
95
|
|
@@ -97,7 +97,7 @@ module Beetle
|
|
97
97
|
q = mock("queue a")
|
98
98
|
q.expects(:subscribed?).returns(false)
|
99
99
|
q.expects(:unsubscribe).never
|
100
|
-
@sub.stubs(:queues).returns(
|
100
|
+
@sub.stubs(:queues).returns("a" => q)
|
101
101
|
@sub.__send__(:pause, "a")
|
102
102
|
end
|
103
103
|
|
@@ -105,7 +105,7 @@ module Beetle
|
|
105
105
|
q = mock("queue a")
|
106
106
|
q.expects(:subscribed?).returns(false)
|
107
107
|
q.expects(:subscribe)
|
108
|
-
@sub.stubs(:queues).returns(
|
108
|
+
@sub.stubs(:queues).returns("a" => q)
|
109
109
|
@sub.__send__(:resume, "a")
|
110
110
|
end
|
111
111
|
|
@@ -113,7 +113,7 @@ module Beetle
|
|
113
113
|
q = mock("queue a")
|
114
114
|
q.expects(:subscribed?).returns(true)
|
115
115
|
q.expects(:subscribe).never
|
116
|
-
@sub.stubs(:queues).returns(
|
116
|
+
@sub.stubs(:queues).returns("a" => q)
|
117
117
|
@sub.__send__(:resume, "a")
|
118
118
|
end
|
119
119
|
|
@@ -146,27 +146,17 @@ module Beetle
|
|
146
146
|
test "binding a queue should create it using the config and bind it to the exchange with the name specified" do
|
147
147
|
@client.register_queue("some_queue", "durable" => true, "exchange" => "some_exchange", "key" => "haha.#", "arguments" => {"schmu" => 5})
|
148
148
|
@sub.expects(:exchange).with("some_exchange").returns(:the_exchange)
|
149
|
+
@sub.expects(:publish_policy_options)
|
149
150
|
q = mock("queue")
|
150
151
|
q.expects(:bind).with(:the_exchange, {:key => "haha.#"})
|
151
|
-
|
152
|
-
|
153
|
-
|
152
|
+
channel = mock("MQ")
|
153
|
+
creation_opts = {:durable => true, :passive => false, :auto_delete => false, :exclusive => false, :arguments => {"schmu" => 5}}
|
154
|
+
channel.expects(:queue).with("some_queue", creation_opts).returns(q)
|
155
|
+
@sub.expects(:channel).returns(channel).twice
|
156
|
+
@sub.expects(:bind_dead_letter_queue!).with(channel, "some_queue", creation_opts)
|
154
157
|
|
155
158
|
@sub.send(:queue, "some_queue")
|
156
|
-
assert_equal q, @sub.send(:queues)["some_queue"]
|
157
|
-
end
|
158
|
-
|
159
|
-
test "binding a queue should create it using the config and bind it to the exchange with the name specified and create policies if requested" do
|
160
|
-
@client.register_queue("some_queue", "durable" => true, "exchange" => "some_exchange", "key" => "haha.#", "arguments" => {"schmu" => 5})
|
161
|
-
@sub.expects(:exchange).with("some_exchange").returns(:the_exchange)
|
162
|
-
q = mock("queue")
|
163
|
-
q.expects(:bind).with(:the_exchange, {:key => "haha.#"})
|
164
|
-
m = mock("MQ")
|
165
|
-
m.expects(:queue).with("some_queue", :durable => true, :passive => false, :auto_delete => false, :exclusive => false, :arguments => {"schmu" => 5}).returns(q)
|
166
|
-
@sub.expects(:channel).returns(m).twice
|
167
|
-
|
168
|
-
@sub.send(:queue, "some_queue", create_policies: true)
|
169
|
-
assert_equal q, @sub.send(:queues)["some_queue"].queue
|
159
|
+
assert_equal q, @sub.send(:queues)["some_queue"]
|
170
160
|
end
|
171
161
|
|
172
162
|
test "binding queues should bind all queues" do
|
@@ -370,7 +360,7 @@ module Beetle
|
|
370
360
|
q = mock("QUEUE")
|
371
361
|
subscription_options = {:ack => true, :key => "#"}
|
372
362
|
q.expects(:subscribe).with(subscription_options).yields(header, "foo")
|
373
|
-
@sub.expects(:queues).returns(
|
363
|
+
@sub.expects(:queues).returns("some_queue" => q).once
|
374
364
|
@sub.send(:subscribe, "some_queue")
|
375
365
|
assert block_called
|
376
366
|
assert @sub.__send__(:has_subscription?, "some_queue")
|
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.0.0.
|
4
|
+
version: 3.0.0.rc2
|
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: 2019-
|
15
|
+
date: 2019-05-13 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: bunny
|