shoryuken 2.0.11 → 3.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/.codeclimate.yml +20 -0
- data/.rubocop.yml +8 -2
- data/.travis.yml +7 -5
- data/CHANGELOG.md +92 -10
- data/Gemfile +1 -0
- data/README.md +20 -57
- data/Rakefile +0 -1
- data/bin/cli/base.rb +42 -0
- data/bin/cli/sqs.rb +188 -0
- data/bin/shoryuken +47 -9
- data/examples/default_worker.rb +1 -12
- data/lib/shoryuken/client.rb +3 -25
- data/lib/shoryuken/default_worker_registry.rb +9 -5
- data/lib/shoryuken/environment_loader.rb +29 -67
- data/lib/shoryuken/fetcher.rb +22 -53
- data/lib/shoryuken/launcher.rb +5 -29
- data/lib/shoryuken/manager.rb +72 -184
- data/lib/shoryuken/message.rb +4 -13
- data/lib/shoryuken/middleware/chain.rb +1 -18
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +21 -18
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +26 -19
- data/lib/shoryuken/polling.rb +204 -0
- data/lib/shoryuken/processor.rb +6 -14
- data/lib/shoryuken/queue.rb +36 -38
- data/lib/shoryuken/runner.rb +143 -0
- data/lib/shoryuken/util.rb +3 -9
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker.rb +1 -1
- data/lib/shoryuken.rb +78 -39
- data/shoryuken.gemspec +6 -6
- data/spec/integration/launcher_spec.rb +4 -3
- data/spec/shoryuken/client_spec.rb +2 -43
- data/spec/shoryuken/default_worker_registry_spec.rb +12 -10
- data/spec/shoryuken/environment_loader_spec.rb +34 -0
- data/spec/shoryuken/fetcher_spec.rb +18 -52
- data/spec/shoryuken/manager_spec.rb +56 -97
- data/spec/shoryuken/middleware/chain_spec.rb +0 -24
- data/spec/shoryuken/middleware/server/auto_delete_spec.rb +2 -2
- data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +7 -3
- data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +56 -33
- data/spec/shoryuken/polling_spec.rb +239 -0
- data/spec/shoryuken/processor_spec.rb +5 -5
- data/spec/shoryuken/queue_spec.rb +110 -63
- data/spec/shoryuken/{cli_spec.rb → runner_spec.rb} +10 -24
- data/spec/shoryuken_spec.rb +13 -1
- data/spec/spec_helper.rb +8 -20
- data/test_workers/endless_interruptive_worker.rb +41 -0
- data/test_workers/endless_uninterruptive_worker.rb +44 -0
- metadata +34 -35
- data/.hound.yml +0 -6
- data/lib/shoryuken/cli.rb +0 -210
- data/lib/shoryuken/sns_arn.rb +0 -27
- data/lib/shoryuken/topic.rb +0 -17
- data/spec/shoryuken/sns_arn_spec.rb +0 -42
- data/spec/shoryuken/topic_spec.rb +0 -32
- data/spec/shoryuken_endpoint.yml +0 -6
- /data/{LICENSE.txt → LICENSE} +0 -0
data/lib/shoryuken/client.rb
CHANGED
|
@@ -1,40 +1,18 @@
|
|
|
1
1
|
module Shoryuken
|
|
2
2
|
class Client
|
|
3
3
|
@@queues = {}
|
|
4
|
-
@@topics = {}
|
|
5
4
|
|
|
6
5
|
class << self
|
|
7
6
|
def queues(name)
|
|
8
7
|
@@queues[name.to_s] ||= Shoryuken::Queue.new(sqs, name)
|
|
9
8
|
end
|
|
10
9
|
|
|
11
|
-
def sns
|
|
12
|
-
@sns ||= Aws::SNS::Client.new(aws_client_options(:sns_endpoint))
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def sns_arn
|
|
16
|
-
@sns_arn ||= SnsArn
|
|
17
|
-
end
|
|
18
|
-
|
|
19
10
|
def sqs
|
|
20
|
-
|
|
11
|
+
@@sqs ||= Shoryuken.sqs_client
|
|
21
12
|
end
|
|
22
13
|
|
|
23
|
-
def
|
|
24
|
-
@@
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
attr_accessor :account_id
|
|
28
|
-
attr_writer :sns, :sqs, :sqs_resource, :sns_arn
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def aws_client_options(service_endpoint_key)
|
|
33
|
-
environment_endpoint = ENV["AWS_#{service_endpoint_key.to_s.upcase}"]
|
|
34
|
-
explicit_endpoint = Shoryuken.options[:aws][service_endpoint_key] || environment_endpoint
|
|
35
|
-
options = {}
|
|
36
|
-
options[:endpoint] = explicit_endpoint unless explicit_endpoint.to_s.empty?
|
|
37
|
-
options
|
|
14
|
+
def sqs=(sqs)
|
|
15
|
+
@@sqs = sqs
|
|
38
16
|
end
|
|
39
17
|
end
|
|
40
18
|
end
|
|
@@ -14,11 +14,15 @@ module Shoryuken
|
|
|
14
14
|
|
|
15
15
|
def fetch_worker(queue, message)
|
|
16
16
|
worker_class = !message.is_a?(Array) &&
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
worker_class =
|
|
17
|
+
message.message_attributes &&
|
|
18
|
+
message.message_attributes['shoryuken_class'] &&
|
|
19
|
+
message.message_attributes['shoryuken_class'][:string_value]
|
|
20
|
+
|
|
21
|
+
worker_class = begin
|
|
22
|
+
worker_class.constantize
|
|
23
|
+
rescue
|
|
24
|
+
@workers[queue]
|
|
25
|
+
end
|
|
22
26
|
|
|
23
27
|
worker_class.new
|
|
24
28
|
end
|
|
@@ -2,30 +2,34 @@ module Shoryuken
|
|
|
2
2
|
class EnvironmentLoader
|
|
3
3
|
attr_reader :options
|
|
4
4
|
|
|
5
|
-
def self.
|
|
6
|
-
new(options)
|
|
5
|
+
def self.setup_options(options)
|
|
6
|
+
instance = new(options)
|
|
7
|
+
instance.setup_options
|
|
8
|
+
instance
|
|
7
9
|
end
|
|
8
10
|
|
|
9
11
|
def self.load_for_rails_console
|
|
10
|
-
|
|
12
|
+
instance = setup_options(config_file: (Rails.root + 'config' + 'shoryuken.yml'))
|
|
13
|
+
instance.load
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def initialize(options)
|
|
14
17
|
@options = options
|
|
15
18
|
end
|
|
16
19
|
|
|
17
|
-
def
|
|
18
|
-
load_rails if options[:rails]
|
|
20
|
+
def setup_options
|
|
19
21
|
initialize_options
|
|
20
22
|
initialize_logger
|
|
21
23
|
merge_cli_defined_queues
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load
|
|
27
|
+
load_rails if options[:rails]
|
|
22
28
|
prefix_active_job_queue_names
|
|
23
29
|
parse_queues
|
|
24
30
|
require_workers
|
|
25
|
-
initialize_aws
|
|
26
31
|
validate_queues
|
|
27
32
|
validate_workers
|
|
28
|
-
patch_deprecated_workers
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
private
|
|
@@ -43,40 +47,9 @@ module Shoryuken
|
|
|
43
47
|
YAML.load(ERB.new(IO.read(path)).result).deep_symbolize_keys
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
def initialize_aws
|
|
47
|
-
# aws-sdk tries to load the credentials from the ENV variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
|
|
48
|
-
# when not explicit supplied
|
|
49
|
-
return if Shoryuken.options[:aws].empty?
|
|
50
|
-
|
|
51
|
-
shoryuken_keys = %w(
|
|
52
|
-
account_id
|
|
53
|
-
sns_endpoint
|
|
54
|
-
sqs_endpoint
|
|
55
|
-
receive_message).map(&:to_sym)
|
|
56
|
-
|
|
57
|
-
aws_options = Shoryuken.options[:aws].reject do |k, v|
|
|
58
|
-
shoryuken_keys.include?(k)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# assume credentials based authentication
|
|
62
|
-
credentials = Aws::Credentials.new(
|
|
63
|
-
aws_options.delete(:access_key_id),
|
|
64
|
-
aws_options.delete(:secret_access_key))
|
|
65
|
-
|
|
66
|
-
# but only if the configuration options have valid values
|
|
67
|
-
aws_options = aws_options.merge(credentials: credentials) if credentials.set?
|
|
68
|
-
|
|
69
|
-
if (callback = Shoryuken.aws_initialization_callback)
|
|
70
|
-
Shoryuken.logger.info { 'Calling Shoryuken.on_aws_initialization block' }
|
|
71
|
-
callback.call(aws_options)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
Aws.config = aws_options
|
|
75
|
-
end
|
|
76
|
-
|
|
77
50
|
def initialize_logger
|
|
78
|
-
Shoryuken::Logging.initialize_logger(options[:logfile]) if options[:logfile]
|
|
79
|
-
Shoryuken.logger.level = Logger::DEBUG if options[:verbose]
|
|
51
|
+
Shoryuken::Logging.initialize_logger(Shoryuken.options[:logfile]) if Shoryuken.options[:logfile]
|
|
52
|
+
Shoryuken.logger.level = Logger::DEBUG if Shoryuken.options[:verbose]
|
|
80
53
|
end
|
|
81
54
|
|
|
82
55
|
def load_rails
|
|
@@ -98,12 +71,11 @@ module Shoryuken
|
|
|
98
71
|
end
|
|
99
72
|
|
|
100
73
|
def merge_cli_defined_queues
|
|
101
|
-
cli_defined_queues = options
|
|
74
|
+
cli_defined_queues = options[:queues].to_a
|
|
102
75
|
|
|
103
76
|
cli_defined_queues.each do |cli_defined_queue|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
77
|
+
# CLI defined queues override config_file defined queues
|
|
78
|
+
Shoryuken.options[:queues].delete_if { |config_file_queue| config_file_queue[0] == cli_defined_queue[0] }
|
|
107
79
|
|
|
108
80
|
Shoryuken.options[:queues] << cli_defined_queue
|
|
109
81
|
end
|
|
@@ -125,7 +97,7 @@ module Shoryuken
|
|
|
125
97
|
end
|
|
126
98
|
|
|
127
99
|
def parse_queue(queue, weight = nil)
|
|
128
|
-
[weight.to_i, 1].max
|
|
100
|
+
Shoryuken.add_queue(queue, [weight.to_i, 1].max)
|
|
129
101
|
end
|
|
130
102
|
|
|
131
103
|
def parse_queues
|
|
@@ -134,28 +106,18 @@ module Shoryuken
|
|
|
134
106
|
end
|
|
135
107
|
end
|
|
136
108
|
|
|
137
|
-
def
|
|
138
|
-
Shoryuken.
|
|
139
|
-
Shoryuken.worker_registry.workers(queue).each do |worker_class|
|
|
140
|
-
if worker_class.instance_method(:perform).arity == 1
|
|
141
|
-
Shoryuken.logger.warn { "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)" }
|
|
109
|
+
def require_workers
|
|
110
|
+
required = Shoryuken.options[:require]
|
|
142
111
|
|
|
143
|
-
|
|
144
|
-
alias_method :deprecated_perform, :perform
|
|
112
|
+
return unless required
|
|
145
113
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
end
|
|
151
|
-
end
|
|
114
|
+
if File.directory?(required)
|
|
115
|
+
Dir[File.join(required, '**', '*.rb')].each(&method(:require))
|
|
116
|
+
else
|
|
117
|
+
require required
|
|
152
118
|
end
|
|
153
119
|
end
|
|
154
120
|
|
|
155
|
-
def require_workers
|
|
156
|
-
require Shoryuken.options[:require] if Shoryuken.options[:require]
|
|
157
|
-
end
|
|
158
|
-
|
|
159
121
|
def validate_queues
|
|
160
122
|
Shoryuken.logger.warn { 'No queues supplied' } if Shoryuken.queues.empty?
|
|
161
123
|
|
|
@@ -163,7 +125,7 @@ module Shoryuken
|
|
|
163
125
|
|
|
164
126
|
Shoryuken.queues.uniq.each do |queue|
|
|
165
127
|
begin
|
|
166
|
-
Shoryuken::Client.queues
|
|
128
|
+
Shoryuken::Client.queues(queue)
|
|
167
129
|
rescue Aws::SQS::Errors::NonExistentQueue
|
|
168
130
|
non_existent_queues << queue
|
|
169
131
|
end
|
|
@@ -173,13 +135,13 @@ module Shoryuken
|
|
|
173
135
|
end
|
|
174
136
|
|
|
175
137
|
def validate_workers
|
|
138
|
+
return if defined?(::ActiveJob)
|
|
139
|
+
|
|
176
140
|
all_queues = Shoryuken.queues
|
|
177
141
|
queues_with_workers = Shoryuken.worker_registry.queues
|
|
178
142
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Shoryuken.logger.warn { "No worker supplied for '#{queue}'" }
|
|
182
|
-
end
|
|
143
|
+
(all_queues - queues_with_workers).each do |queue|
|
|
144
|
+
Shoryuken.logger.warn { "No worker supplied for '#{queue}'" }
|
|
183
145
|
end
|
|
184
146
|
end
|
|
185
147
|
end
|
data/lib/shoryuken/fetcher.rb
CHANGED
|
@@ -1,73 +1,42 @@
|
|
|
1
1
|
module Shoryuken
|
|
2
2
|
class Fetcher
|
|
3
|
-
include Celluloid
|
|
4
3
|
include Util
|
|
5
4
|
|
|
6
5
|
FETCH_LIMIT = 10
|
|
7
6
|
|
|
8
|
-
def
|
|
9
|
-
|
|
7
|
+
def fetch(queue, available_processors)
|
|
8
|
+
started_at = Time.now
|
|
9
|
+
|
|
10
|
+
logger.debug { "Looking for new messages in '#{queue}'" }
|
|
11
|
+
|
|
12
|
+
begin
|
|
13
|
+
limit = available_processors > FETCH_LIMIT ? FETCH_LIMIT : available_processors
|
|
14
|
+
|
|
15
|
+
sqs_msgs = Array(receive_messages(queue, limit))
|
|
16
|
+
logger.info { "Found #{sqs_msgs.size} messages for '#{queue.name}'" } unless sqs_msgs.empty?
|
|
17
|
+
logger.debug { "Fetcher for '#{queue}' completed in #{elapsed(started_at)} ms" }
|
|
18
|
+
sqs_msgs
|
|
19
|
+
rescue => ex
|
|
20
|
+
logger.error { "Error fetching message: #{ex}" }
|
|
21
|
+
logger.error { ex.backtrace.first }
|
|
22
|
+
[]
|
|
23
|
+
end
|
|
10
24
|
end
|
|
11
25
|
|
|
26
|
+
private
|
|
27
|
+
|
|
12
28
|
def receive_messages(queue, limit)
|
|
13
29
|
# AWS limits the batch size by 10
|
|
14
30
|
limit = limit > FETCH_LIMIT ? FETCH_LIMIT : limit
|
|
15
31
|
|
|
16
|
-
options =
|
|
32
|
+
options = Shoryuken.sqs_client_receive_message_opts.to_h.dup
|
|
17
33
|
options[:max_number_of_messages] = limit
|
|
18
34
|
options[:message_attribute_names] = %w(All)
|
|
19
35
|
options[:attribute_names] = %w(All)
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def fetch(queue, available_processors)
|
|
25
|
-
watchdog('Fetcher#fetch died') do
|
|
26
|
-
started_at = Time.now
|
|
27
|
-
|
|
28
|
-
logger.debug { "Looking for new messages in '#{queue}'" }
|
|
29
|
-
|
|
30
|
-
begin
|
|
31
|
-
batch = Shoryuken.worker_registry.batch_receive_messages?(queue)
|
|
32
|
-
limit = batch ? FETCH_LIMIT : available_processors
|
|
33
|
-
|
|
34
|
-
if (sqs_msgs = Array(receive_messages(queue, limit))).any?
|
|
35
|
-
logger.info { "Found #{sqs_msgs.size} messages for '#{queue}'" }
|
|
36
|
-
|
|
37
|
-
if batch
|
|
38
|
-
@manager.async.assign(queue, patch_sqs_msgs!(sqs_msgs))
|
|
39
|
-
else
|
|
40
|
-
sqs_msgs.each { |sqs_msg| @manager.async.assign(queue, sqs_msg) }
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
@manager.async.rebalance_queue_weight!(queue)
|
|
44
|
-
else
|
|
45
|
-
logger.debug { "No message found for '#{queue}'" }
|
|
46
|
-
|
|
47
|
-
@manager.async.pause_queue!(queue)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
logger.debug { "Fetcher for '#{queue}' completed in #{elapsed(started_at)} ms" }
|
|
51
|
-
rescue => ex
|
|
52
|
-
logger.error { "Error fetching message: #{ex}" }
|
|
53
|
-
logger.error { ex.backtrace.first }
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
@manager.async.dispatch
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
private
|
|
62
|
-
|
|
63
|
-
def patch_sqs_msgs!(sqs_msgs)
|
|
64
|
-
sqs_msgs.instance_eval do
|
|
65
|
-
def message_id
|
|
66
|
-
"batch-with-#{size}-messages"
|
|
67
|
-
end
|
|
68
|
-
end
|
|
37
|
+
options.merge!(queue.options)
|
|
69
38
|
|
|
70
|
-
|
|
39
|
+
Shoryuken::Client.queues(queue.name).receive_messages(options)
|
|
71
40
|
end
|
|
72
41
|
end
|
|
73
42
|
end
|
data/lib/shoryuken/launcher.rb
CHANGED
|
@@ -1,43 +1,19 @@
|
|
|
1
1
|
module Shoryuken
|
|
2
2
|
class Launcher
|
|
3
|
-
include Celluloid
|
|
4
3
|
include Util
|
|
5
4
|
|
|
6
|
-
trap_exit :actor_died
|
|
7
|
-
|
|
8
|
-
attr_accessor :manager
|
|
9
|
-
|
|
10
5
|
def initialize
|
|
11
|
-
@
|
|
12
|
-
|
|
13
|
-
@fetcher = Shoryuken::Fetcher.new_link(manager)
|
|
14
|
-
|
|
15
|
-
@done = false
|
|
16
|
-
|
|
17
|
-
manager.fetcher = @fetcher
|
|
6
|
+
@manager = Shoryuken::Manager.new(Shoryuken::Fetcher.new,
|
|
7
|
+
Shoryuken.options[:polling_strategy].new(Shoryuken.queues))
|
|
18
8
|
end
|
|
19
9
|
|
|
20
10
|
def stop(options = {})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@fetcher.terminate if @fetcher.alive?
|
|
24
|
-
|
|
25
|
-
manager.async.stop(shutdown: !!options[:shutdown], timeout: Shoryuken.options[:timeout])
|
|
26
|
-
@condvar.wait
|
|
27
|
-
manager.terminate
|
|
28
|
-
end
|
|
11
|
+
@manager.stop(shutdown: !options[:shutdown].nil?,
|
|
12
|
+
timeout: Shoryuken.options[:timeout])
|
|
29
13
|
end
|
|
30
14
|
|
|
31
15
|
def run
|
|
32
|
-
|
|
33
|
-
manager.async.start
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def actor_died(actor, reason)
|
|
38
|
-
return if @done
|
|
39
|
-
logger.warn { 'Shoryuken died due to the following error, cannot recover, process exiting' }
|
|
40
|
-
exit 1
|
|
16
|
+
@manager.start
|
|
41
17
|
end
|
|
42
18
|
end
|
|
43
19
|
end
|