shoryuken 3.0.6 → 4.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +90 -24
  3. data/.travis.yml +17 -5
  4. data/CHANGELOG.md +265 -62
  5. data/Gemfile +9 -1
  6. data/Gemfile.aws-sdk-core-v2 +13 -0
  7. data/README.md +19 -113
  8. data/Rakefile +1 -1
  9. data/bin/cli/base.rb +0 -3
  10. data/bin/cli/sqs.rb +42 -16
  11. data/bin/shoryuken +4 -9
  12. data/examples/bootstrap_queues.rb +3 -3
  13. data/examples/default_worker.rb +2 -2
  14. data/lib/shoryuken/body_parser.rb +27 -0
  15. data/lib/shoryuken/client.rb +6 -2
  16. data/lib/shoryuken/core_ext.rb +1 -1
  17. data/lib/shoryuken/default_worker_registry.rb +2 -2
  18. data/lib/shoryuken/environment_loader.rb +60 -24
  19. data/lib/shoryuken/extensions/active_job_adapter.rb +21 -11
  20. data/lib/shoryuken/fetcher.rb +58 -19
  21. data/lib/shoryuken/launcher.rb +70 -7
  22. data/lib/shoryuken/logging.rb +1 -6
  23. data/lib/shoryuken/manager.rb +50 -80
  24. data/lib/shoryuken/middleware/chain.rb +4 -0
  25. data/lib/shoryuken/middleware/server/active_record.rb +1 -1
  26. data/lib/shoryuken/middleware/server/auto_delete.rb +4 -9
  27. data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +6 -9
  28. data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +9 -3
  29. data/lib/shoryuken/middleware/server/timing.rb +12 -16
  30. data/lib/shoryuken/options.rb +225 -0
  31. data/lib/shoryuken/polling/base.rb +67 -0
  32. data/lib/shoryuken/polling/strict_priority.rb +77 -0
  33. data/lib/shoryuken/polling/weighted_round_robin.rb +66 -0
  34. data/lib/shoryuken/processor.rb +30 -39
  35. data/lib/shoryuken/queue.rb +41 -10
  36. data/lib/shoryuken/runner.rb +13 -17
  37. data/lib/shoryuken/util.rb +3 -3
  38. data/lib/shoryuken/version.rb +1 -1
  39. data/lib/shoryuken/worker/default_executor.rb +33 -0
  40. data/lib/shoryuken/worker/inline_executor.rb +37 -0
  41. data/lib/shoryuken/worker.rb +76 -31
  42. data/lib/shoryuken/worker_registry.rb +4 -4
  43. data/lib/shoryuken.rb +54 -173
  44. data/shoryuken.gemspec +6 -6
  45. data/spec/integration/launcher_spec.rb +14 -8
  46. data/spec/shoryuken/body_parser_spec.rb +89 -0
  47. data/spec/shoryuken/client_spec.rb +1 -1
  48. data/spec/shoryuken/core_ext_spec.rb +6 -6
  49. data/spec/shoryuken/default_worker_registry_spec.rb +2 -4
  50. data/spec/shoryuken/environment_loader_spec.rb +32 -12
  51. data/spec/shoryuken/extensions/active_job_adapter_spec.rb +64 -0
  52. data/spec/shoryuken/fetcher_spec.rb +101 -18
  53. data/spec/shoryuken/manager_spec.rb +54 -26
  54. data/spec/shoryuken/middleware/chain_spec.rb +17 -5
  55. data/spec/shoryuken/middleware/server/auto_delete_spec.rb +9 -7
  56. data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +4 -4
  57. data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +6 -4
  58. data/spec/shoryuken/middleware/server/timing_spec.rb +5 -3
  59. data/spec/shoryuken/options_spec.rb +180 -0
  60. data/spec/shoryuken/{polling_spec.rb → polling/strict_priority_spec.rb} +2 -101
  61. data/spec/shoryuken/polling/weighted_round_robin_spec.rb +99 -0
  62. data/spec/shoryuken/processor_spec.rb +26 -127
  63. data/spec/shoryuken/queue_spec.rb +115 -41
  64. data/spec/shoryuken/runner_spec.rb +3 -4
  65. data/spec/shoryuken/util_spec.rb +24 -0
  66. data/spec/shoryuken/worker/default_executor_spec.rb +105 -0
  67. data/spec/shoryuken/worker/inline_executor_spec.rb +49 -0
  68. data/spec/shoryuken/worker_spec.rb +35 -96
  69. data/spec/shoryuken_spec.rb +0 -59
  70. data/spec/spec_helper.rb +14 -3
  71. data/test_workers/endless_interruptive_worker.rb +2 -2
  72. data/test_workers/endless_uninterruptive_worker.rb +4 -4
  73. metadata +31 -12
  74. data/lib/shoryuken/polling.rb +0 -204
@@ -0,0 +1,225 @@
1
+ module Shoryuken
2
+ class Options
3
+ DEFAULTS = {
4
+ concurrency: 25,
5
+ queues: [],
6
+ aws: {},
7
+ delay: 0,
8
+ timeout: 8,
9
+ lifecycle_events: {
10
+ startup: [],
11
+ dispatch: [],
12
+ quiet: [],
13
+ shutdown: []
14
+ }
15
+ }.freeze
16
+
17
+ @@groups = {}
18
+ @@worker_registry = DefaultWorkerRegistry.new
19
+ @@active_job_queue_name_prefixing = false
20
+ @@sqs_client = nil
21
+ @@sqs_client_receive_message_opts = {}
22
+ @@start_callback = nil
23
+ @@stop_callback = nil
24
+ @@worker_executor = Worker::DefaultExecutor
25
+ @@launcher_executor = nil
26
+
27
+ class << self
28
+ def active_job?
29
+ defined?(::ActiveJob)
30
+ end
31
+
32
+ def add_group(group, concurrency)
33
+ groups[group] ||= {
34
+ concurrency: concurrency,
35
+ queues: []
36
+ }
37
+ end
38
+
39
+ def groups
40
+ @@groups
41
+ end
42
+
43
+ def add_queue(queue, weight, group)
44
+ weight.times do
45
+ groups[group][:queues] << queue
46
+ end
47
+ end
48
+
49
+ def ungrouped_queues
50
+ groups.values.flat_map { |options| options[:queues] }
51
+ end
52
+
53
+ def worker_registry
54
+ @@worker_registry
55
+ end
56
+
57
+ def worker_registry=(worker_registry)
58
+ @@worker_registry = worker_registry
59
+ end
60
+
61
+ def worker_executor
62
+ @@worker_executor
63
+ end
64
+
65
+ def worker_executor=(worker_executor)
66
+ @@worker_executor = worker_executor
67
+ end
68
+
69
+ def launcher_executor
70
+ @@launcher_executor
71
+ end
72
+
73
+ def launcher_executor=(launcher_executor)
74
+ @@launcher_executor = launcher_executor
75
+ end
76
+
77
+ def polling_strategy(group)
78
+ strategy = (group == 'default' ? options : options[:groups].to_h[group]).to_h[:polling_strategy]
79
+
80
+ case strategy
81
+ when 'WeightedRoundRobin', nil # Default case
82
+ Polling::WeightedRoundRobin
83
+ when 'StrictPriority'
84
+ Polling::StrictPriority
85
+ when Class
86
+ strategy
87
+ else
88
+ raise ArgumentError, "#{strategy} is not a valid polling_strategy"
89
+ end
90
+ end
91
+
92
+ def start_callback
93
+ @@start_callback
94
+ end
95
+
96
+ def start_callback=(start_callback)
97
+ @@start_callback = start_callback
98
+ end
99
+
100
+ def stop_callback
101
+ @@stop_callback
102
+ end
103
+
104
+ def stop_callback=(stop_callback)
105
+ @@stop_callback = stop_callback
106
+ end
107
+
108
+ def active_job_queue_name_prefixing
109
+ @@active_job_queue_name_prefixing
110
+ end
111
+
112
+ def active_job_queue_name_prefixing=(active_job_queue_name_prefixing)
113
+ @@active_job_queue_name_prefixing = active_job_queue_name_prefixing
114
+ end
115
+
116
+ def sqs_client
117
+ @@sqs_client ||= Aws::SQS::Client.new
118
+ end
119
+
120
+ def sqs_client=(sqs_client)
121
+ @@sqs_client = sqs_client
122
+ end
123
+
124
+ def sqs_client_receive_message_opts
125
+ @@sqs_client_receive_message_opts
126
+ end
127
+
128
+ def sqs_client_receive_message_opts=(sqs_client_receive_message_opts)
129
+ @@sqs_client_receive_message_opts['default'] = sqs_client_receive_message_opts
130
+ end
131
+
132
+ def options
133
+ @@options ||= DEFAULTS.dup
134
+ end
135
+
136
+ def logger
137
+ Shoryuken::Logging.logger
138
+ end
139
+
140
+ def register_worker(*args)
141
+ @@worker_registry.register_worker(*args)
142
+ end
143
+
144
+ def configure_server
145
+ yield self if server?
146
+ end
147
+
148
+ def server_middleware
149
+ @@server_chain ||= default_server_middleware
150
+ yield @@server_chain if block_given?
151
+ @@server_chain
152
+ end
153
+
154
+ def configure_client
155
+ yield self unless server?
156
+ end
157
+
158
+ def client_middleware
159
+ @@client_chain ||= default_client_middleware
160
+ yield @@client_chain if block_given?
161
+ @@client_chain
162
+ end
163
+
164
+ def default_worker_options
165
+ @@default_worker_options ||= {
166
+ 'queue' => 'default',
167
+ 'delete' => false,
168
+ 'auto_delete' => false,
169
+ 'auto_visibility_timeout' => false,
170
+ 'retry_intervals' => nil,
171
+ 'batch' => false
172
+ }
173
+ end
174
+
175
+ def default_worker_options=(default_worker_options)
176
+ @@default_worker_options = default_worker_options
177
+ end
178
+
179
+ def on_start(&block)
180
+ @@start_callback = block
181
+ end
182
+
183
+ def on_stop(&block)
184
+ @@stop_callback = block
185
+ end
186
+
187
+ # Register a block to run at a point in the Shoryuken lifecycle.
188
+ # :startup, :quiet or :shutdown are valid events.
189
+ #
190
+ # Shoryuken.configure_server do |config|
191
+ # config.on(:shutdown) do
192
+ # puts "Goodbye cruel world!"
193
+ # end
194
+ # end
195
+ def on(event, &block)
196
+ fail ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
197
+ fail ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
198
+ options[:lifecycle_events][event] << block
199
+ end
200
+
201
+ private
202
+
203
+ def default_server_middleware
204
+ Middleware::Chain.new do |m|
205
+ m.add Middleware::Server::Timing
206
+ m.add Middleware::Server::ExponentialBackoffRetry
207
+ m.add Middleware::Server::AutoDelete
208
+ m.add Middleware::Server::AutoExtendVisibility
209
+ if defined?(::ActiveRecord::Base)
210
+ require 'shoryuken/middleware/server/active_record'
211
+ m.add Middleware::Server::ActiveRecord
212
+ end
213
+ end
214
+ end
215
+
216
+ def default_client_middleware
217
+ Middleware::Chain.new
218
+ end
219
+
220
+ def server?
221
+ defined?(Shoryuken::CLI)
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,67 @@
1
+ module Shoryuken
2
+ module Polling
3
+ QueueConfiguration = Struct.new(:name, :options) do
4
+ def hash
5
+ name.hash
6
+ end
7
+
8
+ def ==(other)
9
+ case other
10
+ when String
11
+ if options.empty?
12
+ name == other
13
+ else
14
+ false
15
+ end
16
+ else
17
+ super
18
+ end
19
+ end
20
+
21
+ alias_method :eql?, :==
22
+
23
+ def to_s
24
+ if options.empty?
25
+ name
26
+ else
27
+ "#<QueueConfiguration #{name} options=#{options.inspect}>"
28
+ end
29
+ end
30
+ end
31
+
32
+ class BaseStrategy
33
+ include Util
34
+
35
+ def next_queue
36
+ fail NotImplementedError
37
+ end
38
+
39
+ def messages_found(_queue, _messages_found)
40
+ fail NotImplementedError
41
+ end
42
+
43
+ def active_queues
44
+ fail NotImplementedError
45
+ end
46
+
47
+ def ==(other)
48
+ case other
49
+ when Array
50
+ @queues == other
51
+ else
52
+ if other.respond_to?(:active_queues)
53
+ active_queues == other.active_queues
54
+ else
55
+ false
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def delay
63
+ Shoryuken.options[:delay].to_f
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,77 @@
1
+ module Shoryuken
2
+ module Polling
3
+ class StrictPriority < BaseStrategy
4
+ def initialize(queues)
5
+ # Priority ordering of the queues, highest priority first
6
+ @queues = queues
7
+ .group_by { |q| q }
8
+ .sort_by { |_, qs| -qs.count }
9
+ .map(&:first)
10
+
11
+ # Pause status of the queues, default to past time (unpaused)
12
+ @paused_until = queues
13
+ .each_with_object({}) { |queue, h| h[queue] = Time.at(0) }
14
+
15
+ # Start queues at 0
16
+ reset_next_queue
17
+ end
18
+
19
+ def next_queue
20
+ next_queue = next_active_queue
21
+ next_queue.nil? ? nil : QueueConfiguration.new(next_queue, {})
22
+ end
23
+
24
+ def messages_found(queue, messages_found)
25
+ if messages_found == 0
26
+ pause(queue)
27
+ else
28
+ reset_next_queue
29
+ end
30
+ end
31
+
32
+ def active_queues
33
+ @queues
34
+ .reverse
35
+ .map.with_index(1)
36
+ .reject { |q, _| queue_paused?(q) }
37
+ .reverse
38
+ end
39
+
40
+ private
41
+
42
+ def next_active_queue
43
+ reset_next_queue if queues_unpaused_since?
44
+
45
+ size = @queues.length
46
+ size.times do
47
+ queue = @queues[@next_queue_index]
48
+ @next_queue_index = (@next_queue_index + 1) % size
49
+ return queue unless queue_paused?(queue)
50
+ end
51
+
52
+ nil
53
+ end
54
+
55
+ def queues_unpaused_since?
56
+ last = @last_unpause_check
57
+ now = @last_unpause_check = Time.now
58
+
59
+ last && @paused_until.values.any? { |t| t > last && t <= now }
60
+ end
61
+
62
+ def reset_next_queue
63
+ @next_queue_index = 0
64
+ end
65
+
66
+ def queue_paused?(queue)
67
+ @paused_until[queue] > Time.now
68
+ end
69
+
70
+ def pause(queue)
71
+ return unless delay > 0
72
+ @paused_until[queue] = Time.now + delay
73
+ logger.debug "Paused #{queue}"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,66 @@
1
+ module Shoryuken
2
+ module Polling
3
+ class WeightedRoundRobin < BaseStrategy
4
+ def initialize(queues)
5
+ @initial_queues = queues
6
+ @queues = queues.dup.uniq
7
+ @paused_queues = []
8
+ end
9
+
10
+ def next_queue
11
+ unpause_queues
12
+ queue = @queues.shift
13
+ return nil if queue.nil?
14
+
15
+ @queues << queue
16
+ QueueConfiguration.new(queue, {})
17
+ end
18
+
19
+ def messages_found(queue, messages_found)
20
+ if messages_found == 0
21
+ pause(queue)
22
+ return
23
+ end
24
+
25
+ maximum_weight = maximum_queue_weight(queue)
26
+ current_weight = current_queue_weight(queue)
27
+ if maximum_weight > current_weight
28
+ logger.info { "Increasing #{queue} weight to #{current_weight + 1}, max: #{maximum_weight}" }
29
+ @queues << queue
30
+ end
31
+ end
32
+
33
+ def active_queues
34
+ unparse_queues(@queues)
35
+ end
36
+
37
+ private
38
+
39
+ def pause(queue)
40
+ return unless @queues.delete(queue)
41
+ @paused_queues << [Time.now + delay, queue]
42
+ logger.debug "Paused #{queue}"
43
+ end
44
+
45
+ def unpause_queues
46
+ return if @paused_queues.empty?
47
+ return if Time.now < @paused_queues.first[0]
48
+ pause = @paused_queues.shift
49
+ @queues << pause[1]
50
+ logger.debug "Unpaused #{pause[1]}"
51
+ end
52
+
53
+ def current_queue_weight(queue)
54
+ queue_weight(@queues, queue)
55
+ end
56
+
57
+ def maximum_queue_weight(queue)
58
+ queue_weight(@initial_queues, queue)
59
+ end
60
+
61
+ def queue_weight(queues, queue)
62
+ queues.count { |q| q == queue }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -2,57 +2,48 @@ module Shoryuken
2
2
  class Processor
3
3
  include Util
4
4
 
5
- def initialize(manager)
6
- @manager = manager
5
+ attr_reader :queue, :sqs_msg
6
+
7
+ def self.process(queue, sqs_msg)
8
+ new(queue, sqs_msg).process
9
+ end
10
+
11
+ def initialize(queue, sqs_msg)
12
+ @queue = queue
13
+ @sqs_msg = sqs_msg
7
14
  end
8
15
 
9
- def process(queue, sqs_msg)
10
- worker = Shoryuken.worker_registry.fetch_worker(queue, sqs_msg)
11
- body = get_body(worker.class, sqs_msg)
16
+ def process
17
+ return logger.error { "No worker found for #{queue}" } unless worker
12
18
 
13
- worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
14
- worker.perform(sqs_msg, body)
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
15
23
  end
16
24
  rescue Exception => ex
17
- @manager.processor_failed(ex)
25
+ logger.error { "Processor failed: #{ex.message}" }
26
+ logger.error { ex.backtrace.join("\n") } unless ex.backtrace.nil?
27
+
18
28
  raise
19
- ensure
20
- @manager.processor_done(queue)
21
29
  end
22
30
 
23
31
  private
24
32
 
25
- def get_body(worker_class, sqs_msg)
26
- if sqs_msg.is_a? Array
27
- sqs_msg.map { |m| parse_body(worker_class, m) }
28
- else
29
- parse_body(worker_class, sqs_msg)
30
- end
33
+ def worker
34
+ @_worker ||= Shoryuken.worker_registry.fetch_worker(queue, sqs_msg)
31
35
  end
32
36
 
33
- def parse_body(worker_class, sqs_msg)
34
- body_parser = worker_class.get_shoryuken_options['body_parser']
35
-
36
- case body_parser
37
- when :json
38
- JSON.parse(sqs_msg.body)
39
- when Proc
40
- body_parser.call(sqs_msg)
41
- when :text, nil
42
- sqs_msg.body
43
- else
44
- if body_parser.respond_to?(:parse)
45
- # JSON.parse
46
- body_parser.parse(sqs_msg.body)
47
- elsif body_parser.respond_to?(:load)
48
- # see https://github.com/phstc/shoryuken/pull/91
49
- # JSON.load
50
- body_parser.load(sqs_msg.body)
51
- end
52
- end
53
- rescue => e
54
- logger.error { "Error parsing the message body: #{e.message}\nbody_parser: #{body_parser}\nsqs_msg.body: #{sqs_msg.body}" }
55
- raise
37
+ def worker_class
38
+ worker.class
39
+ end
40
+
41
+ def body
42
+ @_body ||= sqs_msg.is_a?(Array) ? sqs_msg.map(&method(:parse_body)) : parse_body(sqs_msg)
43
+ end
44
+
45
+ def parse_body(sqs_msg)
46
+ BodyParser.parse(worker_class, sqs_msg)
56
47
  end
57
48
  end
58
49
  end
@@ -1,17 +1,16 @@
1
1
  module Shoryuken
2
2
  class Queue
3
- FIFO_ATTR = 'FifoQueue'
4
- MESSAGE_GROUP_ID = 'ShoryukenMessage'
5
- VISIBILITY_TIMEOUT_ATTR = 'VisibilityTimeout'
3
+ include Util
4
+
5
+ FIFO_ATTR = 'FifoQueue'.freeze
6
+ MESSAGE_GROUP_ID = 'ShoryukenMessage'.freeze
7
+ VISIBILITY_TIMEOUT_ATTR = 'VisibilityTimeout'.freeze
6
8
 
7
9
  attr_accessor :name, :client, :url
8
10
 
9
- def initialize(client, name)
10
- self.name = name
11
+ def initialize(client, name_or_url)
11
12
  self.client = client
12
- self.url = client.get_queue_url(queue_name: name).queue_url
13
- rescue Aws::SQS::Errors::NonExistentQueue => e
14
- raise e, "The specified queue '#{name}' does not exist."
13
+ set_name_and_url(name_or_url)
15
14
  end
16
15
 
17
16
  def visibility_timeout
@@ -19,7 +18,13 @@ module Shoryuken
19
18
  end
20
19
 
21
20
  def delete_messages(options)
22
- client.delete_message_batch(options.merge(queue_url: url))
21
+ client.delete_message_batch(
22
+ options.merge(queue_url: url)
23
+ ).failed.any? do |failure|
24
+ logger.error do
25
+ "Could not delete #{failure.id}, code: '#{failure.code}', message: '#{failure.message}', sender_fault: #{failure.sender_fault}"
26
+ end
27
+ end
23
28
  end
24
29
 
25
30
  def send_message(options)
@@ -39,11 +44,37 @@ module Shoryuken
39
44
  end
40
45
 
41
46
  def fifo?
42
- @_fifo ||= queue_attributes.attributes[FIFO_ATTR] == 'true'
47
+ # Make sure the memoization work with boolean to avoid multiple calls to SQS
48
+ # see https://github.com/phstc/shoryuken/pull/529
49
+ return @_fifo if defined?(@_fifo)
50
+ @_fifo = queue_attributes.attributes[FIFO_ATTR] == 'true'
43
51
  end
44
52
 
45
53
  private
46
54
 
55
+ def set_by_name(name)
56
+ self.name = name
57
+ self.url = client.get_queue_url(queue_name: name).queue_url
58
+ end
59
+
60
+ def set_by_url(url)
61
+ self.name = url.split('/').last
62
+ self.url = url
63
+ end
64
+
65
+ def set_name_and_url(name_or_url)
66
+ if name_or_url.start_with?('https://sqs.')
67
+ set_by_url(name_or_url)
68
+
69
+ # anticipate the fifo? checker for validating the queue URL
70
+ return fifo?
71
+ end
72
+
73
+ set_by_name(name_or_url)
74
+ rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => ex
75
+ raise ex, "The specified queue #{name_or_url} does not exist."
76
+ end
77
+
47
78
  def queue_attributes
48
79
  # Note: Retrieving all queue attributes as requesting `FifoQueue` on non-FIFO queue raises error.
49
80
  # See issue: https://github.com/aws/aws-sdk-ruby/issues/1350
@@ -8,7 +8,6 @@ require 'shoryuken'
8
8
 
9
9
  module Shoryuken
10
10
  # rubocop:disable Lint/InheritException
11
- # rubocop:disable Metrics/AbcSize
12
11
  # See: https://github.com/mperham/sidekiq/blob/33f5d6b2b6c0dfaab11e5d39688cab7ebadc83ae/lib/sidekiq/cli.rb#L20
13
12
  class Shutdown < Interrupt; end
14
13
 
@@ -19,7 +18,7 @@ module Shoryuken
19
18
  def run(options)
20
19
  self_read, self_write = IO.pipe
21
20
 
22
- %w(INT TERM USR1 USR2 TTIN).each do |sig|
21
+ %w[INT TERM USR1 TSTP TTIN].each do |sig|
23
22
  begin
24
23
  trap sig do
25
24
  self_write.puts(sig)
@@ -43,22 +42,15 @@ module Shoryuken
43
42
 
44
43
  @launcher = Shoryuken::Launcher.new
45
44
 
46
- if (callback = Shoryuken.start_callback)
47
- logger.info { 'Calling Shoryuken.on_start block' }
48
- callback.call
49
- end
50
-
51
- fire_event(:startup)
52
-
53
45
  begin
54
- @launcher.run
46
+ @launcher.start
55
47
 
56
48
  while (readable_io = IO.select([self_read]))
57
49
  signal = readable_io.first[0].gets.strip
58
50
  handle_signal(signal)
59
51
  end
60
52
  rescue Interrupt
61
- @launcher.stop(shutdown: true)
53
+ @launcher.stop!
62
54
  exit 0
63
55
  end
64
56
  end
@@ -110,10 +102,15 @@ module Shoryuken
110
102
  logger.info { 'Received USR1, will soft shutdown down' }
111
103
 
112
104
  @launcher.stop
113
- fire_event(:quiet, true)
114
105
  exit 0
115
106
  end
116
107
 
108
+ def execute_terminal_stop
109
+ logger.info { 'Received TSTP, will stop accepting new work' }
110
+
111
+ @launcher.stop
112
+ end
113
+
117
114
  def print_threads_backtrace
118
115
  Thread.list.each do |thread|
119
116
  logger.info { "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}" }
@@ -126,15 +123,14 @@ module Shoryuken
126
123
  end
127
124
 
128
125
  def handle_signal(sig)
129
- logger.info { "Got #{sig} signal" }
126
+ logger.debug "Got #{sig} signal"
130
127
 
131
128
  case sig
132
129
  when 'USR1' then execute_soft_shutdown
133
130
  when 'TTIN' then print_threads_backtrace
134
- when 'USR2'
135
- logger.warn { "Received #{sig}, will do nothing. To execute soft shutdown, please send USR1" }
136
- else
137
- logger.info { "Received #{sig}, will shutdown down" }
131
+ when 'TSTP' then execute_terminal_stop
132
+ when 'TERM', 'INT'
133
+ logger.info { "Received #{sig}, will shutdown" }
138
134
 
139
135
  raise Interrupt
140
136
  end
@@ -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 defined?(::ActiveJob) \
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'] \