shoryuken 1.0.2 → 2.0.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/CHANGELOG.md +11 -0
- data/Gemfile +4 -1
- data/README.md +6 -52
- data/lib/shoryuken/cli.rb +9 -9
- data/lib/shoryuken/client.rb +2 -1
- data/lib/shoryuken/environment_loader.rb +25 -9
- data/lib/shoryuken/fetcher.rb +6 -6
- data/lib/shoryuken/launcher.rb +1 -1
- data/lib/shoryuken/manager.rb +9 -9
- data/lib/shoryuken/message.rb +13 -3
- data/lib/shoryuken/middleware/chain.rb +1 -1
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +1 -1
- data/lib/shoryuken/middleware/server/timing.rb +4 -4
- data/lib/shoryuken/processor.rb +28 -14
- data/lib/shoryuken/queue.rb +43 -12
- data/lib/shoryuken/util.rb +4 -4
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker.rb +1 -1
- data/lib/shoryuken.rb +24 -6
- data/spec/shoryuken/client_spec.rb +45 -4
- data/spec/shoryuken/middleware/chain_spec.rb +3 -1
- data/spec/shoryuken/middleware/server/timing_spec.rb +21 -7
- data/spec/shoryuken/processor_spec.rb +32 -6
- data/spec/shoryuken/queue_spec.rb +58 -0
- data/spec/shoryuken_endpoint.yml +6 -0
- data/spec/spec_helper.rb +4 -1
- metadata +29 -26
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee01f5dc2129537f640aba700e43c8be0f37e77c
|
|
4
|
+
data.tar.gz: 54c1fe1a997a29a77939336ca5f8f2c7e691a745
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5543a21bcfadc457a3bb7444b27ba97d59c9f238bbc9633a100555154823132f7ee6f3cf233f84743ed553cc69a27f8ddf836515690a120b291ffb8212db8783
|
|
7
|
+
data.tar.gz: bcdc762ee9e02366ad4194e372ad76e2e7c23f7e5f15129d8c90d6abfa65af337c4900addaf47d14f8f6578e084a6fd84dad478878589e0c3e2912c5851c89f5
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## [v2.0.0] - 2015-09-22
|
|
2
|
+
|
|
3
|
+
- Allow configuration of SQS/SNS endpoints via environment variables
|
|
4
|
+
- [#130](https://github.com/phstc/shoryuken/pull/130)
|
|
5
|
+
|
|
6
|
+
- Expose queue_name in the message object
|
|
7
|
+
- [#127](https://github.com/phstc/shoryuken/pull/127)
|
|
8
|
+
|
|
9
|
+
- README updates
|
|
10
|
+
- [#122](https://github.com/phstc/shoryuken/pull/122)
|
|
11
|
+
- [#120](https://github.com/phstc/shoryuken/pull/120)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -36,7 +36,7 @@ If all queues get empty, all processors will be changed to the waiting state and
|
|
|
36
36
|
|
|
37
37
|
### Fetch in batches
|
|
38
38
|
|
|
39
|
-
To be even more
|
|
39
|
+
To be even more performant and cost effective, Shoryuken fetches SQS messages in batches, so a single SQS request can fetch up to 10 messages.
|
|
40
40
|
|
|
41
41
|
## Requirements
|
|
42
42
|
|
|
@@ -114,7 +114,7 @@ aws:
|
|
|
114
114
|
access_key_id: ... # or <%= ENV['AWS_ACCESS_KEY_ID'] %>
|
|
115
115
|
secret_access_key: ... # or <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
|
|
116
116
|
region: us-east-1 # or <%= ENV['AWS_REGION'] %>
|
|
117
|
-
receive_message: # See http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/
|
|
117
|
+
receive_message: # See http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#receive_message-instance_method
|
|
118
118
|
# wait_time_seconds: N # The number of seconds to wait for new messages when polling. Defaults to the #wait_time_seconds defined on the queue
|
|
119
119
|
attribute_names:
|
|
120
120
|
- ApproximateReceiveCount
|
|
@@ -132,59 +132,13 @@ The ```aws``` section is used to configure both the Aws objects used by Shoryuke
|
|
|
132
132
|
- ```account_id``` is used when generating SNS ARNs
|
|
133
133
|
- ```sns_endpoint``` can be used to explicitly override the SNS endpoint
|
|
134
134
|
- ```sqs_endpoint``` can be used to explicitly override the SQS endpoint
|
|
135
|
-
- ```receive_message``` can be used to define the options passed to the http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/
|
|
135
|
+
- ```receive_message``` can be used to define the options passed to the http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#receive_message-instance_method
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
You can tell Shoryuken to load your Rails application by passing the `-R` or `--rails` flag to the "shoryuken" command.
|
|
140
|
-
|
|
141
|
-
If you load Rails, and assuming your workers are located in the `app/workers` directory, they will be auto-loaded. This means you don't need to require them explicitly with `-r`.
|
|
142
|
-
|
|
143
|
-
For middleware and other configuration, you might want to create an initializer:
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
Shoryuken.configure_server do |config|
|
|
147
|
-
# Replace Rails logger so messages are logged wherever Shoryuken is logging
|
|
148
|
-
# Note: this entire block is only run by the processor, so we don't overwrite
|
|
149
|
-
# the logger when the app is running as usual.
|
|
150
|
-
Rails.logger = Shoryuken::Logging.logger
|
|
151
|
-
|
|
152
|
-
config.server_middleware do |chain|
|
|
153
|
-
chain.add Shoryuken::MyMiddleware
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
*Note:* In the above case, since we are replacing the Rails logger, it's desired that this initializer runs before other initializers (in case they themselves use the logger). Since by Rails conventions initializers are executed in alphabetical order, this can be achieved by prepending the initializer filename with `00_` (assuming no other initializers alphabetically precede this one).
|
|
159
|
-
|
|
160
|
-
This feature works for Rails 4+, but needs to be confirmed for older versions.
|
|
161
|
-
|
|
162
|
-
#### ActiveJob Support
|
|
137
|
+
The ```sns_endpoint``` and ```sqs_endpoint``` Shoryuken-specific options will also fallback to the environment variables ```AWS_SNS_ENDPOINT``` and ```AWS_SQS_ENDPOINT``` respectively, if they are set.
|
|
163
138
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
It works as expected. Just put your job in `app/jobs`. Here's an example:
|
|
167
|
-
|
|
168
|
-
```ruby
|
|
169
|
-
class ProcessPhotoJob < ActiveJob::Base
|
|
170
|
-
queue_as :default
|
|
171
|
-
|
|
172
|
-
rescue_from ActiveJob::DeserializationError do |e|
|
|
173
|
-
Shoryuken.logger.error ex
|
|
174
|
-
Shoryuken.logger.error ex.backtrace.join("\n")
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def perform(photo)
|
|
178
|
-
photo.process_image!
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Delayed mailers, ActiveRecord serialization, etc. all work.
|
|
184
|
-
|
|
185
|
-
See [ActiveJob docs](http://edgeguides.rubyonrails.org/active_job_basics.html) for more info.
|
|
139
|
+
### Rails Integration
|
|
186
140
|
|
|
187
|
-
|
|
141
|
+
[Check the Rails Integration Active Job documention](https://github.com/phstc/shoryuken/wiki/Rails-Integration-Active-Job).
|
|
188
142
|
|
|
189
143
|
### Start Shoryuken
|
|
190
144
|
|
data/lib/shoryuken/cli.rb
CHANGED
|
@@ -41,7 +41,7 @@ module Shoryuken
|
|
|
41
41
|
@launcher = Shoryuken::Launcher.new
|
|
42
42
|
|
|
43
43
|
if callback = Shoryuken.start_callback
|
|
44
|
-
logger.info
|
|
44
|
+
logger.info { 'Calling Shoryuken.on_start block' }
|
|
45
45
|
callback.call
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -158,7 +158,7 @@ module Shoryuken
|
|
|
158
158
|
|
|
159
159
|
@parser.banner = 'shoryuken [options]'
|
|
160
160
|
@parser.on_tail '-h', '--help', 'Show help' do
|
|
161
|
-
logger.info @parser
|
|
161
|
+
logger.info { @parser }
|
|
162
162
|
exit 1
|
|
163
163
|
end
|
|
164
164
|
@parser.parse!(argv)
|
|
@@ -166,22 +166,22 @@ module Shoryuken
|
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
def handle_signal(sig)
|
|
169
|
-
logger.info "Got #{sig} signal"
|
|
169
|
+
logger.info { "Got #{sig} signal" }
|
|
170
170
|
|
|
171
171
|
case sig
|
|
172
172
|
when 'USR1'
|
|
173
|
-
logger.info 'Received USR1, will soft shutdown down'
|
|
173
|
+
logger.info { 'Received USR1, will soft shutdown down' }
|
|
174
174
|
|
|
175
175
|
launcher.stop
|
|
176
176
|
|
|
177
177
|
exit 0
|
|
178
178
|
when 'TTIN'
|
|
179
179
|
Thread.list.each do |thread|
|
|
180
|
-
logger.info "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
|
180
|
+
logger.info { "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}" }
|
|
181
181
|
if thread.backtrace
|
|
182
|
-
logger.info thread.backtrace.join("\n")
|
|
182
|
+
logger.info { thread.backtrace.join("\n") }
|
|
183
183
|
else
|
|
184
|
-
logger.info '<no backtrace available>'
|
|
184
|
+
logger.info { '<no backtrace available>' }
|
|
185
185
|
end
|
|
186
186
|
end
|
|
187
187
|
|
|
@@ -189,9 +189,9 @@ module Shoryuken
|
|
|
189
189
|
busy = launcher.manager.instance_variable_get(:@busy).size
|
|
190
190
|
queues = launcher.manager.instance_variable_get(:@queues)
|
|
191
191
|
|
|
192
|
-
logger.info "Ready: #{ready}, Busy: #{busy}, Active Queues: #{unparse_queues(queues)}"
|
|
192
|
+
logger.info { "Ready: #{ready}, Busy: #{busy}, Active Queues: #{unparse_queues(queues)}" }
|
|
193
193
|
else
|
|
194
|
-
logger.info "Received #{sig}, will shutdown down"
|
|
194
|
+
logger.info { "Received #{sig}, will shutdown down" }
|
|
195
195
|
|
|
196
196
|
raise Interrupt
|
|
197
197
|
end
|
data/lib/shoryuken/client.rb
CHANGED
|
@@ -30,7 +30,8 @@ module Shoryuken
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
32
|
def aws_client_options(service_endpoint_key)
|
|
33
|
-
|
|
33
|
+
environment_endpoint = ENV["AWS_#{service_endpoint_key.to_s.upcase}"]
|
|
34
|
+
explicit_endpoint = Shoryuken.options[:aws][service_endpoint_key] || environment_endpoint
|
|
34
35
|
options = {}
|
|
35
36
|
options[:endpoint] = explicit_endpoint unless explicit_endpoint.to_s.empty?
|
|
36
37
|
options
|
|
@@ -19,6 +19,7 @@ module Shoryuken
|
|
|
19
19
|
load_rails if options[:rails]
|
|
20
20
|
Shoryuken.options.merge!(config_file_options)
|
|
21
21
|
merge_cli_defined_queues
|
|
22
|
+
prefix_active_job_queue_names
|
|
22
23
|
Shoryuken.options.merge!(options)
|
|
23
24
|
parse_queues
|
|
24
25
|
require_workers
|
|
@@ -33,7 +34,7 @@ module Shoryuken
|
|
|
33
34
|
def config_file_options
|
|
34
35
|
if (path = options[:config_file])
|
|
35
36
|
unless File.exist?(path)
|
|
36
|
-
Shoryuken.logger.warn "Config file #{path} does not exist"
|
|
37
|
+
Shoryuken.logger.warn { "Config file #{path} does not exist" }
|
|
37
38
|
path = nil
|
|
38
39
|
end
|
|
39
40
|
end
|
|
@@ -48,11 +49,11 @@ module Shoryuken
|
|
|
48
49
|
# when not explicit supplied
|
|
49
50
|
return if Shoryuken.options[:aws].empty?
|
|
50
51
|
|
|
51
|
-
shoryuken_keys = %
|
|
52
|
+
shoryuken_keys = %w(
|
|
52
53
|
account_id
|
|
53
54
|
sns_endpoint
|
|
54
55
|
sqs_endpoint
|
|
55
|
-
receive_message)
|
|
56
|
+
receive_message).map(&:to_sym)
|
|
56
57
|
|
|
57
58
|
aws_options = Shoryuken.options[:aws].reject do |k, v|
|
|
58
59
|
shoryuken_keys.include?(k)
|
|
@@ -67,7 +68,7 @@ module Shoryuken
|
|
|
67
68
|
aws_options = aws_options.merge(credentials: credentials) if credentials.set?
|
|
68
69
|
|
|
69
70
|
if (callback = Shoryuken.aws_initialization_callback)
|
|
70
|
-
Shoryuken.logger.info 'Calling Shoryuken.on_aws_initialization block'
|
|
71
|
+
Shoryuken.logger.info { 'Calling Shoryuken.on_aws_initialization block' }
|
|
71
72
|
callback.call(aws_options)
|
|
72
73
|
end
|
|
73
74
|
|
|
@@ -96,7 +97,7 @@ module Shoryuken
|
|
|
96
97
|
require File.expand_path('config/environment.rb')
|
|
97
98
|
end
|
|
98
99
|
|
|
99
|
-
Shoryuken.logger.info 'Rails environment loaded'
|
|
100
|
+
Shoryuken.logger.info { 'Rails environment loaded' }
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
def merge_cli_defined_queues
|
|
@@ -111,6 +112,21 @@ module Shoryuken
|
|
|
111
112
|
end
|
|
112
113
|
end
|
|
113
114
|
|
|
115
|
+
def prefix_active_job_queue_names
|
|
116
|
+
return unless @options[:rails]
|
|
117
|
+
return unless Shoryuken.active_job_queue_name_prefixing
|
|
118
|
+
|
|
119
|
+
queue_name_prefix = ::ActiveJob::Base.queue_name_prefix
|
|
120
|
+
queue_name_delimiter = ::ActiveJob::Base.queue_name_delimiter
|
|
121
|
+
|
|
122
|
+
# See https://github.com/rails/rails/blob/master/activejob/lib/active_job/queue_name.rb#L27
|
|
123
|
+
Shoryuken.options[:queues].to_a.map! do |queue_name, weight|
|
|
124
|
+
name_parts = [queue_name_prefix.presence, queue_name]
|
|
125
|
+
prefixed_queue_name = name_parts.compact.join(queue_name_delimiter)
|
|
126
|
+
[prefixed_queue_name, weight]
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
114
130
|
def parse_queue(queue, weight = nil)
|
|
115
131
|
[weight.to_i, 1].max.times { Shoryuken.queues << queue }
|
|
116
132
|
end
|
|
@@ -125,7 +141,7 @@ module Shoryuken
|
|
|
125
141
|
Shoryuken.worker_registry.queues.each do |queue|
|
|
126
142
|
Shoryuken.worker_registry.workers(queue).each do |worker_class|
|
|
127
143
|
if worker_class.instance_method(:perform).arity == 1
|
|
128
|
-
Shoryuken.logger.warn "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)"
|
|
144
|
+
Shoryuken.logger.warn { "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)" }
|
|
129
145
|
|
|
130
146
|
worker_class.class_eval do
|
|
131
147
|
alias_method :deprecated_perform, :perform
|
|
@@ -144,13 +160,13 @@ module Shoryuken
|
|
|
144
160
|
end
|
|
145
161
|
|
|
146
162
|
def validate_queues
|
|
147
|
-
Shoryuken.logger.warn 'No queues supplied' if Shoryuken.queues.empty?
|
|
163
|
+
Shoryuken.logger.warn { 'No queues supplied' } if Shoryuken.queues.empty?
|
|
148
164
|
|
|
149
165
|
Shoryuken.queues.uniq.each do |queue|
|
|
150
166
|
begin
|
|
151
167
|
Shoryuken::Client.queues queue
|
|
152
168
|
rescue Aws::SQS::Errors::NonExistentQueue
|
|
153
|
-
Shoryuken.logger.warn "AWS Queue '#{queue}' does not exist"
|
|
169
|
+
Shoryuken.logger.warn { "AWS Queue '#{queue}' does not exist" }
|
|
154
170
|
end
|
|
155
171
|
end
|
|
156
172
|
end
|
|
@@ -161,7 +177,7 @@ module Shoryuken
|
|
|
161
177
|
|
|
162
178
|
unless defined?(::ActiveJob)
|
|
163
179
|
(all_queues - queues_with_workers).each do |queue|
|
|
164
|
-
Shoryuken.logger.warn "No worker supplied for '#{queue}'"
|
|
180
|
+
Shoryuken.logger.warn { "No worker supplied for '#{queue}'" }
|
|
165
181
|
end
|
|
166
182
|
end
|
|
167
183
|
end
|
data/lib/shoryuken/fetcher.rb
CHANGED
|
@@ -13,7 +13,7 @@ module Shoryuken
|
|
|
13
13
|
# AWS limits the batch size by 10
|
|
14
14
|
limit = limit > FETCH_LIMIT ? FETCH_LIMIT : limit
|
|
15
15
|
|
|
16
|
-
options = Shoryuken.options[:aws][:receive_message].
|
|
16
|
+
options = (Shoryuken.options[:aws][:receive_message] || {}).dup
|
|
17
17
|
options[:max_number_of_messages] = limit
|
|
18
18
|
options[:message_attribute_names] = %w(All)
|
|
19
19
|
options[:attribute_names] = %w(All)
|
|
@@ -25,14 +25,14 @@ module Shoryuken
|
|
|
25
25
|
watchdog('Fetcher#fetch died') do
|
|
26
26
|
started_at = Time.now
|
|
27
27
|
|
|
28
|
-
logger.debug "Looking for new messages in '#{queue}'"
|
|
28
|
+
logger.debug { "Looking for new messages in '#{queue}'" }
|
|
29
29
|
|
|
30
30
|
begin
|
|
31
31
|
batch = Shoryuken.worker_registry.batch_receive_messages?(queue)
|
|
32
32
|
limit = batch ? FETCH_LIMIT : available_processors
|
|
33
33
|
|
|
34
34
|
if (sqs_msgs = Array(receive_messages(queue, limit))).any?
|
|
35
|
-
logger.info "Found #{sqs_msgs.size} messages for '#{queue}'"
|
|
35
|
+
logger.info { "Found #{sqs_msgs.size} messages for '#{queue}'" }
|
|
36
36
|
|
|
37
37
|
if batch
|
|
38
38
|
@manager.async.assign(queue, patch_sqs_msgs!(sqs_msgs))
|
|
@@ -42,7 +42,7 @@ module Shoryuken
|
|
|
42
42
|
|
|
43
43
|
@manager.async.rebalance_queue_weight!(queue)
|
|
44
44
|
else
|
|
45
|
-
logger.debug "No message found for '#{queue}'"
|
|
45
|
+
logger.debug { "No message found for '#{queue}'" }
|
|
46
46
|
|
|
47
47
|
@manager.async.pause_queue!(queue)
|
|
48
48
|
end
|
|
@@ -51,8 +51,8 @@ module Shoryuken
|
|
|
51
51
|
|
|
52
52
|
logger.debug { "Fetcher for '#{queue}' completed in #{elapsed(started_at)} ms" }
|
|
53
53
|
rescue => ex
|
|
54
|
-
logger.error "Error fetching message: #{ex}"
|
|
55
|
-
logger.error ex.backtrace.first
|
|
54
|
+
logger.error { "Error fetching message: #{ex}" }
|
|
55
|
+
logger.error { ex.backtrace.first }
|
|
56
56
|
|
|
57
57
|
@manager.async.dispatch
|
|
58
58
|
end
|
data/lib/shoryuken/launcher.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Shoryuken
|
|
|
36
36
|
|
|
37
37
|
def actor_died(actor, reason)
|
|
38
38
|
return if @done
|
|
39
|
-
logger.warn 'Shoryuken died due to the following error, cannot recover, process exiting'
|
|
39
|
+
logger.warn { 'Shoryuken died due to the following error, cannot recover, process exiting' }
|
|
40
40
|
exit 1
|
|
41
41
|
end
|
|
42
42
|
end
|
data/lib/shoryuken/manager.rb
CHANGED
|
@@ -23,7 +23,7 @@ module Shoryuken
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def start
|
|
26
|
-
logger.info 'Starting'
|
|
26
|
+
logger.info { 'Starting' }
|
|
27
27
|
|
|
28
28
|
dispatch
|
|
29
29
|
end
|
|
@@ -33,7 +33,7 @@ module Shoryuken
|
|
|
33
33
|
@done = true
|
|
34
34
|
|
|
35
35
|
if (callback = Shoryuken.stop_callback)
|
|
36
|
-
logger.info 'Calling Shoryuken.on_stop block'
|
|
36
|
+
logger.info { 'Calling Shoryuken.on_stop block' }
|
|
37
37
|
callback.call
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -58,7 +58,7 @@ module Shoryuken
|
|
|
58
58
|
|
|
59
59
|
def processor_done(queue, processor)
|
|
60
60
|
watchdog('Manager#processor_done died') do
|
|
61
|
-
logger.
|
|
61
|
+
logger.debug { "Process done for '#{queue}'" }
|
|
62
62
|
|
|
63
63
|
@threads.delete(processor.object_id)
|
|
64
64
|
@busy.delete processor
|
|
@@ -73,7 +73,7 @@ module Shoryuken
|
|
|
73
73
|
|
|
74
74
|
def processor_died(processor, reason)
|
|
75
75
|
watchdog("Manager#processor_died died") do
|
|
76
|
-
logger.error "Process died, reason: #{reason}" unless reason.to_s.empty?
|
|
76
|
+
logger.error { "Process died, reason: #{reason}" unless reason.to_s.empty? }
|
|
77
77
|
|
|
78
78
|
@threads.delete(processor.object_id)
|
|
79
79
|
@busy.delete processor
|
|
@@ -90,7 +90,7 @@ module Shoryuken
|
|
|
90
90
|
|
|
91
91
|
def assign(queue, sqs_msg)
|
|
92
92
|
watchdog('Manager#assign died') do
|
|
93
|
-
logger.
|
|
93
|
+
logger.debug { "Assigning #{sqs_msg.message_id}" }
|
|
94
94
|
|
|
95
95
|
processor = @ready.pop
|
|
96
96
|
@busy << processor
|
|
@@ -102,7 +102,7 @@ module Shoryuken
|
|
|
102
102
|
def rebalance_queue_weight!(queue)
|
|
103
103
|
watchdog('Manager#rebalance_queue_weight! died') do
|
|
104
104
|
if (original = original_queue_weight(queue)) > (current = current_queue_weight(queue))
|
|
105
|
-
logger.info "Increasing '#{queue}' weight to #{current + 1}, max: #{original}"
|
|
105
|
+
logger.info { "Increasing '#{queue}' weight to #{current + 1}, max: #{original}" }
|
|
106
106
|
|
|
107
107
|
@queues << queue
|
|
108
108
|
end
|
|
@@ -112,7 +112,7 @@ module Shoryuken
|
|
|
112
112
|
def pause_queue!(queue)
|
|
113
113
|
return if !@queues.include?(queue) || Shoryuken.options[:delay].to_f <= 0
|
|
114
114
|
|
|
115
|
-
logger.debug "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because it's empty"
|
|
115
|
+
logger.debug { "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because it's empty" }
|
|
116
116
|
|
|
117
117
|
@queues.delete(queue)
|
|
118
118
|
|
|
@@ -158,7 +158,7 @@ module Shoryuken
|
|
|
158
158
|
return if stopped?
|
|
159
159
|
|
|
160
160
|
unless @queues.include? queue
|
|
161
|
-
logger.debug "Restarting '#{queue}'"
|
|
161
|
+
logger.debug { "Restarting '#{queue}'" }
|
|
162
162
|
|
|
163
163
|
@queues << queue
|
|
164
164
|
|
|
@@ -192,7 +192,7 @@ module Shoryuken
|
|
|
192
192
|
|
|
193
193
|
unless defined?(::ActiveJob) || !Shoryuken.worker_registry.workers(queue).empty?
|
|
194
194
|
# when no worker registered pause the queue to avoid endless recursion
|
|
195
|
-
logger.debug "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because no workers registered"
|
|
195
|
+
logger.debug { "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because no workers registered" }
|
|
196
196
|
|
|
197
197
|
after(Shoryuken.options[:delay].to_f) { async.restart_queue!(queue) }
|
|
198
198
|
|
data/lib/shoryuken/message.rb
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
module Shoryuken
|
|
2
2
|
class Message
|
|
3
|
-
attr_accessor :client, :queue_url, :data
|
|
3
|
+
attr_accessor :client, :queue_url, :queue_name, :data
|
|
4
4
|
|
|
5
|
-
def initialize(client,
|
|
5
|
+
def initialize(client, queue, data)
|
|
6
6
|
self.client = client
|
|
7
|
-
self.queue_url = queue_url
|
|
8
7
|
self.data = data
|
|
8
|
+
|
|
9
|
+
if queue.is_a?(Shoryuken::Queue)
|
|
10
|
+
self.queue_url = queue.url
|
|
11
|
+
self.queue_name = queue.name
|
|
12
|
+
else
|
|
13
|
+
# TODO: Remove next major release
|
|
14
|
+
Shoryuken.loggger.warn do
|
|
15
|
+
'[DEPRECATION] Passing a queue url into Shoryuken::Message is deprecated, please pass the queue itself'
|
|
16
|
+
end
|
|
17
|
+
self.queue_url = queue
|
|
18
|
+
end
|
|
9
19
|
end
|
|
10
20
|
|
|
11
21
|
def delete
|
|
@@ -117,7 +117,7 @@ module Shoryuken
|
|
|
117
117
|
|
|
118
118
|
def patch_deprecated_middleware!(klass)
|
|
119
119
|
if klass.instance_method(:call).arity == 3
|
|
120
|
-
Shoryuken.logger.warn "[DEPRECATION] #{klass.name}#call(worker_instance, queue, sqs_msg) is deprecated. Please use #{klass.name}#call(worker_instance, queue, sqs_msg, body)"
|
|
120
|
+
Shoryuken.logger.warn { "[DEPRECATION] #{klass.name}#call(worker_instance, queue, sqs_msg) is deprecated. Please use #{klass.name}#call(worker_instance, queue, sqs_msg, body)" }
|
|
121
121
|
|
|
122
122
|
klass.class_eval do
|
|
123
123
|
alias_method :deprecated_call, :call
|
|
@@ -42,7 +42,7 @@ module Shoryuken
|
|
|
42
42
|
|
|
43
43
|
sqs_msg.change_visibility(visibility_timeout: interval.to_i)
|
|
44
44
|
|
|
45
|
-
logger.info "Message #{sqs_msg.message_id} failed, will be retried in #{interval} seconds."
|
|
45
|
+
logger.info { "Message #{sqs_msg.message_id} failed, will be retried in #{interval} seconds." }
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -9,19 +9,19 @@ module Shoryuken
|
|
|
9
9
|
begin
|
|
10
10
|
started_at = Time.now
|
|
11
11
|
|
|
12
|
-
logger.info "started at #{started_at}"
|
|
12
|
+
logger.info { "started at #{started_at}" }
|
|
13
13
|
|
|
14
14
|
yield
|
|
15
15
|
|
|
16
16
|
total_time = elapsed(started_at)
|
|
17
17
|
|
|
18
18
|
if (total_time / 1000.0) > (timeout = Shoryuken::Client.queues(queue).visibility_timeout)
|
|
19
|
-
logger.warn "exceeded the queue visibility timeout by #{total_time - (timeout * 1000)} ms"
|
|
19
|
+
logger.warn { "exceeded the queue visibility timeout by #{total_time - (timeout * 1000)} ms" }
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
logger.info "completed in: #{total_time} ms"
|
|
22
|
+
logger.info { "completed in: #{total_time} ms" }
|
|
23
23
|
rescue => e
|
|
24
|
-
logger.info "failed in: #{elapsed(started_at)} ms"
|
|
24
|
+
logger.info { "failed in: #{elapsed(started_at)} ms" }
|
|
25
25
|
raise e
|
|
26
26
|
end
|
|
27
27
|
end
|
data/lib/shoryuken/processor.rb
CHANGED
|
@@ -19,12 +19,10 @@ module Shoryuken
|
|
|
19
19
|
timer = auto_visibility_timeout(queue, sqs_msg, worker.class)
|
|
20
20
|
|
|
21
21
|
begin
|
|
22
|
-
|
|
23
|
-
body = get_body(worker.class, sqs_msg)
|
|
22
|
+
body = get_body(worker.class, sqs_msg)
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
24
|
+
worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
|
|
25
|
+
worker.perform(sqs_msg, body)
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
@manager.async.processor_done(queue, current_actor)
|
|
@@ -35,22 +33,31 @@ module Shoryuken
|
|
|
35
33
|
|
|
36
34
|
private
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
class MessageVisibilityExtender
|
|
37
|
+
include Celluloid
|
|
38
|
+
include Util
|
|
39
|
+
|
|
40
|
+
def auto_extend(queue, sqs_msg, worker_class)
|
|
40
41
|
queue_visibility_timeout = Shoryuken::Client.queues(queue).visibility_timeout
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
every(queue_visibility_timeout - 5) do
|
|
43
44
|
begin
|
|
44
|
-
logger.debug "Extending message #{worker_name(worker_class, sqs_msg)}/#{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s."
|
|
45
|
+
logger.debug { "Extending message #{worker_name(worker_class, sqs_msg)}/#{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s." }
|
|
45
46
|
|
|
46
47
|
sqs_msg.visibility_timeout = queue_visibility_timeout
|
|
47
48
|
rescue => e
|
|
48
|
-
logger.error "Could not auto extend the message #{worker_class}/#{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{e.message}"
|
|
49
|
+
logger.error { "Could not auto extend the message #{worker_class}/#{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{e.message}" }
|
|
49
50
|
end
|
|
50
51
|
end
|
|
51
52
|
end
|
|
53
|
+
end
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
def auto_visibility_timeout(queue, sqs_msg, worker_class)
|
|
56
|
+
return unless worker_class.auto_visibility_timeout?
|
|
57
|
+
|
|
58
|
+
@visibility_extender ||= MessageVisibilityExtender.new_link
|
|
59
|
+
|
|
60
|
+
@visibility_extender.auto_extend(queue, sqs_msg, worker_class)
|
|
54
61
|
end
|
|
55
62
|
|
|
56
63
|
def get_body(worker_class, sqs_msg)
|
|
@@ -72,11 +79,18 @@ module Shoryuken
|
|
|
72
79
|
when :text, nil
|
|
73
80
|
sqs_msg.body
|
|
74
81
|
else
|
|
75
|
-
|
|
82
|
+
if body_parser.respond_to?(:parse)
|
|
83
|
+
# JSON.parse
|
|
84
|
+
body_parser.parse(sqs_msg.body)
|
|
85
|
+
elsif body_parser.respond_to?(:load)
|
|
86
|
+
# see https://github.com/phstc/shoryuken/pull/91
|
|
87
|
+
# JSON.load
|
|
88
|
+
body_parser.load(sqs_msg.body)
|
|
89
|
+
end
|
|
76
90
|
end
|
|
77
91
|
rescue => e
|
|
78
|
-
logger.error "Error parsing the message body: #{e.message}\nbody_parser: #{body_parser}\nsqs_msg.body: #{sqs_msg.body}"
|
|
79
|
-
|
|
92
|
+
logger.error { "Error parsing the message body: #{e.message}\nbody_parser: #{body_parser}\nsqs_msg.body: #{sqs_msg.body}" }
|
|
93
|
+
raise
|
|
80
94
|
end
|
|
81
95
|
end
|
|
82
96
|
end
|
data/lib/shoryuken/queue.rb
CHANGED
|
@@ -20,31 +20,62 @@ module Shoryuken
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def send_message(options)
|
|
23
|
-
|
|
23
|
+
options = sanitize_message!(options).merge(queue_url: url)
|
|
24
|
+
|
|
25
|
+
Shoryuken.client_middleware.invoke(options) do
|
|
26
|
+
client.send_message(options)
|
|
27
|
+
end
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
def send_messages(options)
|
|
27
|
-
client.send_message_batch(
|
|
31
|
+
client.send_message_batch(sanitize_messages!(options).merge(queue_url: url))
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def receive_messages(options)
|
|
31
35
|
client.receive_message(options.merge(queue_url: url)).
|
|
32
36
|
messages.
|
|
33
|
-
map { |m| Message.new(client,
|
|
37
|
+
map { |m| Message.new(client, self, m) }
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
private
|
|
37
41
|
|
|
38
|
-
def
|
|
39
|
-
|
|
42
|
+
def sanitize_messages!(options)
|
|
43
|
+
options = case
|
|
44
|
+
when options.is_a?(Array)
|
|
45
|
+
{ entries: options.map.with_index { |m, index| { id: index.to_s, message_body: m } } }
|
|
46
|
+
when options.is_a?(Hash)
|
|
47
|
+
options
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
validate_messages!(options)
|
|
51
|
+
|
|
52
|
+
options
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def sanitize_message!(options)
|
|
56
|
+
options = case
|
|
57
|
+
when options.is_a?(String)
|
|
58
|
+
# send_message('message')
|
|
59
|
+
{ message_body: options }
|
|
60
|
+
when options.is_a?(Hash)
|
|
61
|
+
options
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
validate_message!(options)
|
|
65
|
+
|
|
66
|
+
options
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def validate_messages!(options)
|
|
70
|
+
options[:entries].map { |m| validate_message!(m) }
|
|
71
|
+
end
|
|
40
72
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
end
|
|
73
|
+
def validate_message!(options)
|
|
74
|
+
body = options[:message_body]
|
|
75
|
+
if body.is_a?(Hash)
|
|
76
|
+
options[:message_body] = JSON.dump(body)
|
|
77
|
+
elsif !body.is_a?(String)
|
|
78
|
+
fail ArgumentError, "The message body must be a String and you passed a #{body.class}"
|
|
48
79
|
end
|
|
49
80
|
|
|
50
81
|
options
|
data/lib/shoryuken/util.rb
CHANGED
|
@@ -3,9 +3,9 @@ module Shoryuken
|
|
|
3
3
|
def watchdog(last_words)
|
|
4
4
|
yield
|
|
5
5
|
rescue => ex
|
|
6
|
-
logger.error last_words
|
|
7
|
-
logger.error ex
|
|
8
|
-
logger.error ex.backtrace.join("\n")
|
|
6
|
+
logger.error { last_words }
|
|
7
|
+
logger.error { ex }
|
|
8
|
+
logger.error { ex.backtrace.join("\n") }
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def logger
|
|
@@ -30,7 +30,7 @@ module Shoryuken
|
|
|
30
30
|
&& sqs_msg.message_attributes['shoryuken_class'] \
|
|
31
31
|
&& sqs_msg.message_attributes['shoryuken_class'][:string_value] == ActiveJob::QueueAdapters::ShoryukenAdapter::JobWrapper.to_s
|
|
32
32
|
|
|
33
|
-
"ActiveJob/#{body['job_class']
|
|
33
|
+
"ActiveJob/#{body['job_class']}"
|
|
34
34
|
else
|
|
35
35
|
worker_class.to_s
|
|
36
36
|
end
|
data/lib/shoryuken/version.rb
CHANGED
data/lib/shoryuken/worker.rb
CHANGED
|
@@ -39,7 +39,7 @@ module Shoryuken
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def shoryuken_options(opts = {})
|
|
42
|
-
@shoryuken_options = get_shoryuken_options.merge(stringify_keys(
|
|
42
|
+
@shoryuken_options = get_shoryuken_options.merge(stringify_keys(opts || {}))
|
|
43
43
|
queue = @shoryuken_options['queue']
|
|
44
44
|
if queue.respond_to? :call
|
|
45
45
|
queue = queue.call
|
data/lib/shoryuken.rb
CHANGED
|
@@ -29,8 +29,9 @@ module Shoryuken
|
|
|
29
29
|
timeout: 8
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
@@queues
|
|
32
|
+
@@queues = []
|
|
33
33
|
@@worker_registry = DefaultWorkerRegistry.new
|
|
34
|
+
@@active_job_queue_name_prefixing = false
|
|
34
35
|
|
|
35
36
|
class << self
|
|
36
37
|
def options
|
|
@@ -57,11 +58,14 @@ module Shoryuken
|
|
|
57
58
|
@@worker_registry
|
|
58
59
|
end
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
def active_job_queue_name_prefixing
|
|
62
|
+
@@active_job_queue_name_prefixing
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def active_job_queue_name_prefixing=(prefixing)
|
|
66
|
+
@@active_job_queue_name_prefixing = prefixing
|
|
67
|
+
end
|
|
68
|
+
|
|
65
69
|
def configure_server
|
|
66
70
|
yield self if server?
|
|
67
71
|
end
|
|
@@ -72,6 +76,16 @@ module Shoryuken
|
|
|
72
76
|
@server_chain
|
|
73
77
|
end
|
|
74
78
|
|
|
79
|
+
def configure_client
|
|
80
|
+
yield self
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def client_middleware
|
|
84
|
+
@client_chain ||= default_client_middleware
|
|
85
|
+
yield @client_chain if block_given?
|
|
86
|
+
@client_chain
|
|
87
|
+
end
|
|
88
|
+
|
|
75
89
|
def default_worker_options
|
|
76
90
|
@@default_worker_options ||= {
|
|
77
91
|
'queue' => 'default',
|
|
@@ -116,6 +130,10 @@ module Shoryuken
|
|
|
116
130
|
end
|
|
117
131
|
end
|
|
118
132
|
|
|
133
|
+
def default_client_middleware
|
|
134
|
+
Middleware::Chain.new
|
|
135
|
+
end
|
|
136
|
+
|
|
119
137
|
def server?
|
|
120
138
|
defined?(Shoryuken::CLI)
|
|
121
139
|
end
|
|
@@ -5,12 +5,13 @@ describe Shoryuken::Client do
|
|
|
5
5
|
let(:sqs) { Aws::SQS::Client.new(stub_responses: true, credentials: credentials) }
|
|
6
6
|
let(:queue_name) { 'shoryuken' }
|
|
7
7
|
let(:queue_url) { 'https://eu-west-1.amazonaws.com:6059/123456789012/shoryuken' }
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
described_class.sqs = sqs
|
|
11
|
-
end
|
|
8
|
+
let(:sqs_endpoint) { 'http://localhost:4568' }
|
|
9
|
+
let(:sns_endpoint) { 'http://0.0.0.0:4568' }
|
|
12
10
|
|
|
13
11
|
describe '.queue' do
|
|
12
|
+
before do
|
|
13
|
+
described_class.sqs = sqs
|
|
14
|
+
end
|
|
14
15
|
it 'memoizes queues' do
|
|
15
16
|
sqs.stub_responses(:get_queue_url, { queue_url: queue_url }, { queue_url: 'xyz' })
|
|
16
17
|
|
|
@@ -18,4 +19,44 @@ describe Shoryuken::Client do
|
|
|
18
19
|
expect(Shoryuken::Client.queues(queue_name).url).to eq queue_url
|
|
19
20
|
end
|
|
20
21
|
end
|
|
22
|
+
|
|
23
|
+
describe 'environment variable endpoints' do
|
|
24
|
+
before do
|
|
25
|
+
ENV['AWS_SQS_ENDPOINT'] = sqs_endpoint
|
|
26
|
+
ENV['AWS_SNS_ENDPOINT'] = sns_endpoint
|
|
27
|
+
ENV['AWS_REGION'] = 'us-east-1'
|
|
28
|
+
Shoryuken.options[:aws] = {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'will use config file settings if set' do
|
|
32
|
+
load_config_file_by_file_name('shoryuken_endpoint.yml')
|
|
33
|
+
expect(described_class.sqs.config.endpoint.to_s).to eql('https://github.com/phstc/shoryuken:4568')
|
|
34
|
+
expect(described_class.sns.config.endpoint.to_s).to eq('http://127.0.0.1:4568')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should fallback to environment variable if config file not found or set' do
|
|
38
|
+
load_config_file_by_file_name(nil)
|
|
39
|
+
expect(described_class.sqs.config.endpoint.to_s).to eql(sqs_endpoint)
|
|
40
|
+
expect(described_class.sns.config.endpoint.to_s).to eq(sns_endpoint)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'should fallback to environment variable if config file found but settings not set' do
|
|
44
|
+
load_config_file_by_file_name('shoryuken.yml')
|
|
45
|
+
expect(described_class.sqs.config.endpoint.to_s).to eql(sqs_endpoint)
|
|
46
|
+
expect(described_class.sns.config.endpoint.to_s).to eq(sns_endpoint)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'will fallback to default settings if no config file settings or environment variables found' do
|
|
50
|
+
ENV['AWS_SQS_ENDPOINT'] = nil
|
|
51
|
+
ENV['AWS_SNS_ENDPOINT'] = nil
|
|
52
|
+
load_config_file_by_file_name('shoryuken.yml')
|
|
53
|
+
expect(described_class.sqs.config.endpoint.to_s).to eql('https://sqs.us-east-1.amazonaws.com')
|
|
54
|
+
expect(described_class.sns.config.endpoint.to_s).to eq('https://sns.us-east-1.amazonaws.com')
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def load_config_file_by_file_name(file_name)
|
|
59
|
+
path_name = file_name ? File.join(File.expand_path('../../..', __FILE__), 'spec', file_name) : nil
|
|
60
|
+
Shoryuken::EnvironmentLoader.load(config_file: path_name)
|
|
61
|
+
end
|
|
21
62
|
end
|
|
@@ -58,7 +58,9 @@ describe Shoryuken::Middleware::Chain do
|
|
|
58
58
|
it 'patches deprecated middleware' do
|
|
59
59
|
subject.clear
|
|
60
60
|
|
|
61
|
-
expect(Shoryuken.logger).to receive(:warn)
|
|
61
|
+
expect(Shoryuken.logger).to receive(:warn) do |&block|
|
|
62
|
+
expect(block.call).to eq('[DEPRECATION] DeprecatedMiddleware#call(worker_instance, queue, sqs_msg) is deprecated. Please use DeprecatedMiddleware#call(worker_instance, queue, sqs_msg, body)')
|
|
63
|
+
end
|
|
62
64
|
|
|
63
65
|
subject.add DeprecatedMiddleware
|
|
64
66
|
|
|
@@ -16,8 +16,12 @@ describe Shoryuken::Middleware::Server::Timing do
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it 'logs timing' do
|
|
19
|
-
expect(Shoryuken.logger).to receive(:info)
|
|
20
|
-
|
|
19
|
+
expect(Shoryuken.logger).to receive(:info) do |&block|
|
|
20
|
+
expect(block.call).to match(/started at/)
|
|
21
|
+
end
|
|
22
|
+
expect(Shoryuken.logger).to receive(:info) do |&block|
|
|
23
|
+
expect(block.call).to match(/completed in/)
|
|
24
|
+
end
|
|
21
25
|
|
|
22
26
|
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) {}
|
|
23
27
|
end
|
|
@@ -26,9 +30,15 @@ describe Shoryuken::Middleware::Server::Timing do
|
|
|
26
30
|
it 'logs exceeded' do
|
|
27
31
|
allow(subject).to receive(:elapsed).and_return(120000)
|
|
28
32
|
|
|
29
|
-
expect(Shoryuken.logger).to receive(:info)
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
expect(Shoryuken.logger).to receive(:info) do |&block|
|
|
34
|
+
expect(block.call).to match(/started at/)
|
|
35
|
+
end
|
|
36
|
+
expect(Shoryuken.logger).to receive(:info) do |&block|
|
|
37
|
+
expect(block.call).to match(/completed in/)
|
|
38
|
+
end
|
|
39
|
+
expect(Shoryuken.logger).to receive(:warn) do |&block|
|
|
40
|
+
expect(block.call).to match('exceeded the queue visibility timeout by 60000 ms')
|
|
41
|
+
end
|
|
32
42
|
|
|
33
43
|
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) {}
|
|
34
44
|
end
|
|
@@ -36,8 +46,12 @@ describe Shoryuken::Middleware::Server::Timing do
|
|
|
36
46
|
|
|
37
47
|
context 'when exception' do
|
|
38
48
|
it 'logs failed in' do
|
|
39
|
-
expect(Shoryuken.logger).to receive(:info)
|
|
40
|
-
|
|
49
|
+
expect(Shoryuken.logger).to receive(:info) do |&block|
|
|
50
|
+
expect(block.call).to match(/started at/)
|
|
51
|
+
end
|
|
52
|
+
expect(Shoryuken.logger).to receive(:info) do |&block|
|
|
53
|
+
expect(block.call).to match(/failed in/)
|
|
54
|
+
end
|
|
41
55
|
|
|
42
56
|
expect {
|
|
43
57
|
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise }
|
|
@@ -59,8 +59,28 @@ describe Shoryuken::Processor do
|
|
|
59
59
|
subject.process(queue, sqs_msg)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
it 'parses calling `.load`' do
|
|
63
|
+
TestWorker.get_shoryuken_options['body_parser'] = Class.new do
|
|
64
|
+
def self.load(*args)
|
|
65
|
+
JSON.load(*args)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
body = { 'test' => 'hi' }
|
|
70
|
+
|
|
71
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
72
|
+
|
|
73
|
+
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
74
|
+
|
|
75
|
+
subject.process(queue, sqs_msg)
|
|
76
|
+
end
|
|
77
|
+
|
|
62
78
|
it 'parses calling `.parse`' do
|
|
63
|
-
TestWorker.get_shoryuken_options['body_parser'] =
|
|
79
|
+
TestWorker.get_shoryuken_options['body_parser'] = Class.new do
|
|
80
|
+
def self.parse(*args)
|
|
81
|
+
JSON.parse(*args)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
64
84
|
|
|
65
85
|
body = { 'test' => 'hi' }
|
|
66
86
|
|
|
@@ -72,16 +92,22 @@ describe Shoryuken::Processor do
|
|
|
72
92
|
end
|
|
73
93
|
|
|
74
94
|
context 'when parse errors' do
|
|
75
|
-
|
|
95
|
+
before do
|
|
76
96
|
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
77
97
|
|
|
78
|
-
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, nil)
|
|
79
|
-
|
|
80
98
|
allow(sqs_msg).to receive(:body).and_return('invalid json')
|
|
99
|
+
end
|
|
81
100
|
|
|
82
|
-
|
|
101
|
+
it 'logs the error' do
|
|
102
|
+
expect(subject.logger).to receive(:error) do |&block|
|
|
103
|
+
expect(block.call).to eq("Error parsing the message body: 757: unexpected token at 'invalid json'\nbody_parser: json\nsqs_msg.body: invalid json")
|
|
104
|
+
end
|
|
83
105
|
|
|
84
|
-
subject.process(queue, sqs_msg)
|
|
106
|
+
subject.process(queue, sqs_msg) rescue nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 're raises the error' do
|
|
110
|
+
expect{ subject.process(queue, sqs_msg) }.to raise_error(JSON::ParserError, "757: unexpected token at 'invalid json'")
|
|
85
111
|
end
|
|
86
112
|
end
|
|
87
113
|
|
|
@@ -9,6 +9,19 @@ describe Shoryuken::Queue do
|
|
|
9
9
|
subject { described_class.new(sqs, queue_name) }
|
|
10
10
|
|
|
11
11
|
describe '#send_message' do
|
|
12
|
+
it 'accepts SQS request parameters' do
|
|
13
|
+
# https://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#send_message-instance_method
|
|
14
|
+
expect(sqs).to receive(:send_message).with(hash_including(message_body: 'msg1'))
|
|
15
|
+
|
|
16
|
+
subject.send_message(message_body: 'msg1')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'accepts a string' do
|
|
20
|
+
expect(sqs).to receive(:send_message).with(hash_including(message_body: 'msg1'))
|
|
21
|
+
|
|
22
|
+
subject.send_message('msg1')
|
|
23
|
+
end
|
|
24
|
+
|
|
12
25
|
context 'when body is invalid' do
|
|
13
26
|
it 'raises ArgumentError for nil' do
|
|
14
27
|
expect {
|
|
@@ -21,10 +34,55 @@ describe Shoryuken::Queue do
|
|
|
21
34
|
subject.send_message(message_body: 1)
|
|
22
35
|
}.to raise_error(ArgumentError, 'The message body must be a String and you passed a Fixnum')
|
|
23
36
|
end
|
|
37
|
+
|
|
38
|
+
context 'when a client middleware' do
|
|
39
|
+
class MyClientMiddleware
|
|
40
|
+
def call(options)
|
|
41
|
+
options[:message_body] = 'changed'
|
|
42
|
+
|
|
43
|
+
yield
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
before do
|
|
48
|
+
Shoryuken.configure_client do |config|
|
|
49
|
+
config.client_middleware do |chain|
|
|
50
|
+
chain.add MyClientMiddleware
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
after do
|
|
56
|
+
Shoryuken.configure_client do |config|
|
|
57
|
+
config.client_middleware do |chain|
|
|
58
|
+
chain.remove MyClientMiddleware
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'invokes MyClientMiddleware' do
|
|
64
|
+
expect(sqs).to receive(:send_message).with(hash_including(message_body: 'changed'))
|
|
65
|
+
|
|
66
|
+
subject.send_message(message_body: 'original')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
24
69
|
end
|
|
25
70
|
end
|
|
26
71
|
|
|
27
72
|
describe '#send_messages' do
|
|
73
|
+
it 'accepts SQS request parameters' do
|
|
74
|
+
# https://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#send_message_batch-instance_method
|
|
75
|
+
expect(sqs).to receive(:send_message_batch).with(hash_including(entries: [{ id: '0', message_body: 'msg1'}, { id: '1', message_body: 'msg2' }]))
|
|
76
|
+
|
|
77
|
+
subject.send_messages(entries: [{ id: '0', message_body: 'msg1'}, { id: '1', message_body: 'msg2' }])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'accepts an array of string' do
|
|
81
|
+
expect(sqs).to receive(:send_message_batch).with(hash_including(entries: [{ id: '0', message_body: 'msg1'}, { id: '1', message_body: 'msg2' }]))
|
|
82
|
+
|
|
83
|
+
subject.send_messages(%w(msg1 msg2))
|
|
84
|
+
end
|
|
85
|
+
|
|
28
86
|
context 'when body is invalid' do
|
|
29
87
|
it 'raises ArgumentError for nil' do
|
|
30
88
|
expect {
|
data/spec/spec_helper.rb
CHANGED
|
@@ -39,7 +39,10 @@ class TestWorker
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
RSpec.configure do |config|
|
|
42
|
-
|
|
42
|
+
# Only run slow tests if SPEC_ALL=true and AWS_ACCESS_KEY_ID is present
|
|
43
|
+
# The AWS_ACCESS_KEY_ID checker is because Travis CI does not expose ENV variables to pull requests from forked repositories
|
|
44
|
+
# http://docs.travis-ci.com/user/pull-requests/
|
|
45
|
+
config.filter_run_excluding slow: true if ENV['SPEC_ALL'] != 'true' || ENV['AWS_ACCESS_KEY_ID'].nil?
|
|
43
46
|
|
|
44
47
|
config.before do
|
|
45
48
|
Shoryuken::Client.class_variable_set :@@queues, {}
|
metadata
CHANGED
|
@@ -1,125 +1,125 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shoryuken
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pablo Cantero
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-09-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - ~>
|
|
17
|
+
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '1.6'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - ~>
|
|
24
|
+
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.6'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- -
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- -
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rspec
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- -
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: '0'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- -
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: pry-byebug
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- -
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
61
|
version: '0'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- -
|
|
66
|
+
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: nokogiri
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
|
-
- -
|
|
73
|
+
- - ">="
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
75
|
version: '0'
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
|
-
- -
|
|
80
|
+
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: dotenv
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- -
|
|
87
|
+
- - ">="
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
89
|
version: '0'
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
|
-
- -
|
|
94
|
+
- - ">="
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: aws-sdk-core
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
|
-
- - ~>
|
|
101
|
+
- - "~>"
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
103
|
version: 2.0.21
|
|
104
104
|
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
|
-
- - ~>
|
|
108
|
+
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: 2.0.21
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
112
|
name: celluloid
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
114
114
|
requirements:
|
|
115
|
-
- - ~>
|
|
115
|
+
- - "~>"
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
117
|
version: 0.16.0
|
|
118
118
|
type: :runtime
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
121
|
requirements:
|
|
122
|
-
- - ~>
|
|
122
|
+
- - "~>"
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: 0.16.0
|
|
125
125
|
description: Shoryuken is a super efficient AWS SQS thread based message processor
|
|
@@ -130,11 +130,12 @@ executables:
|
|
|
130
130
|
extensions: []
|
|
131
131
|
extra_rdoc_files: []
|
|
132
132
|
files:
|
|
133
|
-
- .gitignore
|
|
134
|
-
- .hound.yml
|
|
135
|
-
- .rspec
|
|
136
|
-
- .rubocop.yml
|
|
137
|
-
- .travis.yml
|
|
133
|
+
- ".gitignore"
|
|
134
|
+
- ".hound.yml"
|
|
135
|
+
- ".rspec"
|
|
136
|
+
- ".rubocop.yml"
|
|
137
|
+
- ".travis.yml"
|
|
138
|
+
- CHANGELOG.md
|
|
138
139
|
- Gemfile
|
|
139
140
|
- LICENSE.txt
|
|
140
141
|
- README.md
|
|
@@ -186,6 +187,7 @@ files:
|
|
|
186
187
|
- spec/shoryuken/topic_spec.rb
|
|
187
188
|
- spec/shoryuken/util_spec.rb
|
|
188
189
|
- spec/shoryuken/worker_spec.rb
|
|
190
|
+
- spec/shoryuken_endpoint.yml
|
|
189
191
|
- spec/shoryuken_spec.rb
|
|
190
192
|
- spec/spec_helper.rb
|
|
191
193
|
homepage: https://github.com/phstc/shoryuken
|
|
@@ -198,17 +200,17 @@ require_paths:
|
|
|
198
200
|
- lib
|
|
199
201
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
202
|
requirements:
|
|
201
|
-
- -
|
|
203
|
+
- - ">="
|
|
202
204
|
- !ruby/object:Gem::Version
|
|
203
205
|
version: '0'
|
|
204
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
207
|
requirements:
|
|
206
|
-
- -
|
|
208
|
+
- - ">="
|
|
207
209
|
- !ruby/object:Gem::Version
|
|
208
210
|
version: '0'
|
|
209
211
|
requirements: []
|
|
210
212
|
rubyforge_project:
|
|
211
|
-
rubygems_version: 2.
|
|
213
|
+
rubygems_version: 2.2.5
|
|
212
214
|
signing_key:
|
|
213
215
|
specification_version: 4
|
|
214
216
|
summary: Shoryuken is a super efficient AWS SQS thread based message processor
|
|
@@ -230,5 +232,6 @@ test_files:
|
|
|
230
232
|
- spec/shoryuken/topic_spec.rb
|
|
231
233
|
- spec/shoryuken/util_spec.rb
|
|
232
234
|
- spec/shoryuken/worker_spec.rb
|
|
235
|
+
- spec/shoryuken_endpoint.yml
|
|
233
236
|
- spec/shoryuken_spec.rb
|
|
234
237
|
- spec/spec_helper.rb
|