shoryuken 7.0.0.alpha1 → 7.0.0.rc1
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/.github/workflows/push.yml +3 -3
- data/.github/workflows/specs.yml +27 -17
- data/.github/workflows/verify-action-pins.yml +1 -1
- data/.rspec +2 -1
- data/.ruby-version +1 -1
- data/Appraisals +6 -18
- data/CHANGELOG.md +200 -142
- data/Gemfile +1 -0
- data/README.md +12 -13
- data/bin/cli/base.rb +1 -2
- data/bin/cli/sqs.rb +6 -5
- data/bin/shoryuken +3 -2
- data/gemfiles/rails_7_2.gemfile +1 -0
- data/gemfiles/rails_8_0.gemfile +1 -0
- data/gemfiles/{rails_7_1.gemfile → rails_8_1.gemfile} +2 -1
- data/lib/shoryuken/body_parser.rb +3 -1
- data/lib/shoryuken/client.rb +2 -0
- data/lib/shoryuken/default_exception_handler.rb +2 -0
- data/lib/shoryuken/default_worker_registry.rb +11 -11
- data/lib/shoryuken/environment_loader.rb +6 -6
- data/lib/shoryuken/extensions/active_job_adapter.rb +21 -6
- data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +5 -5
- data/lib/shoryuken/extensions/active_job_extensions.rb +2 -0
- data/lib/shoryuken/fetcher.rb +4 -2
- data/lib/shoryuken/helpers/atomic_boolean.rb +44 -0
- data/lib/shoryuken/helpers/atomic_counter.rb +104 -0
- data/lib/shoryuken/helpers/atomic_hash.rb +182 -0
- data/lib/shoryuken/helpers/hash_utils.rb +56 -0
- data/lib/shoryuken/helpers/string_utils.rb +65 -0
- data/lib/shoryuken/helpers/timer_task.rb +66 -0
- data/lib/shoryuken/inline_message.rb +22 -0
- data/lib/shoryuken/launcher.rb +16 -0
- data/lib/shoryuken/logging/base.rb +26 -0
- data/lib/shoryuken/logging/pretty.rb +25 -0
- data/lib/shoryuken/logging/without_timestamp.rb +25 -0
- data/lib/shoryuken/logging.rb +6 -12
- data/lib/shoryuken/manager.rb +6 -4
- data/lib/shoryuken/message.rb +116 -1
- data/lib/shoryuken/middleware/chain.rb +140 -43
- data/lib/shoryuken/middleware/entry.rb +30 -0
- data/lib/shoryuken/middleware/server/active_record.rb +2 -0
- data/lib/shoryuken/middleware/server/auto_delete.rb +2 -0
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +11 -11
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +5 -3
- data/lib/shoryuken/middleware/server/timing.rb +2 -0
- data/lib/shoryuken/options.rb +9 -5
- data/lib/shoryuken/polling/base_strategy.rb +126 -0
- data/lib/shoryuken/polling/queue_configuration.rb +103 -0
- data/lib/shoryuken/polling/strict_priority.rb +2 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +2 -0
- data/lib/shoryuken/processor.rb +5 -2
- data/lib/shoryuken/queue.rb +6 -4
- data/lib/shoryuken/runner.rb +12 -12
- data/lib/shoryuken/util.rb +6 -6
- data/lib/shoryuken/version.rb +3 -1
- data/lib/shoryuken/worker/default_executor.rb +2 -0
- data/lib/shoryuken/worker/inline_executor.rb +3 -1
- data/lib/shoryuken/worker.rb +173 -0
- data/lib/shoryuken/worker_registry.rb +2 -0
- data/lib/shoryuken.rb +8 -28
- data/shoryuken.gemspec +6 -6
- data/spec/integration/active_job_continuation_spec.rb +145 -0
- data/spec/integration/launcher_spec.rb +2 -3
- data/spec/shared_examples_for_active_job.rb +13 -8
- data/spec/shoryuken/body_parser_spec.rb +1 -2
- data/spec/shoryuken/client_spec.rb +1 -1
- data/spec/shoryuken/default_exception_handler_spec.rb +9 -10
- data/spec/shoryuken/default_worker_registry_spec.rb +1 -2
- data/spec/shoryuken/environment_loader_spec.rb +9 -8
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +2 -1
- data/spec/shoryuken/extensions/active_job_base_spec.rb +2 -1
- data/spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb +2 -1
- data/spec/shoryuken/extensions/active_job_continuation_spec.rb +110 -0
- data/spec/shoryuken/extensions/active_job_wrapper_spec.rb +2 -1
- data/spec/shoryuken/fetcher_spec.rb +23 -26
- data/spec/shoryuken/helpers/atomic_boolean_spec.rb +196 -0
- data/spec/shoryuken/helpers/atomic_counter_spec.rb +177 -0
- data/spec/shoryuken/helpers/atomic_hash_spec.rb +307 -0
- data/spec/shoryuken/helpers/hash_utils_spec.rb +145 -0
- data/spec/shoryuken/helpers/string_utils_spec.rb +124 -0
- data/spec/shoryuken/helpers/timer_task_spec.rb +298 -0
- data/spec/shoryuken/helpers_integration_spec.rb +96 -0
- data/spec/shoryuken/inline_message_spec.rb +196 -0
- data/spec/shoryuken/launcher_spec.rb +23 -2
- data/spec/shoryuken/manager_spec.rb +1 -2
- data/spec/shoryuken/middleware/chain_spec.rb +1 -1
- data/spec/shoryuken/middleware/server/auto_delete_spec.rb +1 -1
- data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +1 -1
- data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +1 -1
- data/spec/shoryuken/middleware/server/timing_spec.rb +1 -1
- data/spec/shoryuken/options_spec.rb +4 -4
- data/spec/shoryuken/polling/base_strategy_spec.rb +280 -0
- data/spec/shoryuken/polling/queue_configuration_spec.rb +195 -0
- data/spec/shoryuken/polling/strict_priority_spec.rb +1 -1
- data/spec/shoryuken/polling/weighted_round_robin_spec.rb +1 -1
- data/spec/shoryuken/processor_spec.rb +1 -1
- data/spec/shoryuken/queue_spec.rb +2 -3
- data/spec/shoryuken/runner_spec.rb +1 -3
- data/spec/shoryuken/util_spec.rb +1 -1
- data/spec/shoryuken/worker/default_executor_spec.rb +1 -1
- data/spec/shoryuken/worker/inline_executor_spec.rb +1 -1
- data/spec/shoryuken/worker_spec.rb +15 -11
- data/spec/shoryuken_spec.rb +1 -1
- data/spec/spec_helper.rb +16 -0
- metadata +72 -29
- data/.github/FUNDING.yml +0 -12
- data/gemfiles/rails_6_1.gemfile +0 -18
- data/gemfiles/rails_7_0.gemfile +0 -19
- data/lib/shoryuken/core_ext.rb +0 -69
- data/lib/shoryuken/polling/base.rb +0 -67
- data/shoryuken.jpg +0 -0
- data/spec/shoryuken/core_ext_spec.rb +0 -40
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shoryuken
|
|
4
|
+
module Polling
|
|
5
|
+
# Configuration object representing a queue and its associated options.
|
|
6
|
+
#
|
|
7
|
+
# This class encapsulates a queue name along with any polling-specific
|
|
8
|
+
# options or metadata. It provides a structured way to pass queue
|
|
9
|
+
# information between polling strategies and the message fetching system.
|
|
10
|
+
#
|
|
11
|
+
# The class extends Struct to provide attribute accessors for name and options
|
|
12
|
+
# while adding custom behavior for equality comparison and string representation.
|
|
13
|
+
#
|
|
14
|
+
# @example Creating a basic queue configuration
|
|
15
|
+
# config = QueueConfiguration.new('my_queue', {})
|
|
16
|
+
# config.name # => 'my_queue'
|
|
17
|
+
# config.options # => {}
|
|
18
|
+
#
|
|
19
|
+
# @example Creating a queue configuration with options
|
|
20
|
+
# config = QueueConfiguration.new('priority_queue', { priority: :high })
|
|
21
|
+
# config.name # => 'priority_queue'
|
|
22
|
+
# config.options # => { priority: :high }
|
|
23
|
+
#
|
|
24
|
+
# @example Comparing configurations
|
|
25
|
+
# config1 = QueueConfiguration.new('queue', {})
|
|
26
|
+
# config2 = QueueConfiguration.new('queue', {})
|
|
27
|
+
# config1 == config2 # => true
|
|
28
|
+
# config1 == 'queue' # => true (when options are empty)
|
|
29
|
+
#
|
|
30
|
+
# @attr_reader [String] name The name of the queue
|
|
31
|
+
# @attr_reader [Hash] options Additional options or metadata for the queue
|
|
32
|
+
QueueConfiguration = Struct.new(:name, :options) do
|
|
33
|
+
# Generates a hash value based on the queue name.
|
|
34
|
+
#
|
|
35
|
+
# This method ensures that QueueConfiguration objects can be used
|
|
36
|
+
# as hash keys and that configurations with the same queue name
|
|
37
|
+
# will have the same hash value regardless of their options.
|
|
38
|
+
#
|
|
39
|
+
# @return [Integer] Hash value based on the queue name
|
|
40
|
+
def hash
|
|
41
|
+
name.hash
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Compares this configuration with another object for equality.
|
|
45
|
+
#
|
|
46
|
+
# Two QueueConfiguration objects are equal if they have the same name
|
|
47
|
+
# and options. For convenience, a configuration with empty options can
|
|
48
|
+
# also be compared directly with a string queue name.
|
|
49
|
+
#
|
|
50
|
+
# @param other [Object] The object to compare with
|
|
51
|
+
# @return [Boolean] true if the objects are considered equal
|
|
52
|
+
#
|
|
53
|
+
# @example Comparing with another QueueConfiguration
|
|
54
|
+
# config1 = QueueConfiguration.new('queue', {})
|
|
55
|
+
# config2 = QueueConfiguration.new('queue', {})
|
|
56
|
+
# config1 == config2 # => true
|
|
57
|
+
#
|
|
58
|
+
# @example Comparing with a string (only when options are empty)
|
|
59
|
+
# config = QueueConfiguration.new('queue', {})
|
|
60
|
+
# config == 'queue' # => true
|
|
61
|
+
#
|
|
62
|
+
# config_with_options = QueueConfiguration.new('queue', { weight: 5 })
|
|
63
|
+
# config_with_options == 'queue' # => false
|
|
64
|
+
def ==(other)
|
|
65
|
+
case other
|
|
66
|
+
when String
|
|
67
|
+
if options.empty?
|
|
68
|
+
name == other
|
|
69
|
+
else
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
alias_method :eql?, :==
|
|
78
|
+
|
|
79
|
+
# Returns a string representation of the queue configuration.
|
|
80
|
+
#
|
|
81
|
+
# For configurations with empty options, returns just the queue name.
|
|
82
|
+
# For configurations with options, returns a detailed representation
|
|
83
|
+
# showing both the name and the options hash.
|
|
84
|
+
#
|
|
85
|
+
# @return [String] String representation of the configuration
|
|
86
|
+
#
|
|
87
|
+
# @example Simple queue without options
|
|
88
|
+
# config = QueueConfiguration.new('simple_queue', {})
|
|
89
|
+
# config.to_s # => 'simple_queue'
|
|
90
|
+
#
|
|
91
|
+
# @example Queue with options
|
|
92
|
+
# config = QueueConfiguration.new('complex_queue', { priority: :high })
|
|
93
|
+
# config.to_s # => '#<QueueConfiguration complex_queue options={:priority=>:high}>'
|
|
94
|
+
def to_s
|
|
95
|
+
if options&.empty?
|
|
96
|
+
name
|
|
97
|
+
else
|
|
98
|
+
"#<QueueConfiguration #{name} options=#{options.inspect}>"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/shoryuken/processor.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Shoryuken
|
|
2
4
|
class Processor
|
|
3
5
|
include Util
|
|
@@ -16,6 +18,7 @@ module Shoryuken
|
|
|
16
18
|
def process
|
|
17
19
|
worker_perform = proc do
|
|
18
20
|
return logger.error { "No worker found for #{queue}" } unless worker
|
|
21
|
+
|
|
19
22
|
Shoryuken::Logging.with_context("#{worker_name(worker.class, sqs_msg, body)}/#{queue}/#{sqs_msg.message_id}") do
|
|
20
23
|
worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
|
|
21
24
|
worker.perform(sqs_msg, body)
|
|
@@ -30,8 +33,8 @@ module Shoryuken
|
|
|
30
33
|
else
|
|
31
34
|
worker_perform.call
|
|
32
35
|
end
|
|
33
|
-
rescue Exception =>
|
|
34
|
-
Array(Shoryuken.exception_handlers).each { |handler| handler.call(
|
|
36
|
+
rescue Exception => e
|
|
37
|
+
Array(Shoryuken.exception_handlers).each { |handler| handler.call(e, queue, sqs_msg) }
|
|
35
38
|
|
|
36
39
|
raise
|
|
37
40
|
end
|
data/lib/shoryuken/queue.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Shoryuken
|
|
2
4
|
class Queue
|
|
3
5
|
include Util
|
|
4
6
|
|
|
5
|
-
FIFO_ATTR = 'FifoQueue'
|
|
6
|
-
MESSAGE_GROUP_ID = 'ShoryukenMessage'
|
|
7
|
-
VISIBILITY_TIMEOUT_ATTR = 'VisibilityTimeout'
|
|
7
|
+
FIFO_ATTR = 'FifoQueue'
|
|
8
|
+
MESSAGE_GROUP_ID = 'ShoryukenMessage'
|
|
9
|
+
VISIBILITY_TIMEOUT_ATTR = 'VisibilityTimeout'
|
|
8
10
|
|
|
9
11
|
attr_accessor :name, :client, :url
|
|
10
12
|
|
|
@@ -50,7 +52,7 @@ module Shoryuken
|
|
|
50
52
|
|
|
51
53
|
def fifo?
|
|
52
54
|
# Make sure the memoization work with boolean to avoid multiple calls to SQS
|
|
53
|
-
# see https://github.com/
|
|
55
|
+
# see https://github.com/ruby-shoryuken/shoryuken/pull/529
|
|
54
56
|
return @_fifo if defined?(@_fifo)
|
|
55
57
|
|
|
56
58
|
@_fifo = queue_attributes.attributes[FIFO_ATTR] == 'true'
|
data/lib/shoryuken/runner.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
$stdout.sync = true
|
|
2
4
|
|
|
3
5
|
require 'singleton'
|
|
@@ -7,7 +9,6 @@ require 'erb'
|
|
|
7
9
|
require 'shoryuken'
|
|
8
10
|
|
|
9
11
|
module Shoryuken
|
|
10
|
-
# rubocop:disable Lint/InheritException
|
|
11
12
|
# See: https://github.com/mperham/sidekiq/blob/33f5d6b2b6c0dfaab11e5d39688cab7ebadc83ae/lib/sidekiq/cli.rb#L20
|
|
12
13
|
class Shutdown < Interrupt; end
|
|
13
14
|
|
|
@@ -15,17 +16,18 @@ module Shoryuken
|
|
|
15
16
|
include Util
|
|
16
17
|
include Singleton
|
|
17
18
|
|
|
19
|
+
# @return [Shoryuken::Launcher, nil] the launcher instance, or nil if not yet initialized
|
|
20
|
+
attr_reader :launcher
|
|
21
|
+
|
|
18
22
|
def run(options)
|
|
19
23
|
self_read, self_write = IO.pipe
|
|
20
24
|
|
|
21
25
|
%w[INT TERM USR1 TSTP TTIN].each do |sig|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
self_write.puts(sig)
|
|
25
|
-
end
|
|
26
|
-
rescue ArgumentError
|
|
27
|
-
puts "Signal #{sig} not supported"
|
|
26
|
+
trap sig do
|
|
27
|
+
self_write.puts(sig)
|
|
28
28
|
end
|
|
29
|
+
rescue ArgumentError
|
|
30
|
+
puts "Signal #{sig} not supported"
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
loader = EnvironmentLoader.setup_options(options)
|
|
@@ -77,11 +79,9 @@ module Shoryuken
|
|
|
77
79
|
Process.daemon(true, true)
|
|
78
80
|
|
|
79
81
|
files_to_reopen.each do |file|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
rescue ::Exception
|
|
84
|
-
end
|
|
82
|
+
file.reopen file.path, 'a+'
|
|
83
|
+
file.sync = true
|
|
84
|
+
rescue ::Exception
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
[$stdout, $stderr].each do |io|
|
data/lib/shoryuken/util.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Shoryuken
|
|
2
4
|
module Util
|
|
3
5
|
def logger
|
|
@@ -9,12 +11,10 @@ module Shoryuken
|
|
|
9
11
|
arr = Shoryuken.options[:lifecycle_events][event]
|
|
10
12
|
arr.reverse! if reverse
|
|
11
13
|
arr.each do |block|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
logger.warn "#{ex.class.name}: #{ex.message}"
|
|
17
|
-
end
|
|
14
|
+
block.call(event_options)
|
|
15
|
+
rescue => e
|
|
16
|
+
logger.warn(event: event)
|
|
17
|
+
logger.warn "#{e.class.name}: #{e.message}"
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
data/lib/shoryuken/version.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Shoryuken
|
|
2
4
|
module Worker
|
|
3
5
|
class InlineExecutor
|
|
@@ -11,7 +13,7 @@ module Shoryuken
|
|
|
11
13
|
data_type: 'String'
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
sqs_msg =
|
|
16
|
+
sqs_msg = InlineMessage.new(
|
|
15
17
|
body: body,
|
|
16
18
|
attributes: nil,
|
|
17
19
|
md5_of_body: nil,
|
data/lib/shoryuken/worker.rb
CHANGED
|
@@ -1,4 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Shoryuken
|
|
4
|
+
# Worker module provides the core functionality for creating Shoryuken workers
|
|
5
|
+
# that process messages from Amazon SQS queues.
|
|
6
|
+
#
|
|
7
|
+
# Including this module in a class provides methods for configuring queue processing,
|
|
8
|
+
# enqueueing jobs, and setting up middleware. Workers can be configured for different
|
|
9
|
+
# processing patterns including single message processing, batch processing, and
|
|
10
|
+
# various retry and visibility timeout strategies.
|
|
11
|
+
#
|
|
12
|
+
# @example Basic worker implementation
|
|
13
|
+
# class EmailWorker
|
|
14
|
+
# include Shoryuken::Worker
|
|
15
|
+
# shoryuken_options queue: 'emails'
|
|
16
|
+
#
|
|
17
|
+
# def perform(sqs_msg, body)
|
|
18
|
+
# send_email(body['recipient'], body['subject'], body['content'])
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @example Advanced worker with all options
|
|
23
|
+
# class AdvancedWorker
|
|
24
|
+
# include Shoryuken::Worker
|
|
25
|
+
#
|
|
26
|
+
# shoryuken_options queue: 'advanced_queue',
|
|
27
|
+
# batch: false,
|
|
28
|
+
# auto_delete: true,
|
|
29
|
+
# auto_visibility_timeout: true,
|
|
30
|
+
# retry_intervals: [1, 5, 25, 125, 625]
|
|
31
|
+
#
|
|
32
|
+
# server_middleware do |chain|
|
|
33
|
+
# chain.add MyCustomMiddleware
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# def perform(sqs_msg, body)
|
|
37
|
+
# # Worker implementation
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# @see ClassMethods#shoryuken_options Primary configuration method
|
|
42
|
+
# @see ClassMethods#perform_async For enqueueing jobs
|
|
43
|
+
# @see https://github.com/ruby-shoryuken/shoryuken/wiki/Workers Comprehensive worker documentation
|
|
2
44
|
module Worker
|
|
3
45
|
def self.included(base)
|
|
4
46
|
base.extend(ClassMethods)
|
|
@@ -6,35 +48,166 @@ module Shoryuken
|
|
|
6
48
|
end
|
|
7
49
|
|
|
8
50
|
module ClassMethods
|
|
51
|
+
# Enqueues a job to be processed asynchronously by a Shoryuken worker.
|
|
52
|
+
#
|
|
53
|
+
# @param body [Object] The job payload that will be passed to the worker's perform method
|
|
54
|
+
# @param options [Hash] Additional options for job enqueueing
|
|
55
|
+
# @option options [String] :message_group_id FIFO queue group ID for message ordering
|
|
56
|
+
# @option options [String] :message_deduplication_id FIFO queue deduplication ID
|
|
57
|
+
# @option options [Hash] :message_attributes Custom SQS message attributes
|
|
58
|
+
# @return [String] The message ID of the enqueued job
|
|
59
|
+
#
|
|
60
|
+
# @example Basic job enqueueing
|
|
61
|
+
# MyWorker.perform_async({ user_id: 123, action: 'send_email' })
|
|
62
|
+
#
|
|
63
|
+
# @example FIFO queue with ordering
|
|
64
|
+
# MyWorker.perform_async(data, message_group_id: 'user_123')
|
|
9
65
|
def perform_async(body, options = {})
|
|
10
66
|
Shoryuken.worker_executor.perform_async(self, body, options)
|
|
11
67
|
end
|
|
12
68
|
|
|
69
|
+
# Enqueues a job to be processed after a specified time interval.
|
|
70
|
+
#
|
|
71
|
+
# @param interval [Integer, ActiveSupport::Duration] Delay in seconds, or duration object
|
|
72
|
+
# @param body [Object] The job payload that will be passed to the worker's perform method
|
|
73
|
+
# @param options [Hash] Additional options for job enqueueing (see {#perform_async})
|
|
74
|
+
# @return [String] The message ID of the enqueued job
|
|
75
|
+
#
|
|
76
|
+
# @example Delay job by 5 minutes
|
|
77
|
+
# MyWorker.perform_in(5.minutes, { user_id: 123 })
|
|
78
|
+
#
|
|
79
|
+
# @example Delay job by specific number of seconds
|
|
80
|
+
# MyWorker.perform_in(300, { user_id: 123 })
|
|
13
81
|
def perform_in(interval, body, options = {})
|
|
14
82
|
Shoryuken.worker_executor.perform_in(self, interval, body, options)
|
|
15
83
|
end
|
|
16
84
|
|
|
17
85
|
alias_method :perform_at, :perform_in
|
|
18
86
|
|
|
87
|
+
# Configures server-side middleware chain for this worker class.
|
|
88
|
+
# Middleware runs before and after job processing, similar to Rack middleware.
|
|
89
|
+
#
|
|
90
|
+
# @yield [Shoryuken::Middleware::Chain] The middleware chain for configuration
|
|
91
|
+
# @return [Shoryuken::Middleware::Chain] The configured middleware chain
|
|
92
|
+
#
|
|
93
|
+
# @example Adding custom middleware
|
|
94
|
+
# class MyWorker
|
|
95
|
+
# include Shoryuken::Worker
|
|
96
|
+
#
|
|
97
|
+
# server_middleware do |chain|
|
|
98
|
+
# chain.add MyCustomMiddleware
|
|
99
|
+
# chain.remove Shoryuken::Middleware::Server::ActiveRecord
|
|
100
|
+
# end
|
|
101
|
+
# end
|
|
19
102
|
def server_middleware
|
|
20
103
|
@_server_chain ||= Shoryuken.server_middleware.dup
|
|
21
104
|
yield @_server_chain if block_given?
|
|
22
105
|
@_server_chain
|
|
23
106
|
end
|
|
24
107
|
|
|
108
|
+
# Configures worker options including queue assignment, processing behavior,
|
|
109
|
+
# and SQS-specific settings. This is the main configuration method for workers.
|
|
110
|
+
#
|
|
111
|
+
# @param opts [Hash] Configuration options for the worker
|
|
112
|
+
# @option opts [String, Array<String>] :queue Queue name(s) this worker processes
|
|
113
|
+
# @option opts [Boolean] :batch (false) Process messages in batches of up to 10
|
|
114
|
+
# @option opts [Boolean] :auto_delete (false) Automatically delete messages after processing
|
|
115
|
+
# @option opts [Boolean] :auto_visibility_timeout (false) Automatically extend message visibility
|
|
116
|
+
# @option opts [Array<Integer>] :retry_intervals Exponential backoff retry intervals in seconds
|
|
117
|
+
# @option opts [Hash] :sqs Additional SQS client options
|
|
118
|
+
#
|
|
119
|
+
# @example Basic worker configuration
|
|
120
|
+
# class MyWorker
|
|
121
|
+
# include Shoryuken::Worker
|
|
122
|
+
# shoryuken_options queue: 'my_queue'
|
|
123
|
+
#
|
|
124
|
+
# def perform(sqs_msg, body)
|
|
125
|
+
# # Process the message
|
|
126
|
+
# end
|
|
127
|
+
# end
|
|
128
|
+
#
|
|
129
|
+
# @example Worker with auto-delete and retries
|
|
130
|
+
# class ReliableWorker
|
|
131
|
+
# include Shoryuken::Worker
|
|
132
|
+
# shoryuken_options queue: 'important_queue',
|
|
133
|
+
# auto_delete: true,
|
|
134
|
+
# retry_intervals: [1, 5, 25, 125]
|
|
135
|
+
# end
|
|
136
|
+
#
|
|
137
|
+
# @example Batch processing worker
|
|
138
|
+
# class BatchWorker
|
|
139
|
+
# include Shoryuken::Worker
|
|
140
|
+
# shoryuken_options queue: 'batch_queue', batch: true
|
|
141
|
+
#
|
|
142
|
+
# def perform(sqs_msgs, bodies)
|
|
143
|
+
# # Process array of up to 10 messages
|
|
144
|
+
# bodies.each { |body| process_item(body) }
|
|
145
|
+
# end
|
|
146
|
+
# end
|
|
147
|
+
#
|
|
148
|
+
# @example Multiple queues with priorities
|
|
149
|
+
# class MultiQueueWorker
|
|
150
|
+
# include Shoryuken::Worker
|
|
151
|
+
# shoryuken_options queue: ['high_priority', 'low_priority']
|
|
152
|
+
# end
|
|
153
|
+
#
|
|
154
|
+
# @example Auto-extending visibility timeout for long-running jobs
|
|
155
|
+
# class LongRunningWorker
|
|
156
|
+
# include Shoryuken::Worker
|
|
157
|
+
# shoryuken_options queue: 'slow_queue',
|
|
158
|
+
# auto_visibility_timeout: true
|
|
159
|
+
#
|
|
160
|
+
# def perform(sqs_msg, body)
|
|
161
|
+
# # Long processing that might exceed visibility timeout
|
|
162
|
+
# complex_processing(body)
|
|
163
|
+
# end
|
|
164
|
+
# end
|
|
25
165
|
def shoryuken_options(opts = {})
|
|
26
166
|
self.shoryuken_options_hash = get_shoryuken_options.merge(stringify_keys(opts || {}))
|
|
27
167
|
normalize_worker_queue!
|
|
28
168
|
end
|
|
29
169
|
|
|
170
|
+
# Checks if automatic visibility timeout extension is enabled for this worker.
|
|
171
|
+
# When enabled, Shoryuken automatically extends the message visibility timeout
|
|
172
|
+
# during processing to prevent the message from becoming visible to other consumers.
|
|
173
|
+
#
|
|
174
|
+
# @return [Boolean] true if auto visibility timeout is enabled
|
|
175
|
+
#
|
|
176
|
+
# @see #shoryuken_options Documentation for enabling auto_visibility_timeout
|
|
30
177
|
def auto_visibility_timeout?
|
|
31
178
|
!!get_shoryuken_options['auto_visibility_timeout']
|
|
32
179
|
end
|
|
33
180
|
|
|
181
|
+
# Checks if exponential backoff retry is configured for this worker.
|
|
182
|
+
# When retry intervals are specified, failed jobs will be retried with
|
|
183
|
+
# increasing delays between attempts.
|
|
184
|
+
#
|
|
185
|
+
# @return [Boolean] true if retry intervals are configured
|
|
186
|
+
#
|
|
187
|
+
# @example Configuring exponential backoff
|
|
188
|
+
# shoryuken_options retry_intervals: [1, 5, 25, 125, 625]
|
|
189
|
+
# # Will retry after 1s, 5s, 25s, 125s, then 625s before giving up
|
|
190
|
+
#
|
|
191
|
+
# @see #shoryuken_options Documentation for configuring retry_intervals
|
|
34
192
|
def exponential_backoff?
|
|
35
193
|
!!get_shoryuken_options['retry_intervals']
|
|
36
194
|
end
|
|
37
195
|
|
|
196
|
+
# Checks if automatic message deletion is enabled for this worker.
|
|
197
|
+
# When enabled, successfully processed messages are automatically deleted
|
|
198
|
+
# from the SQS queue. When disabled, you must manually delete messages
|
|
199
|
+
# or they will become visible again after the visibility timeout.
|
|
200
|
+
#
|
|
201
|
+
# @return [Boolean] true if auto delete is enabled
|
|
202
|
+
#
|
|
203
|
+
# @example Manual message deletion when auto_delete is false
|
|
204
|
+
# def perform(sqs_msg, body)
|
|
205
|
+
# process_message(body)
|
|
206
|
+
# # Manually delete the message after successful processing
|
|
207
|
+
# sqs_msg.delete
|
|
208
|
+
# end
|
|
209
|
+
#
|
|
210
|
+
# @see #shoryuken_options Documentation for enabling auto_delete
|
|
38
211
|
def auto_delete?
|
|
39
212
|
!!(get_shoryuken_options['delete'] || get_shoryuken_options['auto_delete'])
|
|
40
213
|
end
|
data/lib/shoryuken.rb
CHANGED
|
@@ -1,38 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'yaml'
|
|
2
4
|
require 'json'
|
|
3
5
|
require 'aws-sdk-sqs'
|
|
4
6
|
require 'time'
|
|
5
7
|
require 'concurrent'
|
|
6
8
|
require 'forwardable'
|
|
9
|
+
require 'zeitwerk'
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
require 'shoryuken/environment_loader'
|
|
13
|
-
require 'shoryuken/queue'
|
|
14
|
-
require 'shoryuken/message'
|
|
15
|
-
require 'shoryuken/client'
|
|
16
|
-
require 'shoryuken/worker'
|
|
17
|
-
require 'shoryuken/worker/default_executor'
|
|
18
|
-
require 'shoryuken/worker/inline_executor'
|
|
19
|
-
require 'shoryuken/worker_registry'
|
|
20
|
-
require 'shoryuken/default_worker_registry'
|
|
21
|
-
require 'shoryuken/default_exception_handler'
|
|
22
|
-
require 'shoryuken/middleware/chain'
|
|
23
|
-
require 'shoryuken/middleware/server/auto_delete'
|
|
24
|
-
Shoryuken::Middleware::Server.autoload :AutoExtendVisibility, 'shoryuken/middleware/server/auto_extend_visibility'
|
|
25
|
-
require 'shoryuken/middleware/server/exponential_backoff_retry'
|
|
26
|
-
require 'shoryuken/middleware/server/timing'
|
|
27
|
-
require 'shoryuken/polling/base'
|
|
28
|
-
require 'shoryuken/polling/weighted_round_robin'
|
|
29
|
-
require 'shoryuken/polling/strict_priority'
|
|
30
|
-
require 'shoryuken/manager'
|
|
31
|
-
require 'shoryuken/launcher'
|
|
32
|
-
require 'shoryuken/processor'
|
|
33
|
-
require 'shoryuken/body_parser'
|
|
34
|
-
require 'shoryuken/fetcher'
|
|
35
|
-
require 'shoryuken/options'
|
|
11
|
+
# Set up Zeitwerk loader
|
|
12
|
+
loader = Zeitwerk::Loader.for_gem
|
|
13
|
+
loader.ignore("#{__dir__}/shoryuken/extensions")
|
|
14
|
+
loader.setup
|
|
36
15
|
|
|
37
16
|
module Shoryuken
|
|
38
17
|
extend SingleForwardable
|
|
@@ -75,6 +54,7 @@ module Shoryuken
|
|
|
75
54
|
:exception_handlers=,
|
|
76
55
|
:options,
|
|
77
56
|
:logger,
|
|
57
|
+
:logger=,
|
|
78
58
|
:register_worker,
|
|
79
59
|
:configure_server,
|
|
80
60
|
:server?,
|
data/shoryuken.gemspec
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
lib = File.expand_path('lib', __dir__)
|
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
3
|
require 'shoryuken/version'
|
|
@@ -17,14 +16,15 @@ Gem::Specification.new do |spec|
|
|
|
17
16
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
17
|
spec.require_paths = ['lib']
|
|
19
18
|
|
|
19
|
+
spec.add_dependency 'aws-sdk-sqs', '>= 1.66.0'
|
|
20
|
+
spec.add_dependency 'concurrent-ruby'
|
|
21
|
+
spec.add_dependency 'thor'
|
|
22
|
+
spec.add_dependency 'zeitwerk', '~> 2.6'
|
|
23
|
+
|
|
20
24
|
spec.add_development_dependency 'dotenv'
|
|
21
25
|
spec.add_development_dependency 'ostruct'
|
|
22
26
|
spec.add_development_dependency 'rake'
|
|
23
27
|
spec.add_development_dependency 'rspec'
|
|
24
28
|
|
|
25
|
-
spec.
|
|
26
|
-
spec.add_dependency 'concurrent-ruby'
|
|
27
|
-
spec.add_dependency 'thor'
|
|
28
|
-
|
|
29
|
-
spec.required_ruby_version = '>= 3.0.0'
|
|
29
|
+
spec.required_ruby_version = '>= 3.1.0'
|
|
30
30
|
end
|