shoryuken 3.1.1 → 3.2.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/.rubocop.yml +1 -1
- data/.travis.yml +13 -3
- data/CHANGELOG.md +82 -1
- data/Gemfile +5 -2
- data/Gemfile.aws-sdk-core-v2 +13 -0
- data/bin/cli/sqs.rb +8 -4
- data/bin/shoryuken +2 -1
- data/lib/shoryuken/body_parser.rb +27 -0
- data/lib/shoryuken/environment_loader.rb +25 -11
- data/lib/shoryuken/fetcher.rb +40 -13
- data/lib/shoryuken/launcher.rb +4 -20
- data/lib/shoryuken/logging.rb +0 -5
- data/lib/shoryuken/manager.rb +31 -22
- data/lib/shoryuken/middleware/chain.rb +4 -0
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +2 -5
- data/lib/shoryuken/middleware/server/timing.rb +12 -14
- data/lib/shoryuken/options.rb +25 -1
- data/lib/shoryuken/processor.rb +9 -21
- data/lib/shoryuken/queue.rb +12 -5
- data/lib/shoryuken/runner.rb +3 -1
- data/lib/shoryuken/util.rb +3 -3
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker/default_executor.rb +33 -0
- data/lib/shoryuken/worker/inline_executor.rb +28 -0
- data/lib/shoryuken/worker.rb +68 -31
- data/lib/shoryuken.rb +14 -1
- data/shoryuken.gemspec +1 -1
- data/spec/shoryuken/body_parser_spec.rb +89 -0
- data/spec/shoryuken/environment_loader_spec.rb +32 -3
- data/spec/shoryuken/fetcher_spec.rb +61 -9
- data/spec/shoryuken/manager_spec.rb +31 -18
- data/spec/shoryuken/middleware/chain_spec.rb +16 -4
- data/spec/shoryuken/middleware/server/timing_spec.rb +5 -3
- data/spec/shoryuken/options_spec.rb +80 -0
- data/spec/shoryuken/processor_spec.rb +15 -97
- data/spec/shoryuken/queue_spec.rb +43 -32
- data/spec/shoryuken/util_spec.rb +25 -1
- data/spec/shoryuken/worker/default_executor_spec.rb +100 -0
- data/spec/shoryuken/worker/inline_executor_spec.rb +23 -0
- data/spec/shoryuken/worker_spec.rb +32 -91
- data/spec/spec_helper.rb +5 -0
- metadata +15 -5
data/lib/shoryuken/options.rb
CHANGED
|
@@ -21,8 +21,13 @@ module Shoryuken
|
|
|
21
21
|
@@sqs_client_receive_message_opts = {}
|
|
22
22
|
@@start_callback = nil
|
|
23
23
|
@@stop_callback = nil
|
|
24
|
+
@@worker_executor = Worker::DefaultExecutor
|
|
24
25
|
|
|
25
26
|
class << self
|
|
27
|
+
def active_job?
|
|
28
|
+
defined?(::ActiveJob)
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
def add_group(group, concurrency)
|
|
27
32
|
groups[group] ||= {
|
|
28
33
|
concurrency: concurrency,
|
|
@@ -52,8 +57,27 @@ module Shoryuken
|
|
|
52
57
|
@@worker_registry = worker_registry
|
|
53
58
|
end
|
|
54
59
|
|
|
60
|
+
def worker_executor
|
|
61
|
+
@@worker_executor
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def worker_executor=(worker_executor)
|
|
65
|
+
@@worker_executor = worker_executor
|
|
66
|
+
end
|
|
67
|
+
|
|
55
68
|
def polling_strategy(group)
|
|
56
|
-
options[group].to_h
|
|
69
|
+
strategy = (group == 'default' ? options : options[:groups].to_h[group]).to_h[:polling_strategy]
|
|
70
|
+
|
|
71
|
+
case strategy
|
|
72
|
+
when 'WeightedRoundRobin', nil # Default case
|
|
73
|
+
Polling::WeightedRoundRobin
|
|
74
|
+
when 'StrictPriority'
|
|
75
|
+
Polling::StrictPriority
|
|
76
|
+
when Class
|
|
77
|
+
strategy
|
|
78
|
+
else
|
|
79
|
+
raise ArgumentError, "#{strategy} is not a valid polling_strategy"
|
|
80
|
+
end
|
|
57
81
|
end
|
|
58
82
|
|
|
59
83
|
def start_callback
|
data/lib/shoryuken/processor.rb
CHANGED
|
@@ -4,6 +4,10 @@ module Shoryuken
|
|
|
4
4
|
|
|
5
5
|
attr_reader :queue, :sqs_msg
|
|
6
6
|
|
|
7
|
+
def self.process(queue, sqs_msg)
|
|
8
|
+
new(queue, sqs_msg).process
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def initialize(queue, sqs_msg)
|
|
8
12
|
@queue = queue
|
|
9
13
|
@sqs_msg = sqs_msg
|
|
@@ -12,8 +16,10 @@ module Shoryuken
|
|
|
12
16
|
def process
|
|
13
17
|
return logger.error { "No worker found for #{queue}" } unless worker
|
|
14
18
|
|
|
15
|
-
worker.class
|
|
16
|
-
worker.
|
|
19
|
+
Shoryuken::Logging.with_context("#{worker_name(worker.class, sqs_msg, body)}/#{queue}/#{sqs_msg.message_id}") do
|
|
20
|
+
worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
|
|
21
|
+
worker.perform(sqs_msg, body)
|
|
22
|
+
end
|
|
17
23
|
end
|
|
18
24
|
rescue Exception => ex
|
|
19
25
|
logger.error { "Processor failed: #{ex.message}" }
|
|
@@ -37,25 +43,7 @@ module Shoryuken
|
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
def parse_body(sqs_msg)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
case body_parser
|
|
43
|
-
when :json
|
|
44
|
-
JSON.parse(sqs_msg.body)
|
|
45
|
-
when Proc
|
|
46
|
-
body_parser.call(sqs_msg)
|
|
47
|
-
when :text, nil
|
|
48
|
-
sqs_msg.body
|
|
49
|
-
else
|
|
50
|
-
if body_parser.respond_to?(:parse)
|
|
51
|
-
# JSON.parse
|
|
52
|
-
body_parser.parse(sqs_msg.body)
|
|
53
|
-
elsif body_parser.respond_to?(:load)
|
|
54
|
-
# see https://github.com/phstc/shoryuken/pull/91
|
|
55
|
-
# JSON.load
|
|
56
|
-
body_parser.load(sqs_msg.body)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
46
|
+
BodyParser.parse(worker_class, sqs_msg)
|
|
59
47
|
end
|
|
60
48
|
end
|
|
61
49
|
end
|
data/lib/shoryuken/queue.rb
CHANGED
|
@@ -49,20 +49,27 @@ module Shoryuken
|
|
|
49
49
|
|
|
50
50
|
private
|
|
51
51
|
|
|
52
|
-
def
|
|
52
|
+
def set_by_name(name)
|
|
53
53
|
self.name = name
|
|
54
54
|
self.url = client.get_queue_url(queue_name: name).queue_url
|
|
55
|
-
rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => ex
|
|
56
|
-
raise ex, "The specified queue #{name} does not exist."
|
|
57
55
|
end
|
|
58
56
|
|
|
59
|
-
def
|
|
57
|
+
def set_by_url(url)
|
|
60
58
|
self.name = url.split('/').last
|
|
61
59
|
self.url = url
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
def set_name_and_url(name_or_url)
|
|
65
|
-
name_or_url.start_with?('https://sqs.')
|
|
63
|
+
if name_or_url.start_with?('https://sqs.')
|
|
64
|
+
set_by_url(name_or_url)
|
|
65
|
+
|
|
66
|
+
# anticipate the fifo? checker for validating the queue URL
|
|
67
|
+
return fifo?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
set_by_name(name_or_url)
|
|
71
|
+
rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => ex
|
|
72
|
+
raise ex, "The specified queue #{name_or_url} does not exist."
|
|
66
73
|
end
|
|
67
74
|
|
|
68
75
|
def queue_attributes
|
data/lib/shoryuken/runner.rb
CHANGED
|
@@ -118,10 +118,12 @@ module Shoryuken
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def handle_signal(sig)
|
|
121
|
+
logger.debug "Got #{sig} signal"
|
|
122
|
+
|
|
121
123
|
case sig
|
|
122
124
|
when 'USR1' then execute_soft_shutdown
|
|
123
125
|
when 'TTIN' then print_threads_backtrace
|
|
124
|
-
|
|
126
|
+
when 'TERM', 'INT'
|
|
125
127
|
logger.info { "Received #{sig}, will shutdown down" }
|
|
126
128
|
|
|
127
129
|
raise Interrupt
|
data/lib/shoryuken/util.rb
CHANGED
|
@@ -4,13 +4,13 @@ module Shoryuken
|
|
|
4
4
|
Shoryuken.logger
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
def fire_event(event, reverse = false)
|
|
7
|
+
def fire_event(event, reverse = false, event_options = {})
|
|
8
8
|
logger.debug { "Firing '#{event}' lifecycle event" }
|
|
9
9
|
arr = Shoryuken.options[:lifecycle_events][event]
|
|
10
10
|
arr.reverse! if reverse
|
|
11
11
|
arr.each do |block|
|
|
12
12
|
begin
|
|
13
|
-
block.call
|
|
13
|
+
block.call(event_options)
|
|
14
14
|
rescue => ex
|
|
15
15
|
logger.warn(event: event)
|
|
16
16
|
logger.warn "#{ex.class.name}: #{ex.message}"
|
|
@@ -30,7 +30,7 @@ module Shoryuken
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def worker_name(worker_class, sqs_msg, body = nil)
|
|
33
|
-
if
|
|
33
|
+
if Shoryuken.active_job? \
|
|
34
34
|
&& !sqs_msg.is_a?(Array) \
|
|
35
35
|
&& sqs_msg.message_attributes \
|
|
36
36
|
&& sqs_msg.message_attributes['shoryuken_class'] \
|
data/lib/shoryuken/version.rb
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Shoryuken
|
|
2
|
+
module Worker
|
|
3
|
+
class DefaultExecutor
|
|
4
|
+
class << self
|
|
5
|
+
def perform_async(worker_class, body, options = {})
|
|
6
|
+
options[:message_attributes] ||= {}
|
|
7
|
+
options[:message_attributes]['shoryuken_class'] = {
|
|
8
|
+
string_value: worker_class.to_s,
|
|
9
|
+
data_type: 'String'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
options[:message_body] = body
|
|
13
|
+
|
|
14
|
+
queue = options.delete(:queue) || worker_class.get_shoryuken_options['queue']
|
|
15
|
+
|
|
16
|
+
Shoryuken::Client.queues(queue).send_message(options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def perform_in(worker_class, interval, body, options = {})
|
|
20
|
+
interval = interval.to_f
|
|
21
|
+
now = Time.now.to_f
|
|
22
|
+
ts = (interval < 1_000_000_000 ? (now + interval).to_f : interval)
|
|
23
|
+
|
|
24
|
+
delay = (ts - now).ceil
|
|
25
|
+
|
|
26
|
+
raise 'The maximum allowed delay is 15 minutes' if delay > 15 * 60
|
|
27
|
+
|
|
28
|
+
worker_class.perform_async(body, options.merge(delay_seconds: delay))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Shoryuken
|
|
2
|
+
module Worker
|
|
3
|
+
class InlineExecutor
|
|
4
|
+
class << self
|
|
5
|
+
def perform_async(worker_class, body, options = {})
|
|
6
|
+
body = JSON.dump(body) if body.is_a?(Hash)
|
|
7
|
+
|
|
8
|
+
sqs_msg = OpenStruct.new(
|
|
9
|
+
body: body,
|
|
10
|
+
attributes: nil,
|
|
11
|
+
md5_of_body: nil,
|
|
12
|
+
md5_of_message_attributes: nil,
|
|
13
|
+
message_attributes: nil,
|
|
14
|
+
message_id: nil,
|
|
15
|
+
receipt_handle: nil,
|
|
16
|
+
delete: nil
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
worker_class.new.perform(sqs_msg, BodyParser.parse(worker_class, sqs_msg))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def perform_in(worker_class, _interval, body, options = {})
|
|
23
|
+
perform_async(worker_class, body, options)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/shoryuken/worker.rb
CHANGED
|
@@ -2,33 +2,16 @@ module Shoryuken
|
|
|
2
2
|
module Worker
|
|
3
3
|
def self.included(base)
|
|
4
4
|
base.extend(ClassMethods)
|
|
5
|
+
base.shoryuken_class_attribute :shoryuken_options_hash
|
|
5
6
|
end
|
|
6
7
|
|
|
7
8
|
module ClassMethods
|
|
8
9
|
def perform_async(body, options = {})
|
|
9
|
-
|
|
10
|
-
options[:message_attributes]['shoryuken_class'] = {
|
|
11
|
-
string_value: self.to_s,
|
|
12
|
-
data_type: 'String'
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
options[:message_body] = body
|
|
16
|
-
|
|
17
|
-
queue = options.delete(:queue) || get_shoryuken_options['queue']
|
|
18
|
-
|
|
19
|
-
Shoryuken::Client.queues(queue).send_message(options)
|
|
10
|
+
Shoryuken.worker_executor.perform_async(self, body, options)
|
|
20
11
|
end
|
|
21
12
|
|
|
22
13
|
def perform_in(interval, body, options = {})
|
|
23
|
-
interval
|
|
24
|
-
now = Time.now.to_f
|
|
25
|
-
ts = (interval < 1_000_000_000 ? (now + interval).to_f : interval)
|
|
26
|
-
|
|
27
|
-
delay = (ts - now).ceil
|
|
28
|
-
|
|
29
|
-
raise 'The maximum allowed delay is 15 minutes' if delay > 15 * 60
|
|
30
|
-
|
|
31
|
-
perform_async(body, options.merge(delay_seconds: delay))
|
|
14
|
+
Shoryuken.worker_executor.perform_in(self, interval, body, options)
|
|
32
15
|
end
|
|
33
16
|
|
|
34
17
|
alias_method :perform_at, :perform_in
|
|
@@ -40,7 +23,7 @@ module Shoryuken
|
|
|
40
23
|
end
|
|
41
24
|
|
|
42
25
|
def shoryuken_options(opts = {})
|
|
43
|
-
|
|
26
|
+
self.shoryuken_options_hash = get_shoryuken_options.merge(stringify_keys(opts || {}))
|
|
44
27
|
normalize_worker_queue!
|
|
45
28
|
end
|
|
46
29
|
|
|
@@ -57,33 +40,87 @@ module Shoryuken
|
|
|
57
40
|
end
|
|
58
41
|
|
|
59
42
|
def get_shoryuken_options # :nodoc:
|
|
60
|
-
|
|
43
|
+
shoryuken_options_hash || Shoryuken.default_worker_options
|
|
61
44
|
end
|
|
62
45
|
|
|
63
46
|
def stringify_keys(hash) # :nodoc:
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
new_hash = {}
|
|
48
|
+
hash.each { |key, value| new_hash[key.to_s] = value }
|
|
49
|
+
new_hash
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def shoryuken_class_attribute(*attrs) # :nodoc:
|
|
53
|
+
attrs.each do |name|
|
|
54
|
+
singleton_class.instance_eval do
|
|
55
|
+
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
|
56
|
+
end
|
|
57
|
+
define_singleton_method(name) { nil }
|
|
58
|
+
|
|
59
|
+
ivar = "@#{name}"
|
|
60
|
+
|
|
61
|
+
singleton_class.instance_eval do
|
|
62
|
+
m = "#{name}="
|
|
63
|
+
undef_method(m) if method_defined?(m) || private_method_defined?(m)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
define_singleton_method("#{name}=") do |val|
|
|
67
|
+
singleton_class.class_eval do
|
|
68
|
+
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
|
69
|
+
define_method(name) { val }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# singleton? backwards compatibility for ruby < 2.1
|
|
73
|
+
singleton_klass = respond_to?(:singleton?) ? singleton? : self != ancestors.first
|
|
74
|
+
|
|
75
|
+
if singleton_klass
|
|
76
|
+
class_eval do
|
|
77
|
+
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
|
78
|
+
define_method(name) do
|
|
79
|
+
if instance_variable_defined? ivar
|
|
80
|
+
instance_variable_get ivar
|
|
81
|
+
else
|
|
82
|
+
singleton_class.send name
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
val
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# instance reader
|
|
91
|
+
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
|
92
|
+
define_method(name) do
|
|
93
|
+
if instance_variable_defined?(ivar)
|
|
94
|
+
instance_variable_get ivar
|
|
95
|
+
else
|
|
96
|
+
self.class.public_send name
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# instance writer
|
|
101
|
+
m = "#{name}="
|
|
102
|
+
undef_method(m) if method_defined?(m) || private_method_defined?(m)
|
|
103
|
+
attr_writer name
|
|
66
104
|
end
|
|
67
|
-
hash
|
|
68
105
|
end
|
|
69
106
|
|
|
70
107
|
private
|
|
71
108
|
|
|
72
109
|
def normalize_worker_queue!
|
|
73
|
-
queue =
|
|
110
|
+
queue = shoryuken_options_hash['queue']
|
|
74
111
|
if queue.respond_to?(:call)
|
|
75
112
|
queue = queue.call
|
|
76
|
-
|
|
113
|
+
shoryuken_options_hash['queue'] = queue
|
|
77
114
|
end
|
|
78
115
|
|
|
79
|
-
case
|
|
116
|
+
case shoryuken_options_hash['queue']
|
|
80
117
|
when Array
|
|
81
|
-
|
|
118
|
+
shoryuken_options_hash['queue'].map!(&:to_s)
|
|
82
119
|
when Symbol
|
|
83
|
-
|
|
120
|
+
shoryuken_options_hash['queue'] = shoryuken_options_hash['queue'].to_s
|
|
84
121
|
end
|
|
85
122
|
|
|
86
|
-
[
|
|
123
|
+
[shoryuken_options_hash['queue']].flatten.compact.each(&method(:register_worker))
|
|
87
124
|
end
|
|
88
125
|
|
|
89
126
|
def register_worker(queue)
|
data/lib/shoryuken.rb
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
require 'yaml'
|
|
2
2
|
require 'json'
|
|
3
3
|
require 'aws-sdk-core'
|
|
4
|
+
begin
|
|
5
|
+
require 'aws-sdk-sqs' unless defined?(Aws::SQS)
|
|
6
|
+
rescue LoadError
|
|
7
|
+
fail "AWS SDK 3 requires aws-sdk-sqs to be installed separately. Please add gem 'aws-sdk-sqs' to your Gemfile"
|
|
8
|
+
end
|
|
4
9
|
require 'time'
|
|
5
10
|
require 'concurrent'
|
|
11
|
+
require 'forwardable'
|
|
6
12
|
|
|
7
13
|
require 'shoryuken/version'
|
|
8
14
|
require 'shoryuken/core_ext'
|
|
@@ -13,6 +19,8 @@ require 'shoryuken/queue'
|
|
|
13
19
|
require 'shoryuken/message'
|
|
14
20
|
require 'shoryuken/client'
|
|
15
21
|
require 'shoryuken/worker'
|
|
22
|
+
require 'shoryuken/worker/default_executor'
|
|
23
|
+
require 'shoryuken/worker/inline_executor'
|
|
16
24
|
require 'shoryuken/worker_registry'
|
|
17
25
|
require 'shoryuken/default_worker_registry'
|
|
18
26
|
require 'shoryuken/middleware/chain'
|
|
@@ -26,20 +34,25 @@ require 'shoryuken/polling/strict_priority'
|
|
|
26
34
|
require 'shoryuken/manager'
|
|
27
35
|
require 'shoryuken/launcher'
|
|
28
36
|
require 'shoryuken/processor'
|
|
37
|
+
require 'shoryuken/body_parser'
|
|
29
38
|
require 'shoryuken/fetcher'
|
|
30
39
|
require 'shoryuken/options'
|
|
31
40
|
|
|
41
|
+
|
|
32
42
|
module Shoryuken
|
|
33
43
|
extend SingleForwardable
|
|
34
44
|
|
|
35
45
|
def_delegators(
|
|
36
46
|
:'Shoryuken::Options',
|
|
47
|
+
:active_job?,
|
|
37
48
|
:add_group,
|
|
38
49
|
:groups,
|
|
39
50
|
:add_queue,
|
|
40
51
|
:ungrouped_queues,
|
|
41
52
|
:worker_registry,
|
|
42
53
|
:worker_registry=,
|
|
54
|
+
:worker_executor,
|
|
55
|
+
:worker_executor=,
|
|
43
56
|
:polling_strategy,
|
|
44
57
|
:start_callback,
|
|
45
58
|
:start_callback=,
|
|
@@ -67,4 +80,4 @@ module Shoryuken
|
|
|
67
80
|
)
|
|
68
81
|
end
|
|
69
82
|
|
|
70
|
-
require 'shoryuken/extensions/active_job_adapter' if
|
|
83
|
+
require 'shoryuken/extensions/active_job_adapter' if Shoryuken.active_job?
|
data/shoryuken.gemspec
CHANGED
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.add_development_dependency 'pry-byebug'
|
|
24
24
|
spec.add_development_dependency 'dotenv'
|
|
25
25
|
|
|
26
|
-
spec.add_dependency 'aws-sdk-core', '
|
|
26
|
+
spec.add_dependency 'aws-sdk-core', '>= 2'
|
|
27
27
|
spec.add_dependency 'concurrent-ruby'
|
|
28
28
|
spec.add_dependency 'thor'
|
|
29
29
|
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'shoryuken/body_parser'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Shoryuken::BodyParser do
|
|
5
|
+
let(:sqs_msg) { double }
|
|
6
|
+
|
|
7
|
+
describe '#parser' do
|
|
8
|
+
it 'parses the body into JSON' do
|
|
9
|
+
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
10
|
+
|
|
11
|
+
body = { 'test' => 'hi' }
|
|
12
|
+
|
|
13
|
+
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
14
|
+
|
|
15
|
+
expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'parses the body calling the proc' do
|
|
19
|
+
TestWorker.get_shoryuken_options['body_parser'] = proc { |sqs_msg| "*#{sqs_msg.body}*" }
|
|
20
|
+
|
|
21
|
+
allow(sqs_msg).to receive(:body).and_return('test')
|
|
22
|
+
|
|
23
|
+
expect(described_class.parse(TestWorker, sqs_msg)).to eq('*test*')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'parses the body as text' do
|
|
27
|
+
TestWorker.get_shoryuken_options['body_parser'] = :text
|
|
28
|
+
|
|
29
|
+
body = 'test'
|
|
30
|
+
|
|
31
|
+
allow(sqs_msg).to receive(:body).and_return(body)
|
|
32
|
+
|
|
33
|
+
expect(described_class.parse(TestWorker, sqs_msg)).to eq('test')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'parses calling `.load`' do
|
|
37
|
+
TestWorker.get_shoryuken_options['body_parser'] = Class.new do
|
|
38
|
+
def self.load(*args)
|
|
39
|
+
JSON.load(*args)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
body = { 'test' => 'hi' }
|
|
44
|
+
|
|
45
|
+
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
46
|
+
|
|
47
|
+
expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'parses calling `.parse`' do
|
|
51
|
+
TestWorker.get_shoryuken_options['body_parser'] = Class.new do
|
|
52
|
+
def self.parse(*args)
|
|
53
|
+
JSON.parse(*args)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
body = { 'test' => 'hi' }
|
|
58
|
+
|
|
59
|
+
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
60
|
+
|
|
61
|
+
expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'when parse errors' do
|
|
65
|
+
before do
|
|
66
|
+
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
67
|
+
|
|
68
|
+
allow(sqs_msg).to receive(:body).and_return('invalid JSON')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
specify do
|
|
72
|
+
expect { described_class.parse(TestWorker, sqs_msg) }.
|
|
73
|
+
to raise_error(JSON::ParserError, /unexpected token at 'invalid JSON'/)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context 'when `object_type: nil`' do
|
|
78
|
+
it 'parses the body as text' do
|
|
79
|
+
TestWorker.get_shoryuken_options['body_parser'] = nil
|
|
80
|
+
|
|
81
|
+
body = 'test'
|
|
82
|
+
|
|
83
|
+
allow(sqs_msg).to receive(:body).and_return(body)
|
|
84
|
+
|
|
85
|
+
expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
+
require 'active_job'
|
|
2
3
|
|
|
3
|
-
# rubocop:disable Metrics/BlockLength
|
|
4
4
|
RSpec.describe Shoryuken::EnvironmentLoader do
|
|
5
5
|
subject { described_class.new({}) }
|
|
6
6
|
|
|
7
7
|
describe '#parse_queues' do
|
|
8
8
|
before do
|
|
9
|
-
|
|
10
|
-
allow(subject).to receive(:load_rails).with(anything)
|
|
9
|
+
allow(subject).to receive(:load_rails)
|
|
11
10
|
allow(subject).to receive(:prefix_active_job_queue_names)
|
|
12
11
|
allow(subject).to receive(:require_workers)
|
|
13
12
|
allow(subject).to receive(:validate_queues)
|
|
@@ -22,4 +21,34 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
|
22
21
|
expect(Shoryuken.groups['default'][:queues]).to eq(%w(queue1 queue2 queue2))
|
|
23
22
|
end
|
|
24
23
|
end
|
|
24
|
+
|
|
25
|
+
describe '#prefix_active_job_queue_names' do
|
|
26
|
+
before do
|
|
27
|
+
allow(subject).to receive(:load_rails)
|
|
28
|
+
allow(subject).to receive(:require_workers)
|
|
29
|
+
allow(subject).to receive(:validate_queues)
|
|
30
|
+
allow(subject).to receive(:validate_workers)
|
|
31
|
+
allow(subject).to receive(:patch_deprecated_workers)
|
|
32
|
+
|
|
33
|
+
ActiveJob::Base.queue_name_prefix = 'test'
|
|
34
|
+
ActiveJob::Base.queue_name_delimiter = '_'
|
|
35
|
+
|
|
36
|
+
allow(Shoryuken).to receive(:active_job?).and_return(true)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
specify do
|
|
40
|
+
Shoryuken.active_job_queue_name_prefixing = true
|
|
41
|
+
|
|
42
|
+
Shoryuken.options[:queues] = ['queue1', ['queue2', 2]]
|
|
43
|
+
|
|
44
|
+
Shoryuken.options[:groups] = {
|
|
45
|
+
'group1' => { queues: %w(group1_queue1 group1_queue2) }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
subject.load
|
|
49
|
+
|
|
50
|
+
expect(Shoryuken.groups['default'][:queues]).to eq(%w(test_queue1 test_queue2 test_queue2))
|
|
51
|
+
expect(Shoryuken.groups['group1'][:queues]).to eq(%w(test_group1_queue1 test_group1_queue2))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
25
54
|
end
|
|
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
|
2
2
|
require 'shoryuken/manager'
|
|
3
3
|
require 'shoryuken/fetcher'
|
|
4
4
|
|
|
5
|
+
# rubocop:disable Metrics/BlockLength
|
|
5
6
|
RSpec.describe Shoryuken::Fetcher do
|
|
6
7
|
let(:queue) { instance_double('Shoryuken::Queue') }
|
|
7
8
|
let(:queue_name) { 'default' }
|
|
@@ -13,7 +14,7 @@ RSpec.describe Shoryuken::Fetcher do
|
|
|
13
14
|
Shoryuken::Message,
|
|
14
15
|
queue_url: queue_name,
|
|
15
16
|
body: 'test',
|
|
16
|
-
message_id: 'fc754df79cc24c4196ca5996a44b771e'
|
|
17
|
+
message_id: 'fc754df79cc24c4196ca5996a44b771e'
|
|
17
18
|
)
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -27,23 +28,74 @@ RSpec.describe Shoryuken::Fetcher do
|
|
|
27
28
|
|
|
28
29
|
Shoryuken.sqs_client_receive_message_opts[group] = { wait_time_seconds: 10 }
|
|
29
30
|
|
|
30
|
-
expect(queue).to receive(:receive_messages).
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
expect(queue).to receive(:receive_messages).with(
|
|
32
|
+
wait_time_seconds: 10,
|
|
33
|
+
max_number_of_messages: limit,
|
|
34
|
+
message_attribute_names: ['All'],
|
|
35
|
+
attribute_names: ['All']
|
|
36
|
+
).and_return([])
|
|
33
37
|
|
|
34
38
|
subject.fetch(queue_config, limit)
|
|
35
39
|
end
|
|
36
40
|
|
|
41
|
+
it 'logs debug only' do
|
|
42
|
+
# See https://github.com/phstc/shoryuken/issues/435
|
|
43
|
+
logger = double 'logger'
|
|
44
|
+
|
|
45
|
+
allow(subject).to receive(:logger).and_return(logger)
|
|
46
|
+
|
|
47
|
+
expect(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
|
|
48
|
+
|
|
49
|
+
expect(queue).to receive(:receive_messages).and_return([double('SQS Msg')])
|
|
50
|
+
|
|
51
|
+
expect(logger).to receive(:debug).exactly(3).times
|
|
52
|
+
expect(logger).to_not receive(:info)
|
|
53
|
+
|
|
54
|
+
subject.fetch(queue_config, limit)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'when receive options per queue' do
|
|
58
|
+
let(:limit) { 5 }
|
|
59
|
+
|
|
60
|
+
specify do
|
|
61
|
+
expect(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
|
|
62
|
+
|
|
63
|
+
Shoryuken.sqs_client_receive_message_opts[queue_name] = { max_number_of_messages: 1 }
|
|
64
|
+
|
|
65
|
+
expect(queue).to receive(:receive_messages).with(
|
|
66
|
+
max_number_of_messages: 1,
|
|
67
|
+
message_attribute_names: ['All'],
|
|
68
|
+
attribute_names: ['All']
|
|
69
|
+
).and_return([])
|
|
70
|
+
|
|
71
|
+
subject.fetch(queue_config, limit)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context 'when max_number_of_messages opt is great than limit' do
|
|
76
|
+
it 'uses limit' do
|
|
77
|
+
expect(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
|
|
78
|
+
|
|
79
|
+
Shoryuken.sqs_client_receive_message_opts[queue_name] = { max_number_of_messages: 20 }
|
|
80
|
+
|
|
81
|
+
expect(queue).to receive(:receive_messages).with(
|
|
82
|
+
max_number_of_messages: limit,
|
|
83
|
+
message_attribute_names: ['All'],
|
|
84
|
+
attribute_names: ['All']
|
|
85
|
+
).and_return([])
|
|
86
|
+
|
|
87
|
+
subject.fetch(queue_config, limit)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
37
91
|
context 'when limit is greater than FETCH_LIMIT' do
|
|
38
92
|
let(:limit) { 20 }
|
|
39
93
|
|
|
40
94
|
specify do
|
|
41
|
-
Shoryuken.sqs_client_receive_message_opts[group] = {}
|
|
42
|
-
|
|
43
95
|
allow(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
|
|
44
|
-
expect(queue).to receive(:receive_messages).
|
|
45
|
-
|
|
46
|
-
|
|
96
|
+
expect(queue).to receive(:receive_messages).with(
|
|
97
|
+
max_number_of_messages: described_class::FETCH_LIMIT, attribute_names: ['All'], message_attribute_names: ['All']
|
|
98
|
+
).and_return([])
|
|
47
99
|
|
|
48
100
|
subject.fetch(queue_config, limit)
|
|
49
101
|
end
|