sanger_warren 0.2.0.rc1 → 0.4.0
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/.yardopts +3 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile.lock +18 -18
- data/README.md +29 -0
- data/lefthook.yml +1 -1
- data/lib/warren/app/cli.rb +1 -0
- data/lib/warren/app/config.rb +10 -0
- data/lib/warren/app/consumer.rb +12 -0
- data/lib/warren/app/consumer_add.rb +15 -6
- data/lib/warren/app/consumer_start.rb +15 -0
- data/lib/warren/app/exchange_config.rb +13 -0
- data/lib/warren/callback/broadcast_with_warren.rb +1 -1
- data/lib/warren/config/consumers.rb +24 -2
- data/lib/warren/delay_exchange.rb +85 -0
- data/lib/warren/den.rb +23 -7
- data/lib/warren/fox.rb +7 -3
- data/lib/warren/framework_adaptor/rails_adaptor.rb +52 -0
- data/lib/warren/handler.rb +16 -0
- data/lib/warren/handler/broadcast.rb +31 -9
- data/lib/warren/handler/log.rb +8 -0
- data/lib/warren/handler/test.rb +9 -10
- data/lib/warren/message.rb +2 -0
- data/lib/warren/message/full.rb +20 -0
- data/lib/warren/message/short.rb +8 -0
- data/lib/warren/message/simple.rb +15 -0
- data/lib/warren/subscriber/base.rb +33 -5
- data/lib/warren/subscription.rb +13 -6
- data/lib/warren/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdb0a5a3497a84b68ad0be1b01bfb80167452b6a3209badf12d5bba8cb9e3390
|
4
|
+
data.tar.gz: 8271f438b3ad81320a3a1530292edefdc66a40b4e858a908568b151f22a3ae3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 517e0e58370945acc610974a4657fc424ee2483d4e794ce74ebe026580aad2fa47e23643304cedc6df99e6354e1a06a430387f70da71c877ec80bc022c7b3118
|
7
|
+
data.tar.gz: 6bbdfe62aa22ea3bc42106800c79fe1f8b3a16a5d50898479ce17ab1b1bf3854ce02a65a83d641d07295dfb138df5ae2d43a60762a6f82c40f10cc1054877579
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,37 @@ Unreleased section to make new releases easy.
|
|
5
5
|
|
6
6
|
## [Unreleased]
|
7
7
|
|
8
|
+
## [0.4.0] - 2021-06-09
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
- Ensured backwards compatibility with 0.2.0
|
13
|
+
|
14
|
+
## [0.3.0] - 2021-06-04
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Added support for delay exchanges to process messages after a fixed delay
|
19
|
+
- Increased documentation
|
20
|
+
- Added Warren::Message::Simple for wrapping just routing key and payload.
|
21
|
+
- Added optional worker_count to warren_consumers.yml to control number of worker threads
|
22
|
+
|
23
|
+
### Removed
|
24
|
+
|
25
|
+
- Warren::Handler::Test and Warren::Handler::Test::Channel no loner respond to
|
26
|
+
`add_exchange`. These methods were undocumented, and unused internally.
|
27
|
+
|
28
|
+
## Changed
|
29
|
+
|
30
|
+
- Messages must now implement `#headers`, although simply returning an empty
|
31
|
+
hash is sufficient.
|
32
|
+
See {Warren::Message::Simple#headers} for example
|
33
|
+
- Subscriber templates now use the path 'app/warren/subscriber' rather than
|
34
|
+
'app/warren/subscribers' to correctly match class namespacing.
|
35
|
+
- 3 consumer worker threads will be spun up by default
|
36
|
+
|
37
|
+
## [0.2.0]
|
38
|
+
|
8
39
|
### Added
|
9
40
|
|
10
41
|
- Added railties to automatically initialize and configure Warren in rails apps.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sanger_warren (0.
|
4
|
+
sanger_warren (0.4.0)
|
5
5
|
bunny (~> 2.17.0)
|
6
6
|
connection_pool (~> 2.2.0)
|
7
7
|
multi_json (~> 1.0)
|
@@ -10,13 +10,13 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
activemodel (5.2.
|
14
|
-
activesupport (= 5.2.
|
15
|
-
activerecord (5.2.
|
16
|
-
activemodel (= 5.2.
|
17
|
-
activesupport (= 5.2.
|
13
|
+
activemodel (5.2.6)
|
14
|
+
activesupport (= 5.2.6)
|
15
|
+
activerecord (5.2.6)
|
16
|
+
activemodel (= 5.2.6)
|
17
|
+
activesupport (= 5.2.6)
|
18
18
|
arel (>= 9.0)
|
19
|
-
activesupport (5.2.
|
19
|
+
activesupport (5.2.6)
|
20
20
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
21
21
|
i18n (>= 0.7, < 2)
|
22
22
|
minitest (~> 5.1)
|
@@ -28,24 +28,24 @@ GEM
|
|
28
28
|
amq-protocol (~> 2.3, >= 2.3.1)
|
29
29
|
coderay (1.1.3)
|
30
30
|
concurrent-ruby (1.1.8)
|
31
|
-
connection_pool (2.2.
|
31
|
+
connection_pool (2.2.5)
|
32
32
|
diff-lcs (1.4.4)
|
33
|
-
docile (1.
|
33
|
+
docile (1.4.0)
|
34
34
|
i18n (1.8.10)
|
35
35
|
concurrent-ruby (~> 1.0)
|
36
36
|
method_source (1.0.0)
|
37
37
|
minitest (5.14.4)
|
38
38
|
multi_json (1.15.0)
|
39
39
|
parallel (1.20.1)
|
40
|
-
parser (3.0.
|
40
|
+
parser (3.0.1.1)
|
41
41
|
ast (~> 2.4.1)
|
42
|
-
pry (0.14.
|
42
|
+
pry (0.14.1)
|
43
43
|
coderay (~> 1.1)
|
44
44
|
method_source (~> 1.0)
|
45
45
|
rainbow (3.0.0)
|
46
46
|
rake (13.0.3)
|
47
47
|
regexp_parser (2.1.1)
|
48
|
-
rexml (3.2.
|
48
|
+
rexml (3.2.5)
|
49
49
|
rspec (3.10.0)
|
50
50
|
rspec-core (~> 3.10.0)
|
51
51
|
rspec-expectations (~> 3.10.0)
|
@@ -59,20 +59,20 @@ GEM
|
|
59
59
|
diff-lcs (>= 1.2.0, < 2.0)
|
60
60
|
rspec-support (~> 3.10.0)
|
61
61
|
rspec-support (3.10.2)
|
62
|
-
rubocop (1.
|
62
|
+
rubocop (1.15.0)
|
63
63
|
parallel (~> 1.10)
|
64
64
|
parser (>= 3.0.0.0)
|
65
65
|
rainbow (>= 2.2.2, < 4.0)
|
66
66
|
regexp_parser (>= 1.8, < 3.0)
|
67
67
|
rexml
|
68
|
-
rubocop-ast (>= 1.
|
68
|
+
rubocop-ast (>= 1.5.0, < 2.0)
|
69
69
|
ruby-progressbar (~> 1.7)
|
70
70
|
unicode-display_width (>= 1.4.0, < 3.0)
|
71
|
-
rubocop-ast (1.
|
72
|
-
parser (>=
|
71
|
+
rubocop-ast (1.5.0)
|
72
|
+
parser (>= 3.0.1.1)
|
73
73
|
rubocop-rake (0.5.1)
|
74
74
|
rubocop
|
75
|
-
rubocop-rspec (2.
|
75
|
+
rubocop-rspec (2.3.0)
|
76
76
|
rubocop (~> 1.0)
|
77
77
|
rubocop-ast (>= 1.1.0)
|
78
78
|
ruby-progressbar (1.11.0)
|
@@ -81,7 +81,7 @@ GEM
|
|
81
81
|
simplecov-html (~> 0.11)
|
82
82
|
simplecov_json_formatter (~> 0.1)
|
83
83
|
simplecov-html (0.12.3)
|
84
|
-
simplecov_json_formatter (0.1.
|
84
|
+
simplecov_json_formatter (0.1.3)
|
85
85
|
thor (1.1.0)
|
86
86
|
thread_safe (0.3.6)
|
87
87
|
tzinfo (1.2.9)
|
data/README.md
CHANGED
@@ -32,6 +32,13 @@ If using with a Rails app, you can simply run `bundle exec warren config` to
|
|
32
32
|
help generate a warren config file. Warren will automatically be initialize
|
33
33
|
on Rails start-up.
|
34
34
|
|
35
|
+
In rails 5 you will need to add the following to your `config/application.rb`
|
36
|
+
to ensure the auto-loader can find the subscribers.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
config.autoload_paths += %W{#{Rails.root}/app}
|
40
|
+
```
|
41
|
+
|
35
42
|
### Handler types
|
36
43
|
|
37
44
|
In development mode, warren is usually configured to log to the console only. If
|
@@ -97,6 +104,28 @@ These options can be over-ridden in the warren_consumers.yml file if necessary.
|
|
97
104
|
If you wish to completely disable dead-letter queue configuration, such as when
|
98
105
|
using policies, then you can set dead_letters to false.
|
99
106
|
|
107
|
+
#### Delayed messaged
|
108
|
+
|
109
|
+
Warren uses a delay exchange / queue approach for delaying the redelivery of messages.
|
110
|
+
We don't currently support the
|
111
|
+
[delayed message exchange plugin](https://github.com/rabbitmq/rabbitmq-delayed-message-exchange)
|
112
|
+
|
113
|
+
The way this works:
|
114
|
+
|
115
|
+
- Consumers with a delay option will automatically register an exchange and a queue
|
116
|
+
- By default both of these will be named '<consumer_name>.delay' although you can
|
117
|
+
override this by updating `warren_consumers.yml`
|
118
|
+
- The '<consumer_name>.delay' queue will have a time-to-live (ttl) set, after
|
119
|
+
which any messages on the queue will be dead-lettered
|
120
|
+
- The dead-letter exchange for this queue is configured to an empty string `''`
|
121
|
+
which corresponds to the 'default exchange', and the routing key is set to
|
122
|
+
match the original queue name.
|
123
|
+
- This exchange is a little bit special, and automatically routes messages to
|
124
|
+
any queue on the host where the queue name matches the message routing key.
|
125
|
+
|
126
|
+
This approach avoids repeat delivery of the message to any other queues bound
|
127
|
+
to the original exchange, and avoids the need for further exchanges or bindings.
|
128
|
+
|
100
129
|
### Running consumers
|
101
130
|
|
102
131
|
To run all configure consumers use:
|
data/lefthook.yml
CHANGED
@@ -7,7 +7,7 @@ pre-commit:
|
|
7
7
|
commands:
|
8
8
|
rubocop:
|
9
9
|
glob: '{*.{rb,arb,axlsx,builder,fcgi,gemfile,gemspec,god,jb,jbuilder,mspec,opal,pluginspec,podspec,rabl,rake,rbuild,rbw,rbx,ru,ruby,spec,thor,watchr},.irbrc,.pryrc,.simplecov,buildfile,Appraisals,Berksfile,Brewfile,Buildfile,Capfile,Cheffile,Dangerfile,Deliverfile,Fastfile,*Fastfile,Gemfile,Guardfile,Jarfile,Mavenfile,Podfile,Puppetfile,Rakefile,rakefile,Snapfile,Steepfile,Thorfile,Vagabondfile,Vagrantfile}'
|
10
|
-
run: rubocop --display-style-guide --extra-details --force-exclusion --parallel {staged_files} || (echo 'Run `lefthook run fix` to run
|
10
|
+
run: rubocop --display-style-guide --extra-details --force-exclusion --parallel {staged_files} || (echo 'Run `lefthook run fix` to run autocorrect on staged files only'; exit 1)
|
11
11
|
|
12
12
|
fix:
|
13
13
|
parallel: true
|
data/lib/warren/app/cli.rb
CHANGED
@@ -23,6 +23,7 @@ module Warren
|
|
23
23
|
desc: 'The path to the configuration file to generate'
|
24
24
|
option :exchange, type: :string,
|
25
25
|
desc: 'The RabbitMQ exchange to connect to'
|
26
|
+
# Invoked by `$ warren config` generates a `warren.yml` file.
|
26
27
|
def config
|
27
28
|
Warren::App::Config.invoke(self, path: options['path'], exchange: options['exchange'])
|
28
29
|
end
|
data/lib/warren/app/config.rb
CHANGED
@@ -42,6 +42,16 @@ module Warren
|
|
42
42
|
# circumstances should you commit sensitive information in the file.
|
43
43
|
TEMPLATE
|
44
44
|
|
45
|
+
# Triggers the configuration task. Primarily called by the Thor CLI.
|
46
|
+
# Will either use arguments passed in from the command line, or prompt the
|
47
|
+
# user for them if missing.
|
48
|
+
#
|
49
|
+
# @param shell [Thor::Shell::Basic] Thor shell instance for feedback
|
50
|
+
# @param path [String] Path to the `warren.yml` file
|
51
|
+
# @param exchange [String, nil] Name of the exchange to use, if passed in from CLI
|
52
|
+
#
|
53
|
+
# @return [Void]
|
54
|
+
#
|
45
55
|
def self.invoke(shell, path:, exchange: nil)
|
46
56
|
new(shell, path: path, exchange: exchange).invoke
|
47
57
|
end
|
data/lib/warren/app/consumer.rb
CHANGED
@@ -32,6 +32,14 @@ module Warren
|
|
32
32
|
option :path, type: :string,
|
33
33
|
default: Warren::Config::Consumers::DEFAULT_PATH,
|
34
34
|
desc: 'The path to the consumer configuration file to generate'
|
35
|
+
option :delay, type: :numeric,
|
36
|
+
desc: 'The delay (ms) on the delay queue. 0 to skip queue creation.'
|
37
|
+
# Invoked by `$ warren consumer add` adds a consumer to the `warren_consumers.yml`
|
38
|
+
#
|
39
|
+
# @param name [String, nil] Optional: Passed in from Command. The name of the consumer to create.
|
40
|
+
#
|
41
|
+
# @return [Void]
|
42
|
+
#
|
35
43
|
def add(name = nil)
|
36
44
|
say 'Adding a consumer'
|
37
45
|
Warren::App::ConsumerAdd.invoke(self, name, options)
|
@@ -44,6 +52,10 @@ module Warren
|
|
44
52
|
option :consumers, type: :array,
|
45
53
|
desc: 'The consumers to start. Defaults to all consumers',
|
46
54
|
banner: 'consumer_name other_consumer'
|
55
|
+
# Invoked by `$ warren consumer start`. Starts up the configured consumers
|
56
|
+
#
|
57
|
+
# @return [Void]
|
58
|
+
#
|
47
59
|
def start
|
48
60
|
say 'Starting consumers'
|
49
61
|
Warren::App::ConsumerStart.invoke(self, options)
|
@@ -7,7 +7,8 @@ module Warren
|
|
7
7
|
module App
|
8
8
|
# Handles the initial creation of the configuration object
|
9
9
|
class ConsumerAdd
|
10
|
-
|
10
|
+
# Default namespace for new Subscribers
|
11
|
+
SUBSCRIBER_NAMESPACE = %w[Warren Subscriber].freeze
|
11
12
|
|
12
13
|
attr_reader :name, :desc, :queue
|
13
14
|
|
@@ -46,6 +47,7 @@ module Warren
|
|
46
47
|
@name = name
|
47
48
|
@desc = options[:desc]
|
48
49
|
@queue = options[:queue]
|
50
|
+
@delay = options[:delay]
|
49
51
|
@config = Warren::Config::Consumers.new(options[:path])
|
50
52
|
@bindings = Warren::App::ExchangeConfig.parse(shell, options[:bindings])
|
51
53
|
end
|
@@ -69,7 +71,7 @@ module Warren
|
|
69
71
|
def subscribed_class
|
70
72
|
class_name = name.split(/[\s\-_]/).map(&:capitalize).join
|
71
73
|
|
72
|
-
|
74
|
+
[*SUBSCRIBER_NAMESPACE, class_name].join('::')
|
73
75
|
end
|
74
76
|
|
75
77
|
def check_name
|
@@ -98,6 +100,9 @@ module Warren
|
|
98
100
|
@desc ||= @shell.ask 'Provide an optional description: '
|
99
101
|
@queue ||= @shell.ask 'Provide the name of the queue to connect to: '
|
100
102
|
@bindings ||= gather_bindings
|
103
|
+
@delay ||= @shell.ask(
|
104
|
+
'Create a delay queue? Specify delay in milliseconds to create; set to 0 or leave blank to skip.'
|
105
|
+
).to_i
|
101
106
|
nil
|
102
107
|
end
|
103
108
|
|
@@ -106,16 +111,20 @@ module Warren
|
|
106
111
|
end
|
107
112
|
|
108
113
|
def write_configuration
|
109
|
-
@config.add_consumer(
|
114
|
+
@config.add_consumer(
|
115
|
+
@name, desc: @desc, queue: @queue,
|
116
|
+
bindings: @bindings, subscribed_class: subscribed_class,
|
117
|
+
delay: @delay
|
118
|
+
)
|
110
119
|
@config.save
|
111
120
|
end
|
112
121
|
|
113
122
|
def write_subscriber
|
114
|
-
@shell.template('subscriber.tt',
|
123
|
+
@shell.template('subscriber.tt', subscriber_path, context: binding)
|
115
124
|
end
|
116
125
|
|
117
|
-
def
|
118
|
-
"app
|
126
|
+
def subscriber_path
|
127
|
+
"#{['app', *SUBSCRIBER_NAMESPACE, @name.tr(' -', '_')].map(&:downcase).join('/')}.rb"
|
119
128
|
end
|
120
129
|
end
|
121
130
|
end
|
@@ -7,6 +7,17 @@ module Warren
|
|
7
7
|
module App
|
8
8
|
# Handles the initial creation of the configuration object
|
9
9
|
class ConsumerStart
|
10
|
+
#
|
11
|
+
# Starts up a warren client process for the configured consumers.
|
12
|
+
#
|
13
|
+
# @param shell [Thor::Shell::Basic] Thor shell instance for feedback
|
14
|
+
# @param options [Hash] Hash of command line arguments from Thor
|
15
|
+
# @option options [String] :path Path to the `warren_consumers.yml `file
|
16
|
+
# @option options [Array<String>] :consumers Array of configured consumers to start.
|
17
|
+
# Defaults to all consumers
|
18
|
+
#
|
19
|
+
# @return [Void]
|
20
|
+
#
|
10
21
|
def self.invoke(shell, options)
|
11
22
|
new(shell, options).invoke
|
12
23
|
end
|
@@ -17,6 +28,10 @@ module Warren
|
|
17
28
|
@consumers = options[:consumers]
|
18
29
|
end
|
19
30
|
|
31
|
+
#
|
32
|
+
# Starts up a warren client process for the configured consumers.
|
33
|
+
#
|
34
|
+
# @return [Void]
|
20
35
|
def invoke
|
21
36
|
Warren::Client.new(@config, consumers: @consumers).run
|
22
37
|
end
|
@@ -102,6 +102,7 @@ module Warren
|
|
102
102
|
|
103
103
|
def ask_direct_binding
|
104
104
|
exchange = ask_exchange
|
105
|
+
routing_key_tip
|
105
106
|
routing_key = @shell.ask 'Specify a routing_key: '
|
106
107
|
add_binding('direct', exchange, { routing_key: routing_key })
|
107
108
|
end
|
@@ -119,6 +120,7 @@ module Warren
|
|
119
120
|
|
120
121
|
def ask_topic_binding
|
121
122
|
exchange = ask_exchange
|
123
|
+
routing_key_tip
|
122
124
|
loop do
|
123
125
|
routing_key = @shell.ask 'Specify a routing_key [Leave blank to stop adding]: '
|
124
126
|
break if routing_key == ''
|
@@ -133,6 +135,17 @@ module Warren
|
|
133
135
|
'options' => options
|
134
136
|
}
|
135
137
|
end
|
138
|
+
|
139
|
+
def routing_key_tip
|
140
|
+
# Suggested cop style of %<routing_key_prefix>s but prefer suggesting the simpler option as it
|
141
|
+
# would be all to easy to miss out the 's', resulting in varying behaviour depending on the following
|
142
|
+
# character
|
143
|
+
# rubocop:disable Style/FormatStringToken
|
144
|
+
@shell.say(
|
145
|
+
'Tip: Use %{routing_key_prefix} in routing keys to reference the routing_key_prefix specified in warren.yml'
|
146
|
+
)
|
147
|
+
# rubocop:enable Style/FormatStringToken
|
148
|
+
end
|
136
149
|
end
|
137
150
|
end
|
138
151
|
end
|
@@ -15,7 +15,7 @@ module Warren
|
|
15
15
|
# Creates the callback object
|
16
16
|
#
|
17
17
|
# @param handler [Warren::Handler] The handler to take the messaged
|
18
|
-
# @param message_class [Warren::Message] The
|
18
|
+
# @param message_class [Warren::Message] The adaptor to render the messages
|
19
19
|
#
|
20
20
|
def initialize(handler:, message_class: Warren::Message::Short)
|
21
21
|
@handler = handler
|
@@ -60,10 +60,12 @@ module Warren
|
|
60
60
|
# @param desc [String] Description of the consumer (Primarily for documentation)
|
61
61
|
# @param queue [String] Name of the queue to attach to
|
62
62
|
# @param bindings [Array<Hash>] Array of binding configuration hashed
|
63
|
+
# @param delay [Integer] Delay on the generated delay exchange
|
63
64
|
#
|
64
65
|
# @return [Hash] The consumer configuration hash
|
65
66
|
#
|
66
|
-
|
67
|
+
# rubocop:todo Metrics/ParameterLists
|
68
|
+
def add_consumer(name, desc:, queue:, bindings:, subscribed_class:, delay:)
|
67
69
|
dead_letter_exchange = "#{name}.dead-letters"
|
68
70
|
@config[name] = {
|
69
71
|
'desc' => desc,
|
@@ -71,9 +73,12 @@ module Warren
|
|
71
73
|
'subscribed_class' => subscribed_class,
|
72
74
|
# This smells wrong. I don't like the call back out to the App namespace
|
73
75
|
'dead_letters' => queue_config(dead_letter_exchange,
|
74
|
-
Warren::App::ExchangeConfig.default_dead_letter(dead_letter_exchange))
|
76
|
+
Warren::App::ExchangeConfig.default_dead_letter(dead_letter_exchange)),
|
77
|
+
'delay' => delay_exchange_configuration(ttl: delay, original_queue: queue, consumer_name: name),
|
78
|
+
'worker_count' => 3
|
75
79
|
}
|
76
80
|
end
|
81
|
+
# rubocop:enable Metrics/ParameterLists
|
77
82
|
|
78
83
|
private
|
79
84
|
|
@@ -86,6 +91,23 @@ module Warren
|
|
86
91
|
}
|
87
92
|
end
|
88
93
|
|
94
|
+
# rubocop:todo Metrics/MethodLength
|
95
|
+
def delay_exchange_configuration(ttl:, original_queue:, consumer_name:)
|
96
|
+
return {} if ttl.nil? || ttl.zero?
|
97
|
+
|
98
|
+
{
|
99
|
+
'exchange' => { 'name' => "#{consumer_name}.delay", 'options' => { type: 'fanout', durable: true } },
|
100
|
+
'bindings' => [{
|
101
|
+
'queue' => { 'name' => "#{consumer_name}.delay", 'options' => {
|
102
|
+
durable: true, arguments: {
|
103
|
+
'x-dead-letter-exchange' => '', 'x-message-ttl' => ttl, 'x-dead-letter-routing-key' => original_queue
|
104
|
+
}
|
105
|
+
} }, 'options' => {}
|
106
|
+
}]
|
107
|
+
}
|
108
|
+
end
|
109
|
+
# rubocop:enable Metrics/MethodLength
|
110
|
+
|
89
111
|
#
|
90
112
|
# Loads the configuration, should be a hash
|
91
113
|
#
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warren
|
4
|
+
# Configures and wraps up delay exchange on a Bunny Channel/Queue
|
5
|
+
# A delay exchange routes immediately onto a queue with a ttl
|
6
|
+
# once messages on this queue expire they are dead-lettered back onto
|
7
|
+
# to original exchange
|
8
|
+
# Note: This does not currently support the rabbitmq-delayed-message-exchange
|
9
|
+
# plugin.
|
10
|
+
class DelayExchange
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_reader :channel
|
14
|
+
|
15
|
+
#
|
16
|
+
# Create a new delay exchange. Handles queue creation, binding and attaching
|
17
|
+
# consumers to the queues
|
18
|
+
#
|
19
|
+
# @param channel [Warren::Handler::Broadcast::Channel] A channel on which to register queues
|
20
|
+
# @param config [Hash] queue configuration hash
|
21
|
+
#
|
22
|
+
def initialize(channel:, config:)
|
23
|
+
@channel = channel
|
24
|
+
@exchange_config = config&.fetch('exchange', nil)
|
25
|
+
@bindings = config&.fetch('bindings', []) || []
|
26
|
+
end
|
27
|
+
|
28
|
+
def_delegators :channel, :nack, :ack
|
29
|
+
|
30
|
+
# Ensures the queues and channels are set up to receive messages
|
31
|
+
# keys: additional routing_keys to bind
|
32
|
+
def activate!
|
33
|
+
establish_bindings!
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Post a message to the delay exchange.
|
38
|
+
#
|
39
|
+
# @param payload [String] The message payload
|
40
|
+
# @param routing_key [String] The routing key of the re-sent message
|
41
|
+
# @param headers [Hash] A hash of headers. Typically: { attempts: <Integer> }
|
42
|
+
# @option headers [Integer] :attempts The number of times the message has been processed
|
43
|
+
#
|
44
|
+
# @return [Void]
|
45
|
+
#
|
46
|
+
def publish(payload, routing_key:, headers: {})
|
47
|
+
raise StandardError, 'No delay queue configured' unless configured?
|
48
|
+
|
49
|
+
message = Warren::Message::Simple.new(routing_key, payload, headers)
|
50
|
+
channel.publish(message, exchange: exchange)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def configured?
|
56
|
+
@exchange_config&.key?('name')
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_binding(queue, options)
|
60
|
+
queue.bind(exchange, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def exchange
|
64
|
+
@exchange ||= channel.exchange(*@exchange_config.values_at('name', 'options'))
|
65
|
+
end
|
66
|
+
|
67
|
+
def queue(config)
|
68
|
+
channel.queue(*config.values_at('name', 'options'))
|
69
|
+
end
|
70
|
+
|
71
|
+
def establish_bindings!
|
72
|
+
@bindings.each do |binding_config|
|
73
|
+
queue = queue(binding_config['queue'])
|
74
|
+
transformed_options = merge_routing_key_prefix(binding_config['options'])
|
75
|
+
add_binding(queue, transformed_options)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def merge_routing_key_prefix(options)
|
80
|
+
options.transform_values do |value|
|
81
|
+
format(value, routing_key_prefix: channel.routing_key_prefix)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/warren/den.rb
CHANGED
@@ -3,12 +3,17 @@
|
|
3
3
|
require 'bunny'
|
4
4
|
require 'warren/fox'
|
5
5
|
require 'warren/subscription'
|
6
|
+
require 'warren/delay_exchange'
|
6
7
|
|
7
8
|
module Warren
|
8
9
|
# A Den is in charge of creating a Fox from a consumer configuration
|
9
|
-
#
|
10
|
-
#
|
10
|
+
# It handles the registration of dead-letter queues, and configuration of
|
11
|
+
# {Warren::Subscription subscriptions} and
|
12
|
+
# {Warren::DelayExchange delay exchanges}
|
11
13
|
class Den
|
14
|
+
# The number of simultaneous workers generated by default
|
15
|
+
DEFAULT_WORKER_COUNT = 3
|
16
|
+
|
12
17
|
#
|
13
18
|
# Create a {Warren::Fox} work pool.
|
14
19
|
# @param app_name [String] The name of the application. Corresponds to the
|
@@ -34,9 +39,10 @@ module Warren
|
|
34
39
|
config = dead_letter_config
|
35
40
|
return unless config
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
42
|
+
Warren.handler.with_channel do |channel|
|
43
|
+
subscription = Warren::Subscription.new(channel: channel, config: config)
|
44
|
+
subscription.activate!
|
45
|
+
end
|
40
46
|
end
|
41
47
|
|
42
48
|
private
|
@@ -54,12 +60,18 @@ module Warren
|
|
54
60
|
# and while we *can* share channels between consumers it results in them
|
55
61
|
# sharing the same worker pool. This process lets us control workers on
|
56
62
|
# a per-queue basis. Currently that just means one worker per consumer.
|
57
|
-
channel = Warren.handler.new_channel
|
63
|
+
channel = Warren.handler.new_channel(worker_count: worker_count)
|
58
64
|
subscription = Warren::Subscription.new(channel: channel, config: queue_config)
|
65
|
+
delay = Warren::DelayExchange.new(channel: channel, config: delay_config)
|
59
66
|
Warren::Fox.new(name: @app_name,
|
60
67
|
subscription: subscription,
|
61
68
|
adaptor: @adaptor,
|
62
|
-
subscribed_class: subscribed_class
|
69
|
+
subscribed_class: subscribed_class,
|
70
|
+
delayed: delay)
|
71
|
+
end
|
72
|
+
|
73
|
+
def worker_count
|
74
|
+
consumer_config.fetch('worker_count', DEFAULT_WORKER_COUNT)
|
63
75
|
end
|
64
76
|
|
65
77
|
def queue_config
|
@@ -70,6 +82,10 @@ module Warren
|
|
70
82
|
consumer_config.fetch('dead_letters')
|
71
83
|
end
|
72
84
|
|
85
|
+
def delay_config
|
86
|
+
consumer_config.fetch('delay', nil)
|
87
|
+
end
|
88
|
+
|
73
89
|
def subscribed_class
|
74
90
|
Object.const_get(consumer_config.fetch('subscribed_class'))
|
75
91
|
end
|
data/lib/warren/fox.rb
CHANGED
@@ -20,7 +20,7 @@ module Warren
|
|
20
20
|
# Maximum wait time between database retries: 5 minutes
|
21
21
|
MAX_RECONNECT_DELAY = 60 * 5
|
22
22
|
|
23
|
-
attr_reader :state, :subscription, :consumer_tag
|
23
|
+
attr_reader :state, :subscription, :consumer_tag, :delayed
|
24
24
|
|
25
25
|
#
|
26
26
|
# Creates a fox, a RabbitMQ consumer.
|
@@ -30,10 +30,13 @@ module Warren
|
|
30
30
|
# @param name [String] The name of the consumer
|
31
31
|
# @param subscription [Warren::Subscription] Describes the queue to subscribe to
|
32
32
|
# @param adaptor [#recovered?,#handle,#env] An adaptor to handle framework specifics
|
33
|
+
# @param subscribed_class [Warren::Subscriber::Base] The class to process received messages
|
34
|
+
# @param delayed [Warren::DelayExchange] The details handling delayed message broadcast
|
33
35
|
#
|
34
|
-
def initialize(name:, subscription:, adaptor:, subscribed_class:)
|
36
|
+
def initialize(name:, subscription:, adaptor:, subscribed_class:, delayed:)
|
35
37
|
@consumer_tag = "#{adaptor.env}_#{name}_#{Process.pid}"
|
36
38
|
@subscription = subscription
|
39
|
+
@delayed = delayed
|
37
40
|
@logger = Warren::LogTagger.new(logger: adaptor.logger, tag: "#{FOX} #{@consumer_tag}")
|
38
41
|
@adaptor = adaptor
|
39
42
|
@subscribed_class = subscribed_class
|
@@ -52,6 +55,7 @@ module Warren
|
|
52
55
|
def run!
|
53
56
|
starting!
|
54
57
|
subscription.activate! # Set up the queues
|
58
|
+
delayed.activate!
|
55
59
|
running! # Transition to running state
|
56
60
|
subscribe! # Subscribe to the queue
|
57
61
|
|
@@ -117,7 +121,7 @@ module Warren
|
|
117
121
|
end
|
118
122
|
end
|
119
123
|
|
120
|
-
# Cancels the consumer and
|
124
|
+
# Cancels the consumer and un-registers it
|
121
125
|
def unsubscribe!
|
122
126
|
info { 'Unsubscribing' }
|
123
127
|
@consumer&.cancel
|
@@ -1,6 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Warren
|
4
|
+
# Namespace for framework adaptors.
|
5
|
+
#
|
6
|
+
# A FrameworkAdaptor should implement the following instance methods:
|
7
|
+
#
|
8
|
+
# == recovered? => Bool
|
9
|
+
# Indicates that any temporary issues (such as database connectivity problems)
|
10
|
+
# are resolved and consumers may restart.
|
11
|
+
#
|
12
|
+
# == handle
|
13
|
+
#
|
14
|
+
# Wraps the processing of each message, is expected to `yield` to allow
|
15
|
+
# processing. May be responsible for handling connection pools, and
|
16
|
+
# framework-specific exceptions. Raising {Warren::Exceptions::TemporaryIssue}
|
17
|
+
# here will cause consumers to sleep until `recovered?` returns true.
|
18
|
+
#
|
19
|
+
# == env => String
|
20
|
+
#
|
21
|
+
# Returns the current environment of the application.
|
22
|
+
#
|
23
|
+
# == logger => Logger
|
24
|
+
#
|
25
|
+
# Returns your application logger. Is expected to be compatible with the
|
26
|
+
# standard library Logger class.
|
27
|
+
# @see https://ruby-doc.org/stdlib-2.7.0/libdoc/logger/rdoc/Logger.html
|
28
|
+
#
|
29
|
+
# == load_application
|
30
|
+
#
|
31
|
+
# Called upon running `warren consumer start`. Should ensure your application
|
32
|
+
# is correctly loaded sufficiently for processing messages
|
33
|
+
#
|
4
34
|
module FrameworkAdaptor
|
5
35
|
# The RailsAdaptor provides error handling and application
|
6
36
|
# loading for Rails applications
|
@@ -33,6 +63,11 @@ module Warren
|
|
33
63
|
end
|
34
64
|
end
|
35
65
|
|
66
|
+
#
|
67
|
+
# Checks that the database has recovered to allow message processing
|
68
|
+
#
|
69
|
+
# @return [Bool] Returns true if the application has recovered
|
70
|
+
#
|
36
71
|
def recovered?
|
37
72
|
ActiveRecord::Base.connection.reconnect!
|
38
73
|
true
|
@@ -40,6 +75,14 @@ module Warren
|
|
40
75
|
false
|
41
76
|
end
|
42
77
|
|
78
|
+
#
|
79
|
+
# Checks ensures a database connection has been checked out before
|
80
|
+
# yielding to allow message processing. Rescues loss of the database
|
81
|
+
# connection and raises {Warren::Exceptions::TemporaryIssue} to send
|
82
|
+
# the consumers to sleep until it recovers.
|
83
|
+
#
|
84
|
+
# @return [Void]
|
85
|
+
#
|
43
86
|
def handle
|
44
87
|
with_connection do
|
45
88
|
yield
|
@@ -60,14 +103,23 @@ module Warren
|
|
60
103
|
ActiveRecord::Base.clear_active_connections!
|
61
104
|
end
|
62
105
|
|
106
|
+
# Returns the rails environment
|
107
|
+
#
|
108
|
+
# @return [ActiveSupport::StringInquirer] The rails environment
|
63
109
|
def env
|
64
110
|
Rails.env
|
65
111
|
end
|
66
112
|
|
113
|
+
# Returns the configured logger
|
114
|
+
#
|
115
|
+
# @return [Logger,ActiveSupport::Logger,...] The application logger
|
67
116
|
def logger
|
68
117
|
Rails.logger
|
69
118
|
end
|
70
119
|
|
120
|
+
# Triggers full loading of the rails application and dependencies
|
121
|
+
#
|
122
|
+
# @return [Void]
|
71
123
|
def load_application
|
72
124
|
$stdout.puts 'Loading application...'
|
73
125
|
require './config/environment'
|
data/lib/warren/handler.rb
CHANGED
@@ -7,6 +7,22 @@ module Warren
|
|
7
7
|
# A {Warren::Handler} provides an interface for sending messages to either
|
8
8
|
# a message queue, a log, or an internal store for testing purposes.
|
9
9
|
module Handler
|
10
|
+
#
|
11
|
+
# Generates a template for routing keys for the given prefix, or a template
|
12
|
+
# that returns the provided routing key if no prefix is supplied.
|
13
|
+
#
|
14
|
+
# @example With a prefix
|
15
|
+
# template = Warren::Handler.routing_key_template('example') # => 'example.%s'
|
16
|
+
# format(template, 'routing.key') #=> 'example.routing.key'
|
17
|
+
#
|
18
|
+
# @example Without a prefix
|
19
|
+
# template = Warren::Handler.routing_key_template(nil) # => '%s'
|
20
|
+
# format(template, 'routing.key') #=> 'routing.key'
|
21
|
+
#
|
22
|
+
# @param prefix [String, nil] The prefix to use in the template
|
23
|
+
#
|
24
|
+
# @return [String] A template for generating routing keys
|
25
|
+
#
|
10
26
|
def self.routing_key_template(prefix)
|
11
27
|
prefix ? "#{prefix}.%s" : '%s'
|
12
28
|
end
|
@@ -17,25 +17,47 @@ module Warren
|
|
17
17
|
class Channel
|
18
18
|
extend Forwardable
|
19
19
|
|
20
|
+
attr_reader :routing_key_prefix
|
21
|
+
|
20
22
|
def_delegators :@bun_channel, :close, :exchange, :queue, :prefetch, :ack, :nack
|
21
23
|
|
22
|
-
def initialize(bun_channel,
|
24
|
+
def initialize(bun_channel, routing_key_prefix:, exchange: nil)
|
23
25
|
@bun_channel = bun_channel
|
24
26
|
@exchange_name = exchange
|
25
|
-
@
|
27
|
+
@routing_key_prefix = routing_key_prefix
|
28
|
+
@routing_key_template = Handler.routing_key_template(routing_key_prefix)
|
26
29
|
end
|
27
30
|
|
31
|
+
# Publishes `message` to the configured exchange
|
32
|
+
#
|
33
|
+
# @param message [#routing_key,#payload] A message should respond to routing_key and payload.
|
34
|
+
# @see Warren::Message::Full
|
35
|
+
#
|
36
|
+
# @return [Warren::Handler::Broadcast::Channel] returns self for chaining
|
37
|
+
#
|
28
38
|
def <<(message)
|
29
|
-
|
39
|
+
publish(message)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Publishes `message` to `exchange` (Defaults to configured exchange)
|
43
|
+
#
|
44
|
+
# @param message [#routing_key,#payload] A message should respond to routing_key and payload.
|
45
|
+
# @see Warren::Message::Full
|
46
|
+
# @param exchange [Bunny::Exchange] The exchange to publish to
|
47
|
+
#
|
48
|
+
# @return [Warren::Handler::Broadcast::Channel] returns self for chaining
|
49
|
+
#
|
50
|
+
def publish(message, exchange: configured_exchange)
|
51
|
+
exchange.publish(message.payload, routing_key: key_for(message), headers: message.headers)
|
30
52
|
self
|
31
53
|
end
|
32
54
|
|
33
55
|
private
|
34
56
|
|
35
|
-
def
|
57
|
+
def configured_exchange
|
36
58
|
raise StandardError, 'No exchange configured' if @exchange_name.nil?
|
37
59
|
|
38
|
-
@
|
60
|
+
@configured_exchange ||= exchange(@exchange_name, auto_delete: false, durable: true, type: :topic)
|
39
61
|
end
|
40
62
|
|
41
63
|
def key_for(message)
|
@@ -56,7 +78,7 @@ module Warren
|
|
56
78
|
@server = server
|
57
79
|
@exchange_name = exchange
|
58
80
|
@pool_size = pool_size
|
59
|
-
@
|
81
|
+
@routing_key_prefix = routing_key_prefix
|
60
82
|
end
|
61
83
|
|
62
84
|
#
|
@@ -105,9 +127,9 @@ module Warren
|
|
105
127
|
self
|
106
128
|
end
|
107
129
|
|
108
|
-
def new_channel
|
109
|
-
Channel.new(session.create_channel(nil,
|
110
|
-
|
130
|
+
def new_channel(worker_count: 1)
|
131
|
+
Channel.new(session.create_channel(nil, worker_count), exchange: @exchange_name,
|
132
|
+
routing_key_prefix: @routing_key_prefix)
|
111
133
|
end
|
112
134
|
|
113
135
|
private
|
data/lib/warren/handler/log.rb
CHANGED
@@ -14,6 +14,13 @@ module Warren
|
|
14
14
|
@routing_key_template = routing_key_template
|
15
15
|
end
|
16
16
|
|
17
|
+
# Logs `message` to the configured logger
|
18
|
+
#
|
19
|
+
# @param message [#routing_key,#payload] A message should respond to routing_key and payload.
|
20
|
+
# @see Warren::Message::Full
|
21
|
+
#
|
22
|
+
# @return [Warren::Handler::Broadcast::Channel] returns self for chaining
|
23
|
+
#
|
17
24
|
def <<(message)
|
18
25
|
@logger.info "Published: #{key_for(message)}"
|
19
26
|
@logger.debug "Payload: #{message.payload}"
|
@@ -40,6 +47,7 @@ module Warren
|
|
40
47
|
end
|
41
48
|
end
|
42
49
|
|
50
|
+
# Small object to track exchange properties for logging purposes
|
43
51
|
Exchange = Struct.new(:name, :options)
|
44
52
|
|
45
53
|
# Queue class to provide extended logging in development mode
|
data/lib/warren/handler/test.rb
CHANGED
@@ -77,13 +77,16 @@ module Warren
|
|
77
77
|
@warren = warren
|
78
78
|
end
|
79
79
|
|
80
|
+
# Records `message` for testing purposes
|
81
|
+
#
|
82
|
+
# @param message [#routing_key,#payload] A message should respond to routing_key and payload.
|
83
|
+
# @see Warren::Message::Full
|
84
|
+
#
|
85
|
+
# @return [Warren::Handler::Broadcast::Channel] returns self for chaining
|
86
|
+
#
|
80
87
|
def <<(message)
|
81
88
|
@warren << message
|
82
89
|
end
|
83
|
-
|
84
|
-
def add_exchange(name, options)
|
85
|
-
@warren.add_exchange(name, options)
|
86
|
-
end
|
87
90
|
end
|
88
91
|
|
89
92
|
#
|
@@ -100,7 +103,7 @@ module Warren
|
|
100
103
|
end
|
101
104
|
|
102
105
|
#
|
103
|
-
# Yields a new
|
106
|
+
# Yields a new channel, which proxies all message back to {messages} on the
|
104
107
|
# {Warren::Handler::Test}
|
105
108
|
#
|
106
109
|
# @return [void]
|
@@ -111,7 +114,7 @@ module Warren
|
|
111
114
|
end
|
112
115
|
|
113
116
|
#
|
114
|
-
# Returns a new
|
117
|
+
# Returns a new channel, which proxies all message back to {messages} on the
|
115
118
|
# {Warren::Handler::Test}
|
116
119
|
#
|
117
120
|
# @return [Warren::Test::Channel] A rabbitMQ channel that logs messaged to the test warren
|
@@ -185,10 +188,6 @@ module Warren
|
|
185
188
|
@messages << message if @enabled
|
186
189
|
end
|
187
190
|
|
188
|
-
def add_exchange(name, options)
|
189
|
-
@exchanges << [name, options] if @enabled
|
190
|
-
end
|
191
|
-
|
192
191
|
private
|
193
192
|
|
194
193
|
def raise_if_not_tracking
|
data/lib/warren/message.rb
CHANGED
@@ -2,11 +2,13 @@
|
|
2
2
|
|
3
3
|
require_relative 'message/short'
|
4
4
|
require_relative 'message/full'
|
5
|
+
require_relative 'message/simple'
|
5
6
|
|
6
7
|
# Namespace to collect message formats
|
7
8
|
# A Warren compatible message must implement:
|
8
9
|
# routing_key: returns the routing_key for the message
|
9
10
|
# payload: returns the message payload
|
11
|
+
# headers: Returns a headers hash
|
10
12
|
#
|
11
13
|
# Additionally, if you wish to use the Message with the ActiveRecord
|
12
14
|
# helpers, then the initialize should take the ActiveRecord::Base object
|
data/lib/warren/message/full.rb
CHANGED
@@ -10,6 +10,13 @@ module Warren
|
|
10
10
|
@record = record
|
11
11
|
end
|
12
12
|
|
13
|
+
#
|
14
|
+
# The routing key that will be used for the message, not including the
|
15
|
+
# routing_key_prefix configured in warren.yml. If {#record} responds
|
16
|
+
# to `routing_key` will use that instead
|
17
|
+
#
|
18
|
+
# @return [String] The routing key.
|
19
|
+
#
|
13
20
|
def routing_key
|
14
21
|
if record.respond_to?(:routing_key)
|
15
22
|
record.routing_key
|
@@ -18,9 +25,22 @@ module Warren
|
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
28
|
+
#
|
29
|
+
# The payload of the message.
|
30
|
+
# @see https://github.com/intridea/multi_json
|
31
|
+
#
|
32
|
+
# @return [String] The message payload
|
21
33
|
def payload
|
22
34
|
MultiJson.dump(record)
|
23
35
|
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# For compatibility. Returns an empty hash.
|
39
|
+
#
|
40
|
+
# @return [{}] Empty hash
|
41
|
+
def headers
|
42
|
+
{}
|
43
|
+
end
|
24
44
|
end
|
25
45
|
end
|
26
46
|
end
|
data/lib/warren/message/short.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Warren
|
4
|
+
# Namespace for Warren message wrappers.
|
5
|
+
module Message
|
6
|
+
# A simple message simply wraps the routing key and payload together
|
7
|
+
# @!attribute [rw] routing_key
|
8
|
+
# @return [String] The routing key of the message
|
9
|
+
# @!attribute [rw] payload
|
10
|
+
# @return [String] The payload of the message
|
11
|
+
# @!attribute [rw] headers
|
12
|
+
# @return [Hash] Hash of header attributes. Can be empty hash.
|
13
|
+
Simple = Struct.new(:routing_key, :payload, :headers)
|
14
|
+
end
|
15
|
+
end
|
@@ -24,7 +24,8 @@ module Warren
|
|
24
24
|
# delegators (Supplied by Forwardable)
|
25
25
|
# Essentially syntax is:
|
26
26
|
# def_delegators <target>, *<methods_to_delegate>
|
27
|
-
def_delegators :fox, :subscription, :warn, :info, :error, :debug
|
27
|
+
def_delegators :fox, :subscription, :warn, :info, :error, :debug, :delayed
|
28
|
+
def_delegators :delivery_info, :routing_key, :delivery_tag
|
28
29
|
|
29
30
|
#
|
30
31
|
# Construct a basic subscriber for each received message. Call {#process}
|
@@ -69,6 +70,8 @@ module Warren
|
|
69
70
|
warn "Re-queue: #{payload}"
|
70
71
|
warn "Re-queue Exception: #{exception.message}"
|
71
72
|
raise_if_acknowledged
|
73
|
+
# nack arguments: delivery_tag, multiple, requeue
|
74
|
+
# http://reference.rubybunny.info/Bunny/Channel.html#nack-instance_method
|
72
75
|
subscription.nack(delivery_tag, false, true)
|
73
76
|
@acknowledged = true
|
74
77
|
warn 'Re-queue nacked'
|
@@ -90,18 +93,43 @@ module Warren
|
|
90
93
|
error 'Dead-letter nacked'
|
91
94
|
end
|
92
95
|
|
96
|
+
#
|
97
|
+
# Re-post the message to the delay exchange and acknowledges receipt of
|
98
|
+
# the original message. The delay exchange will return the messages to
|
99
|
+
# the original queue after a delay.
|
100
|
+
#
|
101
|
+
# @param exception [StandardError] The exception that has caused the
|
102
|
+
# message to require a delay
|
103
|
+
#
|
104
|
+
# @return [Void]
|
105
|
+
#
|
106
|
+
def delay(exception)
|
107
|
+
return dead_letter(exception) if attempt > max_retries
|
108
|
+
|
109
|
+
warn "Delay: #{payload}"
|
110
|
+
warn "Delay Exception: #{exception.message}"
|
111
|
+
# Publish the message to the delay queue
|
112
|
+
delayed.publish(payload, routing_key: routing_key, headers: { attempts: attempt + 1 })
|
113
|
+
# Acknowledge the original message
|
114
|
+
ack
|
115
|
+
end
|
116
|
+
|
93
117
|
private
|
94
118
|
|
119
|
+
def max_retries
|
120
|
+
30
|
121
|
+
end
|
122
|
+
|
123
|
+
def attempt
|
124
|
+
headers.fetch('attempts', 0)
|
125
|
+
end
|
126
|
+
|
95
127
|
def headers
|
96
128
|
# Annoyingly it appears that a message with no headers
|
97
129
|
# returns nil, not an empty hash
|
98
130
|
properties.headers || {}
|
99
131
|
end
|
100
132
|
|
101
|
-
def delivery_tag
|
102
|
-
delivery_info.delivery_tag
|
103
|
-
end
|
104
|
-
|
105
133
|
# Acknowledge the message as successfully processed.
|
106
134
|
# Will raise {Warren::MultipleAcknowledgements} if the message has been
|
107
135
|
# acknowledged or rejected already.
|
data/lib/warren/subscription.rb
CHANGED
@@ -11,14 +11,14 @@ module Warren
|
|
11
11
|
# Great a new subscription. Handles queue creation, binding and attaching
|
12
12
|
# consumers to the queues
|
13
13
|
#
|
14
|
-
# @param channel [Warren::Handler::Broadcast::Channel] A
|
14
|
+
# @param channel [Warren::Handler::Broadcast::Channel] A channel on which to register queues
|
15
15
|
# @param config [Hash] queue configuration hash
|
16
16
|
#
|
17
17
|
def initialize(channel:, config:)
|
18
18
|
@channel = channel
|
19
|
-
@queue_name = config
|
20
|
-
@queue_options = config
|
21
|
-
@bindings = config
|
19
|
+
@queue_name = config&.fetch('name')
|
20
|
+
@queue_options = config&.fetch('options')
|
21
|
+
@bindings = config&.fetch('bindings')
|
22
22
|
end
|
23
23
|
|
24
24
|
def_delegators :channel, :nack, :ack
|
@@ -58,13 +58,20 @@ module Warren
|
|
58
58
|
def queue
|
59
59
|
raise StandardError, 'No queue configured' if @queue_name.nil?
|
60
60
|
|
61
|
-
channel.queue(@queue_name, @queue_options)
|
61
|
+
@queue ||= channel.queue(@queue_name, @queue_options)
|
62
62
|
end
|
63
63
|
|
64
64
|
def establish_bindings!
|
65
65
|
@bindings.each do |binding_config|
|
66
66
|
exchange = exchange(binding_config['exchange'])
|
67
|
-
|
67
|
+
transformed_options = merge_routing_key_prefix(binding_config['options'])
|
68
|
+
add_binding(exchange, transformed_options)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def merge_routing_key_prefix(options)
|
73
|
+
options.transform_values do |value|
|
74
|
+
format(value, routing_key_prefix: channel.routing_key_prefix)
|
68
75
|
end
|
69
76
|
end
|
70
77
|
end
|
data/lib/warren/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sanger_warren
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Glover
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -80,6 +80,7 @@ files:
|
|
80
80
|
- ".gitignore"
|
81
81
|
- ".rspec"
|
82
82
|
- ".rubocop.yml"
|
83
|
+
- ".yardopts"
|
83
84
|
- CHANGELOG.md
|
84
85
|
- Gemfile
|
85
86
|
- Gemfile.lock
|
@@ -107,6 +108,7 @@ files:
|
|
107
108
|
- lib/warren/callback/broadcast_with_warren.rb
|
108
109
|
- lib/warren/client.rb
|
109
110
|
- lib/warren/config/consumers.rb
|
111
|
+
- lib/warren/delay_exchange.rb
|
110
112
|
- lib/warren/den.rb
|
111
113
|
- lib/warren/exceptions.rb
|
112
114
|
- lib/warren/fox.rb
|
@@ -121,6 +123,7 @@ files:
|
|
121
123
|
- lib/warren/message.rb
|
122
124
|
- lib/warren/message/full.rb
|
123
125
|
- lib/warren/message/short.rb
|
126
|
+
- lib/warren/message/simple.rb
|
124
127
|
- lib/warren/railtie.rb
|
125
128
|
- lib/warren/subscriber/base.rb
|
126
129
|
- lib/warren/subscription.rb
|
@@ -148,9 +151,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
151
|
version: 2.6.0
|
149
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
153
|
requirements:
|
151
|
-
- - "
|
154
|
+
- - ">="
|
152
155
|
- !ruby/object:Gem::Version
|
153
|
-
version:
|
156
|
+
version: '0'
|
154
157
|
requirements: []
|
155
158
|
rubygems_version: 3.1.4
|
156
159
|
signing_key:
|