shoryuken 2.1.0 → 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 +5 -8
- data/.rubocop.yml +8 -2
- data/.travis.yml +5 -0
- data/CHANGELOG.md +88 -11
- 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 -1
- data/lib/shoryuken/client.rb +3 -15
- data/lib/shoryuken/default_worker_registry.rb +9 -5
- data/lib/shoryuken/environment_loader.rb +11 -40
- data/lib/shoryuken/fetcher.rb +22 -53
- data/lib/shoryuken/launcher.rb +5 -29
- data/lib/shoryuken/manager.rb +72 -190
- data/lib/shoryuken/message.rb +4 -13
- data/lib/shoryuken/middleware/chain.rb +1 -18
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +13 -14
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +29 -20
- data/lib/shoryuken/polling.rb +204 -0
- data/lib/shoryuken/processor.rb +6 -14
- data/lib/shoryuken/queue.rb +1 -3
- data/lib/shoryuken/runner.rb +143 -0
- data/lib/shoryuken/util.rb +0 -8
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker.rb +1 -1
- data/lib/shoryuken.rb +77 -55
- data/shoryuken.gemspec +6 -6
- data/spec/integration/launcher_spec.rb +4 -3
- data/spec/shoryuken/client_spec.rb +2 -45
- 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/{cli_spec.rb → runner_spec.rb} +8 -22
- data/spec/shoryuken_spec.rb +13 -1
- data/spec/spec_helper.rb +3 -18
- data/test_workers/endless_interruptive_worker.rb +41 -0
- data/test_workers/endless_uninterruptive_worker.rb +44 -0
- metadata +32 -34
- data/lib/shoryuken/aws_config.rb +0 -64
- data/lib/shoryuken/cli.rb +0 -215
- 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/lib/shoryuken/client.rb
CHANGED
|
@@ -1,31 +1,19 @@
|
|
|
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 ||= Shoryuken::AwsConfig.sns
|
|
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
|
-
@@
|
|
14
|
+
def sqs=(sqs)
|
|
15
|
+
@@sqs = sqs
|
|
25
16
|
end
|
|
26
|
-
|
|
27
|
-
attr_accessor :account_id
|
|
28
|
-
attr_writer :sns, :sqs, :sqs_resource, :sns_arn
|
|
29
17
|
end
|
|
30
18
|
end
|
|
31
19
|
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
|
|
@@ -28,10 +28,8 @@ module Shoryuken
|
|
|
28
28
|
prefix_active_job_queue_names
|
|
29
29
|
parse_queues
|
|
30
30
|
require_workers
|
|
31
|
-
initialize_aws
|
|
32
31
|
validate_queues
|
|
33
32
|
validate_workers
|
|
34
|
-
patch_deprecated_workers
|
|
35
33
|
end
|
|
36
34
|
|
|
37
35
|
private
|
|
@@ -49,17 +47,9 @@ module Shoryuken
|
|
|
49
47
|
YAML.load(ERB.new(IO.read(path)).result).deep_symbolize_keys
|
|
50
48
|
end
|
|
51
49
|
|
|
52
|
-
# DEPRECATED: Please use configure_server and configure_client in
|
|
53
|
-
# https://github.com/phstc/shoryuken/blob/a81637d577b36c5cf245882733ea91a335b6602f/lib/shoryuken.rb#L82
|
|
54
|
-
# Please delete this method afert next release (v2.0.12 or later)
|
|
55
|
-
def initialize_aws
|
|
56
|
-
Shoryuken.logger.warn { "[DEPRECATION] aws in shoryuken.yml is deprecated. Please use configure_server and configure_client in your initializer"} unless Shoryuken.options[:aws].nil?
|
|
57
|
-
Shoryuken::AwsConfig.setup(Shoryuken.options[:aws])
|
|
58
|
-
end
|
|
59
|
-
|
|
60
50
|
def initialize_logger
|
|
61
|
-
Shoryuken::Logging.initialize_logger(options[:logfile]) if options[:logfile]
|
|
62
|
-
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]
|
|
63
53
|
end
|
|
64
54
|
|
|
65
55
|
def load_rails
|
|
@@ -81,12 +71,11 @@ module Shoryuken
|
|
|
81
71
|
end
|
|
82
72
|
|
|
83
73
|
def merge_cli_defined_queues
|
|
84
|
-
cli_defined_queues = options
|
|
74
|
+
cli_defined_queues = options[:queues].to_a
|
|
85
75
|
|
|
86
76
|
cli_defined_queues.each do |cli_defined_queue|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
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] }
|
|
90
79
|
|
|
91
80
|
Shoryuken.options[:queues] << cli_defined_queue
|
|
92
81
|
end
|
|
@@ -108,7 +97,7 @@ module Shoryuken
|
|
|
108
97
|
end
|
|
109
98
|
|
|
110
99
|
def parse_queue(queue, weight = nil)
|
|
111
|
-
[weight.to_i, 1].max
|
|
100
|
+
Shoryuken.add_queue(queue, [weight.to_i, 1].max)
|
|
112
101
|
end
|
|
113
102
|
|
|
114
103
|
def parse_queues
|
|
@@ -117,24 +106,6 @@ module Shoryuken
|
|
|
117
106
|
end
|
|
118
107
|
end
|
|
119
108
|
|
|
120
|
-
def patch_deprecated_workers
|
|
121
|
-
Shoryuken.worker_registry.queues.each do |queue|
|
|
122
|
-
Shoryuken.worker_registry.workers(queue).each do |worker_class|
|
|
123
|
-
if worker_class.instance_method(:perform).arity == 1
|
|
124
|
-
Shoryuken.logger.warn { "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)" }
|
|
125
|
-
|
|
126
|
-
worker_class.class_eval do
|
|
127
|
-
alias_method :deprecated_perform, :perform
|
|
128
|
-
|
|
129
|
-
def perform(sqs_msg, body = nil)
|
|
130
|
-
deprecated_perform(sqs_msg)
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
109
|
def require_workers
|
|
139
110
|
required = Shoryuken.options[:require]
|
|
140
111
|
|
|
@@ -154,7 +125,7 @@ module Shoryuken
|
|
|
154
125
|
|
|
155
126
|
Shoryuken.queues.uniq.each do |queue|
|
|
156
127
|
begin
|
|
157
|
-
Shoryuken::Client.queues
|
|
128
|
+
Shoryuken::Client.queues(queue)
|
|
158
129
|
rescue Aws::SQS::Errors::NonExistentQueue
|
|
159
130
|
non_existent_queues << queue
|
|
160
131
|
end
|
|
@@ -164,13 +135,13 @@ module Shoryuken
|
|
|
164
135
|
end
|
|
165
136
|
|
|
166
137
|
def validate_workers
|
|
138
|
+
return if defined?(::ActiveJob)
|
|
139
|
+
|
|
167
140
|
all_queues = Shoryuken.queues
|
|
168
141
|
queues_with_workers = Shoryuken.worker_registry.queues
|
|
169
142
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Shoryuken.logger.warn { "No worker supplied for '#{queue}'" }
|
|
173
|
-
end
|
|
143
|
+
(all_queues - queues_with_workers).each do |queue|
|
|
144
|
+
Shoryuken.logger.warn { "No worker supplied for '#{queue}'" }
|
|
174
145
|
end
|
|
175
146
|
end
|
|
176
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.debug { "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: #{reason}" }
|
|
40
|
-
exit 1
|
|
16
|
+
@manager.start
|
|
41
17
|
end
|
|
42
18
|
end
|
|
43
19
|
end
|
data/lib/shoryuken/manager.rb
CHANGED
|
@@ -1,251 +1,133 @@
|
|
|
1
|
-
require 'shoryuken/processor'
|
|
2
|
-
require 'shoryuken/fetcher'
|
|
3
|
-
|
|
4
1
|
module Shoryuken
|
|
5
2
|
class Manager
|
|
6
|
-
include Celluloid
|
|
7
3
|
include Util
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
BATCH_LIMIT = 10
|
|
6
|
+
HEARTBEAT_INTERVAL = 0.1
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
def initialize(fetcher, polling_strategy)
|
|
9
|
+
@count = Shoryuken.options.fetch(:concurrency, 25)
|
|
12
10
|
|
|
13
|
-
def initialize(condvar)
|
|
14
|
-
@count = Shoryuken.options[:concurrency] || 25
|
|
15
11
|
raise(ArgumentError, "Concurrency value #{@count} is invalid, it needs to be a positive number") unless @count > 0
|
|
12
|
+
|
|
16
13
|
@queues = Shoryuken.queues.dup.uniq
|
|
17
|
-
@finished = condvar
|
|
18
14
|
|
|
19
|
-
@done = false
|
|
15
|
+
@done = Concurrent::AtomicBoolean.new(false)
|
|
16
|
+
@dispatching = Concurrent::AtomicBoolean.new(false)
|
|
17
|
+
|
|
18
|
+
@fetcher = fetcher
|
|
19
|
+
@polling_strategy = polling_strategy
|
|
20
20
|
|
|
21
|
-
@
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
@heartbeat = Concurrent::TimerTask.new(run_now: true,
|
|
22
|
+
execution_interval: HEARTBEAT_INTERVAL,
|
|
23
|
+
timeout_interval: 60) { dispatch }
|
|
24
|
+
|
|
25
|
+
@pool = Concurrent::FixedThreadPool.new(@count, max_queue: @count)
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def start
|
|
27
29
|
logger.info { 'Starting' }
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
@heartbeat.execute
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def stop(options = {})
|
|
33
|
-
|
|
34
|
-
@done = true
|
|
35
|
-
|
|
36
|
-
if (callback = Shoryuken.stop_callback)
|
|
37
|
-
logger.info { 'Calling Shoryuken.on_stop block' }
|
|
38
|
-
callback.call
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
fire_event(:shutdown, true)
|
|
42
|
-
|
|
43
|
-
@fetcher.terminate if @fetcher.alive?
|
|
44
|
-
|
|
45
|
-
logger.info { "Shutting down #{@ready.size} quiet workers" }
|
|
35
|
+
@done.make_true
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@ready.clear
|
|
51
|
-
|
|
52
|
-
return after(0) { @finished.signal } if @busy.empty?
|
|
53
|
-
|
|
54
|
-
if options[:shutdown]
|
|
55
|
-
hard_shutdown_in(options[:timeout])
|
|
56
|
-
else
|
|
57
|
-
soft_shutdown(options[:timeout])
|
|
58
|
-
end
|
|
37
|
+
if (callback = Shoryuken.stop_callback)
|
|
38
|
+
logger.info { 'Calling Shoryuken.on_stop block' }
|
|
39
|
+
callback.call
|
|
59
40
|
end
|
|
60
|
-
end
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
watchdog('Manager#processor_done died') do
|
|
64
|
-
logger.debug { "Process done for '#{queue}'" }
|
|
42
|
+
fire_event(:shutdown, true)
|
|
65
43
|
|
|
66
|
-
|
|
67
|
-
@busy.delete processor
|
|
44
|
+
logger.info { 'Shutting down workers' }
|
|
68
45
|
|
|
69
|
-
|
|
70
|
-
processor.terminate if processor.alive?
|
|
71
|
-
return after(0) { @finished.signal } if @busy.empty?
|
|
72
|
-
else
|
|
73
|
-
@ready << processor
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
46
|
+
@heartbeat.kill
|
|
77
47
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@threads.delete(processor.object_id)
|
|
83
|
-
@busy.delete processor
|
|
84
|
-
|
|
85
|
-
if stopped?
|
|
86
|
-
return after(0) { @finished.signal } if @busy.empty?
|
|
87
|
-
else
|
|
88
|
-
@ready << build_processor
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def stopped?
|
|
94
|
-
@done
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def assign(queue, sqs_msg)
|
|
98
|
-
watchdog('Manager#assign died') do
|
|
99
|
-
logger.debug { "Assigning #{sqs_msg.message_id}" }
|
|
100
|
-
|
|
101
|
-
processor = @ready.pop
|
|
102
|
-
@busy << processor
|
|
103
|
-
|
|
104
|
-
processor.async.process(queue, sqs_msg)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def rebalance_queue_weight!(queue)
|
|
109
|
-
watchdog('Manager#rebalance_queue_weight! died') do
|
|
110
|
-
if (original = original_queue_weight(queue)) > (current = current_queue_weight(queue))
|
|
111
|
-
logger.info { "Increasing '#{queue}' weight to #{current + 1}, max: #{original}" }
|
|
112
|
-
|
|
113
|
-
@queues << queue
|
|
114
|
-
end
|
|
48
|
+
if options[:shutdown]
|
|
49
|
+
hard_shutdown_in(options[:timeout])
|
|
50
|
+
else
|
|
51
|
+
soft_shutdown
|
|
115
52
|
end
|
|
116
53
|
end
|
|
117
54
|
|
|
118
|
-
def
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
logger.debug { "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because it's empty" }
|
|
122
|
-
|
|
123
|
-
@queues.delete(queue)
|
|
124
|
-
|
|
125
|
-
after(Shoryuken.options[:delay].to_f) { async.restart_queue!(queue) }
|
|
55
|
+
def processor_done(queue)
|
|
56
|
+
logger.debug { "Process done for '#{queue}'" }
|
|
126
57
|
end
|
|
127
58
|
|
|
59
|
+
private
|
|
128
60
|
|
|
129
61
|
def dispatch
|
|
130
|
-
return if
|
|
62
|
+
return if @done.true?
|
|
63
|
+
return unless @dispatching.make_true
|
|
131
64
|
|
|
132
|
-
|
|
65
|
+
return if ready.zero?
|
|
66
|
+
return unless (queue = @polling_strategy.next_queue)
|
|
133
67
|
|
|
134
|
-
|
|
135
|
-
logger.debug { 'Pausing fetcher, because all processors are busy' }
|
|
136
|
-
|
|
137
|
-
dispatch_later
|
|
138
|
-
return
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
if (queue = next_queue)
|
|
142
|
-
@fetcher.async.fetch(queue, @ready.size)
|
|
143
|
-
else
|
|
144
|
-
logger.debug { 'Pausing fetcher, because all queues are paused' }
|
|
68
|
+
logger.debug { "Ready: #{ready}, Busy: #{busy}, Active Queues: #{@polling_strategy.active_queues}" }
|
|
145
69
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def real_thread(proxy_id, thr)
|
|
151
|
-
@threads[proxy_id] = thr
|
|
70
|
+
batched_queue?(queue) ? dispatch_batch(queue) : dispatch_single_messages(queue)
|
|
71
|
+
ensure
|
|
72
|
+
@dispatching.make_false
|
|
152
73
|
end
|
|
153
74
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def dispatch_later
|
|
157
|
-
@_dispatch_timer ||= after(1) do
|
|
158
|
-
@_dispatch_timer = nil
|
|
159
|
-
dispatch
|
|
160
|
-
end
|
|
75
|
+
def busy
|
|
76
|
+
@count - ready
|
|
161
77
|
end
|
|
162
78
|
|
|
163
|
-
def
|
|
164
|
-
|
|
165
|
-
processor.proxy_id = processor.object_id
|
|
166
|
-
processor
|
|
79
|
+
def ready
|
|
80
|
+
@pool.remaining_capacity
|
|
167
81
|
end
|
|
168
82
|
|
|
169
|
-
def
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
unless @queues.include? queue
|
|
173
|
-
logger.debug { "Restarting '#{queue}'" }
|
|
174
|
-
|
|
175
|
-
@queues << queue
|
|
176
|
-
|
|
177
|
-
if @fetcher_paused
|
|
178
|
-
logger.debug { 'Restarting fetcher' }
|
|
179
|
-
|
|
180
|
-
@fetcher_paused = false
|
|
83
|
+
def assign(queue, sqs_msg)
|
|
84
|
+
logger.debug { "Assigning #{sqs_msg.message_id}" }
|
|
181
85
|
|
|
182
|
-
|
|
183
|
-
end
|
|
184
|
-
end
|
|
86
|
+
@pool.post { Processor.new(self).process(queue, sqs_msg) }
|
|
185
87
|
end
|
|
186
88
|
|
|
187
|
-
def
|
|
188
|
-
|
|
89
|
+
def dispatch_batch(queue)
|
|
90
|
+
batch = @fetcher.fetch(queue, BATCH_LIMIT)
|
|
91
|
+
@polling_strategy.messages_found(queue.name, batch.size)
|
|
92
|
+
assign(queue.name, patch_batch!(batch))
|
|
189
93
|
end
|
|
190
94
|
|
|
191
|
-
def
|
|
192
|
-
|
|
95
|
+
def dispatch_single_messages(queue)
|
|
96
|
+
messages = @fetcher.fetch(queue, ready)
|
|
97
|
+
@polling_strategy.messages_found(queue.name, messages.size)
|
|
98
|
+
messages.each { |message| assign(queue.name, message) }
|
|
193
99
|
end
|
|
194
100
|
|
|
195
|
-
def
|
|
196
|
-
|
|
101
|
+
def batched_queue?(queue)
|
|
102
|
+
Shoryuken.worker_registry.batch_receive_messages?(queue.name)
|
|
197
103
|
end
|
|
198
104
|
|
|
199
|
-
def
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
queue = @queues.shift
|
|
204
|
-
|
|
205
|
-
unless defined?(::ActiveJob) || !Shoryuken.worker_registry.workers(queue).empty?
|
|
206
|
-
# when no worker registered pause the queue to avoid endless recursion
|
|
207
|
-
logger.debug { "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because no workers registered" }
|
|
208
|
-
|
|
209
|
-
after(Shoryuken.options[:delay].to_f) { async.restart_queue!(queue) }
|
|
105
|
+
def soft_shutdown
|
|
106
|
+
@pool.shutdown
|
|
107
|
+
@pool.wait_for_termination
|
|
108
|
+
end
|
|
210
109
|
|
|
211
|
-
|
|
110
|
+
def hard_shutdown_in(delay)
|
|
111
|
+
if busy > 0
|
|
112
|
+
logger.info { "Pausing up to #{delay} seconds to allow workers to finish..." }
|
|
212
113
|
end
|
|
213
114
|
|
|
214
|
-
|
|
215
|
-
@queues << queue
|
|
216
|
-
|
|
217
|
-
queue
|
|
218
|
-
end
|
|
115
|
+
@pool.shutdown
|
|
219
116
|
|
|
220
|
-
|
|
221
|
-
logger.info { "Waiting for #{@busy.size} busy workers" }
|
|
117
|
+
return if @pool.wait_for_termination(delay)
|
|
222
118
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
else
|
|
226
|
-
@finished.signal
|
|
227
|
-
end
|
|
119
|
+
logger.info { "Hard shutting down #{busy} busy workers" }
|
|
120
|
+
@pool.kill
|
|
228
121
|
end
|
|
229
122
|
|
|
230
|
-
def
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
after(delay) do
|
|
235
|
-
watchdog('Manager#hard_shutdown_in died') do
|
|
236
|
-
if @busy.size > 0
|
|
237
|
-
logger.info { "Hard shutting down #{@busy.size} busy workers" }
|
|
238
|
-
|
|
239
|
-
@busy.each do |processor|
|
|
240
|
-
if processor.alive? && t = @threads.delete(processor.object_id)
|
|
241
|
-
t.raise Shutdown
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
@finished.signal
|
|
123
|
+
def patch_batch!(sqs_msgs)
|
|
124
|
+
sqs_msgs.instance_eval do
|
|
125
|
+
def message_id
|
|
126
|
+
"batch-with-#{size}-messages"
|
|
247
127
|
end
|
|
248
128
|
end
|
|
129
|
+
|
|
130
|
+
sqs_msgs
|
|
249
131
|
end
|
|
250
132
|
end
|
|
251
133
|
end
|
data/lib/shoryuken/message.rb
CHANGED
|
@@ -3,19 +3,10 @@ module Shoryuken
|
|
|
3
3
|
attr_accessor :client, :queue_url, :queue_name, :data
|
|
4
4
|
|
|
5
5
|
def initialize(client, queue, data)
|
|
6
|
-
self.client
|
|
7
|
-
self.data
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
self.queue_url = queue.url
|
|
11
|
-
self.queue_name = queue.name
|
|
12
|
-
else
|
|
13
|
-
# TODO: Remove next major release
|
|
14
|
-
Shoryuken.logger.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
|
|
6
|
+
self.client = client
|
|
7
|
+
self.data = data
|
|
8
|
+
self.queue_url = queue.url
|
|
9
|
+
self.queue_name = queue.name
|
|
19
10
|
end
|
|
20
11
|
|
|
21
12
|
def delete
|
|
@@ -102,32 +102,15 @@ module Shoryuken
|
|
|
102
102
|
|
|
103
103
|
class Entry
|
|
104
104
|
attr_reader :klass
|
|
105
|
+
|
|
105
106
|
def initialize(klass, *args)
|
|
106
107
|
@klass = klass
|
|
107
108
|
@args = args
|
|
108
|
-
|
|
109
|
-
patch_deprecated_middleware!(klass)
|
|
110
109
|
end
|
|
111
110
|
|
|
112
111
|
def make_new
|
|
113
112
|
@klass.new(*@args)
|
|
114
113
|
end
|
|
115
|
-
|
|
116
|
-
private
|
|
117
|
-
|
|
118
|
-
def patch_deprecated_middleware!(klass)
|
|
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)" }
|
|
121
|
-
|
|
122
|
-
klass.class_eval do
|
|
123
|
-
alias_method :deprecated_call, :call
|
|
124
|
-
|
|
125
|
-
def call(worker_instance, queue, sqs_msg, body = nil, &block)
|
|
126
|
-
deprecated_call(worker_instance, queue, sqs_msg, &block)
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
114
|
end
|
|
132
115
|
end
|
|
133
116
|
end
|