shoryuken 7.0.0.alpha2 → 7.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/.github/workflows/push.yml +2 -2
- data/.github/workflows/specs.yml +38 -43
- data/.github/workflows/verify-action-pins.yml +1 -1
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -1
- data/.yard-lint.yml +279 -0
- data/CHANGELOG.md +69 -1
- data/Gemfile +1 -1
- data/README.md +2 -7
- data/Rakefile +4 -10
- data/bin/clean_localstack +52 -0
- data/bin/cli/base.rb +21 -0
- data/bin/cli/sqs.rb +61 -2
- data/bin/integrations +275 -0
- data/bin/scenario +154 -0
- data/bin/shoryuken +1 -1
- data/lib/{shoryuken/extensions/active_job_extensions.rb → active_job/extensions.rb} +15 -4
- data/lib/active_job/queue_adapters/shoryuken_adapter.rb +208 -0
- data/lib/active_job/queue_adapters/shoryuken_concurrent_send_adapter.rb +78 -0
- data/lib/shoryuken/active_job/current_attributes.rb +139 -0
- data/lib/shoryuken/active_job/job_wrapper.rb +28 -0
- data/lib/shoryuken/body_parser.rb +8 -0
- data/lib/shoryuken/client.rb +14 -0
- data/lib/shoryuken/default_exception_handler.rb +9 -0
- data/lib/shoryuken/default_worker_registry.rb +29 -1
- data/lib/shoryuken/environment_loader.rb +78 -8
- data/lib/shoryuken/errors.rb +33 -0
- data/lib/shoryuken/fetcher.rb +37 -1
- data/lib/shoryuken/helpers/atomic_boolean.rb +19 -5
- data/lib/shoryuken/helpers/timer_task.rb +80 -0
- data/lib/shoryuken/launcher.rb +53 -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 +39 -25
- data/lib/shoryuken/manager.rb +70 -1
- data/lib/shoryuken/message.rb +114 -1
- data/lib/shoryuken/middleware/chain.rb +139 -43
- data/lib/shoryuken/middleware/entry.rb +30 -0
- data/lib/shoryuken/middleware/server/active_record.rb +8 -0
- data/lib/shoryuken/middleware/server/auto_delete.rb +10 -0
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +27 -1
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +29 -0
- data/lib/shoryuken/middleware/server/timing.rb +11 -0
- data/lib/shoryuken/options.rb +129 -6
- data/lib/shoryuken/polling/base_strategy.rb +1 -0
- data/lib/shoryuken/polling/strict_priority.rb +39 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +42 -0
- data/lib/shoryuken/processor.rb +32 -1
- data/lib/shoryuken/queue.rb +93 -4
- data/lib/shoryuken/runner.rb +45 -4
- data/lib/shoryuken/util.rb +26 -1
- data/lib/shoryuken/version.rb +2 -1
- data/lib/shoryuken/worker/default_executor.rb +21 -1
- data/lib/shoryuken/worker/inline_executor.rb +24 -0
- data/lib/shoryuken/worker.rb +193 -0
- data/lib/shoryuken/worker_registry.rb +33 -0
- data/lib/shoryuken.rb +18 -6
- data/renovate.json +29 -2
- data/shoryuken.gemspec +2 -1
- data/spec/integration/.rspec +1 -0
- data/spec/integration/active_job/adapter_configuration/configuration_spec.rb +26 -0
- data/spec/integration/active_job/bulk_enqueue/bulk_enqueue_spec.rb +53 -0
- data/spec/integration/active_job/current_attributes/bulk_enqueue_spec.rb +50 -0
- data/spec/integration/active_job/current_attributes/complex_types_spec.rb +55 -0
- data/spec/integration/active_job/current_attributes/empty_context_spec.rb +41 -0
- data/spec/integration/active_job/current_attributes/full_context_spec.rb +63 -0
- data/spec/integration/active_job/current_attributes/partial_context_spec.rb +57 -0
- data/spec/integration/active_job/custom_attributes/number_attributes_spec.rb +37 -0
- data/spec/integration/active_job/custom_attributes/string_attributes_spec.rb +39 -0
- data/spec/integration/active_job/error_handling/job_wrapper_spec.rb +53 -0
- data/spec/integration/active_job/fifo_and_attributes/deduplication_spec.rb +86 -0
- data/spec/integration/active_job/retry/discard_on_spec.rb +43 -0
- data/spec/integration/active_job/retry/retry_on_spec.rb +36 -0
- data/spec/integration/active_job/roundtrip/roundtrip_spec.rb +52 -0
- data/spec/integration/active_job/scheduled/scheduled_spec.rb +76 -0
- data/spec/integration/active_record_middleware/active_record_middleware_spec.rb +84 -0
- data/spec/integration/auto_delete/auto_delete_spec.rb +53 -0
- data/spec/integration/auto_extend_visibility/auto_extend_visibility_spec.rb +57 -0
- data/spec/integration/aws_config/aws_config_spec.rb +59 -0
- data/spec/integration/batch_processing/batch_processing_spec.rb +37 -0
- data/spec/integration/body_parser/json_parser_spec.rb +45 -0
- data/spec/integration/body_parser/proc_parser_spec.rb +54 -0
- data/spec/integration/body_parser/text_parser_spec.rb +43 -0
- data/spec/integration/concurrent_processing/concurrent_processing_spec.rb +45 -0
- data/spec/integration/dead_letter_queue/dead_letter_queue_spec.rb +91 -0
- data/spec/integration/exception_handlers/exception_handlers_spec.rb +69 -0
- data/spec/integration/exponential_backoff/exponential_backoff_spec.rb +67 -0
- data/spec/integration/fifo_ordering/fifo_ordering_spec.rb +44 -0
- data/spec/integration/large_payloads/large_payloads_spec.rb +30 -0
- data/spec/integration/launcher/launcher_spec.rb +40 -0
- data/spec/integration/message_attributes/message_attributes_spec.rb +54 -0
- data/spec/integration/message_operations/message_operations_spec.rb +59 -0
- data/spec/integration/middleware_chain/empty_chain_spec.rb +11 -0
- data/spec/integration/middleware_chain/execution_order_spec.rb +33 -0
- data/spec/integration/middleware_chain/removal_spec.rb +31 -0
- data/spec/integration/middleware_chain/short_circuit_spec.rb +40 -0
- data/spec/integration/polling_strategies/polling_strategies_spec.rb +46 -0
- data/spec/integration/queue_operations/queue_operations_spec.rb +84 -0
- data/spec/integration/rails/rails_72/Gemfile +6 -0
- data/spec/integration/rails/rails_72/activejob_adapter_spec.rb +98 -0
- data/spec/integration/rails/rails_80/Gemfile +6 -0
- data/spec/integration/rails/rails_80/activejob_adapter_spec.rb +98 -0
- data/spec/integration/rails/rails_80/continuation_spec.rb +79 -0
- data/spec/integration/rails/rails_81/Gemfile +6 -0
- data/spec/integration/rails/rails_81/activejob_adapter_spec.rb +98 -0
- data/spec/integration/rails/rails_81/continuation_spec.rb +79 -0
- data/spec/integration/retry_behavior/retry_behavior_spec.rb +45 -0
- data/spec/integration/spec_helper.rb +7 -0
- data/spec/integration/strict_priority_polling/strict_priority_polling_spec.rb +58 -0
- data/spec/integration/visibility_timeout/visibility_timeout_spec.rb +37 -0
- data/spec/integration/worker_enqueueing/worker_enqueueing_spec.rb +60 -0
- data/spec/integration/worker_groups/worker_groups_spec.rb +79 -0
- data/spec/integration/worker_lifecycle/worker_lifecycle_spec.rb +33 -0
- data/spec/integrations_helper.rb +243 -0
- data/spec/lib/active_job/extensions_spec.rb +149 -0
- data/spec/lib/active_job/queue_adapters/shoryuken_adapter_spec.rb +29 -0
- data/spec/{shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb → lib/active_job/queue_adapters/shoryuken_concurrent_send_adapter_spec.rb} +3 -3
- data/spec/{shoryuken/extensions/active_job_wrapper_spec.rb → lib/shoryuken/active_job/job_wrapper_spec.rb} +4 -4
- data/spec/{shoryuken → lib/shoryuken}/environment_loader_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/helpers/hash_utils_spec.rb +14 -14
- data/spec/{shoryuken → lib/shoryuken}/helpers/string_utils_spec.rb +3 -3
- data/spec/lib/shoryuken/helpers/timer_task_spec.rb +298 -0
- data/spec/{shoryuken → lib/shoryuken}/helpers_integration_spec.rb +9 -9
- data/spec/{shoryuken → lib/shoryuken}/launcher_spec.rb +22 -0
- data/spec/lib/shoryuken/logging_spec.rb +242 -0
- data/spec/lib/shoryuken/message_spec.rb +109 -0
- data/spec/lib/shoryuken/middleware/entry_spec.rb +68 -0
- data/spec/lib/shoryuken/middleware/server/active_record_spec.rb +133 -0
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/auto_extend_visibility_spec.rb +50 -0
- data/spec/{shoryuken → lib/shoryuken}/options_spec.rb +2 -2
- data/spec/{shoryuken → lib/shoryuken}/util_spec.rb +1 -1
- data/spec/lib/shoryuken/version_spec.rb +17 -0
- data/spec/lib/shoryuken/worker_registry_spec.rb +63 -0
- data/spec/shared_examples_for_active_job.rb +29 -9
- data/spec/spec_helper.rb +34 -3
- metadata +230 -91
- data/.devcontainer/Dockerfile +0 -17
- data/.devcontainer/base.Dockerfile +0 -43
- data/.devcontainer/devcontainer.json +0 -35
- data/Appraisals +0 -23
- data/gemfiles/.gitignore +0 -1
- data/gemfiles/rails_7_0.gemfile +0 -19
- data/gemfiles/rails_7_1.gemfile +0 -19
- data/gemfiles/rails_7_2.gemfile +0 -19
- data/gemfiles/rails_8_0.gemfile +0 -19
- data/lib/shoryuken/extensions/active_job_adapter.rb +0 -110
- data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +0 -50
- data/spec/integration/launcher_spec.rb +0 -127
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +0 -8
- data/spec/shoryuken/extensions/active_job_base_spec.rb +0 -85
- /data/spec/{shoryuken → lib/shoryuken}/body_parser_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/client_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/default_exception_handler_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/default_worker_registry_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/fetcher_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/helpers/atomic_boolean_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/helpers/atomic_counter_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/helpers/atomic_hash_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/inline_message_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/manager_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/chain_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/server/auto_delete_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/server/exponential_backoff_retry_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/middleware/server/timing_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/base_strategy_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/queue_configuration_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/strict_priority_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/polling/weighted_round_robin_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/processor_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/queue_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/runner_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/worker/default_executor_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/worker/inline_executor_spec.rb +0 -0
- /data/spec/{shoryuken → lib/shoryuken}/worker_spec.rb +0 -0
- /data/spec/{shoryuken_spec.rb → lib/shoryuken_spec.rb} +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shoryuken
|
|
4
|
+
module Middleware
|
|
5
|
+
# Represents an entry in a middleware chain, storing the middleware class
|
|
6
|
+
# and any arguments needed for its instantiation.
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class Entry
|
|
10
|
+
# @return [Class] The middleware class this entry represents
|
|
11
|
+
attr_reader :klass
|
|
12
|
+
|
|
13
|
+
# Creates a new middleware entry.
|
|
14
|
+
#
|
|
15
|
+
# @param klass [Class] The middleware class
|
|
16
|
+
# @param args [Array] Arguments to pass to the middleware constructor
|
|
17
|
+
def initialize(klass, *args)
|
|
18
|
+
@klass = klass
|
|
19
|
+
@args = args
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Creates a new instance of the middleware class with the stored arguments.
|
|
23
|
+
#
|
|
24
|
+
# @return [Object] A new instance of the middleware class
|
|
25
|
+
def make_new
|
|
26
|
+
@klass.new(*@args)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -2,8 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Shoryuken
|
|
4
4
|
module Middleware
|
|
5
|
+
# Server-side middleware that runs around message processing
|
|
5
6
|
module Server
|
|
7
|
+
# Middleware that clears ActiveRecord connections after message processing.
|
|
8
|
+
# Ensures database connections are returned to the pool after each job.
|
|
6
9
|
class ActiveRecord
|
|
10
|
+
# Processes a message and clears database connections afterwards
|
|
11
|
+
#
|
|
12
|
+
# @param _args [Array<Object>] middleware call arguments (unused)
|
|
13
|
+
# @yield continues to the next middleware in the chain
|
|
14
|
+
# @return [Object] return value from the next middleware or worker in the chain
|
|
7
15
|
def call(*_args)
|
|
8
16
|
yield
|
|
9
17
|
ensure
|
|
@@ -3,7 +3,17 @@
|
|
|
3
3
|
module Shoryuken
|
|
4
4
|
module Middleware
|
|
5
5
|
module Server
|
|
6
|
+
# Middleware that automatically deletes messages after successful processing.
|
|
7
|
+
# Only deletes messages when the worker has auto_delete enabled.
|
|
6
8
|
class AutoDelete
|
|
9
|
+
# Processes a message and deletes it if auto_delete is enabled
|
|
10
|
+
#
|
|
11
|
+
# @param worker [Object] the worker instance
|
|
12
|
+
# @param queue [String] the queue name
|
|
13
|
+
# @param sqs_msg [Shoryuken::Message, Array<Shoryuken::Message>] the message or batch
|
|
14
|
+
# @param _body [Object] the parsed message body (unused)
|
|
15
|
+
# @yield continues to the next middleware in the chain
|
|
16
|
+
# @return [void]
|
|
7
17
|
def call(worker, queue, sqs_msg, _body)
|
|
8
18
|
yield
|
|
9
19
|
|
|
@@ -3,11 +3,22 @@
|
|
|
3
3
|
module Shoryuken
|
|
4
4
|
module Middleware
|
|
5
5
|
module Server
|
|
6
|
+
# Middleware that automatically extends message visibility timeout during processing.
|
|
7
|
+
# Prevents messages from becoming visible to other consumers while still being processed.
|
|
6
8
|
class AutoExtendVisibility
|
|
7
9
|
include Util
|
|
8
10
|
|
|
11
|
+
# Number of seconds before timeout to extend visibility
|
|
9
12
|
EXTEND_UPFRONT_SECONDS = 5
|
|
10
13
|
|
|
14
|
+
# Processes a message with automatic visibility timeout extension
|
|
15
|
+
#
|
|
16
|
+
# @param worker [Object] the worker instance
|
|
17
|
+
# @param queue [String] the queue name
|
|
18
|
+
# @param sqs_msg [Shoryuken::Message, Array<Shoryuken::Message>] the message or batch
|
|
19
|
+
# @param body [Object] the parsed message body
|
|
20
|
+
# @yield continues to the next middleware in the chain
|
|
21
|
+
# @return [void]
|
|
11
22
|
def call(worker, queue, sqs_msg, body)
|
|
12
23
|
return yield unless worker.class.auto_visibility_timeout?
|
|
13
24
|
|
|
@@ -24,13 +35,21 @@ module Shoryuken
|
|
|
24
35
|
|
|
25
36
|
private
|
|
26
37
|
|
|
38
|
+
# Helper class for extending message visibility
|
|
27
39
|
class MessageVisibilityExtender
|
|
28
40
|
include Util
|
|
29
41
|
|
|
42
|
+
# Creates a timer task that extends message visibility
|
|
43
|
+
#
|
|
44
|
+
# @param _worker [Object] the worker instance (unused)
|
|
45
|
+
# @param queue [String] the queue name
|
|
46
|
+
# @param sqs_msg [Shoryuken::Message] the message
|
|
47
|
+
# @param _body [Object] the parsed message body (unused)
|
|
48
|
+
# @return [Shoryuken::Helpers::TimerTask] the timer task
|
|
30
49
|
def auto_extend(_worker, queue, sqs_msg, _body)
|
|
31
50
|
queue_visibility_timeout = Shoryuken::Client.queues(queue).visibility_timeout
|
|
32
51
|
|
|
33
|
-
|
|
52
|
+
Shoryuken::Helpers::TimerTask.new(execution_interval: queue_visibility_timeout - EXTEND_UPFRONT_SECONDS) do
|
|
34
53
|
logger.debug do
|
|
35
54
|
"Extending message #{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s"
|
|
36
55
|
end
|
|
@@ -44,6 +63,13 @@ module Shoryuken
|
|
|
44
63
|
end
|
|
45
64
|
end
|
|
46
65
|
|
|
66
|
+
# Creates and starts a visibility extension timer
|
|
67
|
+
#
|
|
68
|
+
# @param worker [Object] the worker instance
|
|
69
|
+
# @param queue [String] the queue name
|
|
70
|
+
# @param sqs_msg [Shoryuken::Message] the message
|
|
71
|
+
# @param body [Object] the parsed message body
|
|
72
|
+
# @return [Shoryuken::Helpers::TimerTask] the started timer
|
|
47
73
|
def auto_visibility_timer(worker, queue, sqs_msg, body)
|
|
48
74
|
MessageVisibilityExtender.new.auto_extend(worker, queue, sqs_msg, body).tap(&:execute)
|
|
49
75
|
end
|
|
@@ -3,9 +3,22 @@
|
|
|
3
3
|
module Shoryuken
|
|
4
4
|
module Middleware
|
|
5
5
|
module Server
|
|
6
|
+
# Middleware that implements exponential backoff retry for failed messages.
|
|
7
|
+
# When a job fails, the message visibility timeout is adjusted based on
|
|
8
|
+
# configured retry intervals.
|
|
6
9
|
class ExponentialBackoffRetry
|
|
7
10
|
include Util
|
|
8
11
|
|
|
12
|
+
# Processes a message with exponential backoff retry on failure
|
|
13
|
+
#
|
|
14
|
+
# @param worker [Object] the worker instance
|
|
15
|
+
# @param _queue [String] the queue name (unused)
|
|
16
|
+
# @param sqs_msg [Shoryuken::Message, Array<Shoryuken::Message>] the message or batch
|
|
17
|
+
# @param _body [Object] the parsed message body (unused)
|
|
18
|
+
# @yield continues to the next middleware in the chain
|
|
19
|
+
# @return [void]
|
|
20
|
+
# @raise [StandardError] re-raises the original exception if retry intervals are not configured
|
|
21
|
+
# or if retry limit is exceeded
|
|
9
22
|
def call(worker, _queue, sqs_msg, _body)
|
|
10
23
|
return yield unless worker.class.exponential_backoff?
|
|
11
24
|
|
|
@@ -32,6 +45,11 @@ module Shoryuken
|
|
|
32
45
|
|
|
33
46
|
private
|
|
34
47
|
|
|
48
|
+
# Gets the retry interval for a given attempt number
|
|
49
|
+
#
|
|
50
|
+
# @param retry_intervals [Array<Integer>, #call] the configured intervals or callable
|
|
51
|
+
# @param attempts [Integer] the current attempt number
|
|
52
|
+
# @return [Integer, nil] the interval in seconds or nil
|
|
35
53
|
def get_interval(retry_intervals, attempts)
|
|
36
54
|
return retry_intervals.call(attempts) if retry_intervals.respond_to?(:call)
|
|
37
55
|
|
|
@@ -42,12 +60,23 @@ module Shoryuken
|
|
|
42
60
|
end
|
|
43
61
|
end
|
|
44
62
|
|
|
63
|
+
# Calculates the next visibility timeout capped at SQS maximum
|
|
64
|
+
#
|
|
65
|
+
# @param interval [Integer] the desired interval
|
|
66
|
+
# @param started_at [Time] when processing started
|
|
67
|
+
# @return [Integer] the capped visibility timeout
|
|
45
68
|
def next_visibility_timeout(interval, started_at)
|
|
46
69
|
max_timeout = 43_200 - (Time.now - started_at).ceil - 1
|
|
47
70
|
interval = max_timeout if interval > max_timeout
|
|
48
71
|
interval.to_i
|
|
49
72
|
end
|
|
50
73
|
|
|
74
|
+
# Handles a message failure by adjusting visibility timeout
|
|
75
|
+
#
|
|
76
|
+
# @param sqs_msg [Shoryuken::Message] the failed message
|
|
77
|
+
# @param started_at [Time] when processing started
|
|
78
|
+
# @param retry_intervals [Array<Integer>, #call] the configured intervals
|
|
79
|
+
# @return [Boolean] true if retry was scheduled, false otherwise
|
|
51
80
|
def handle_failure(sqs_msg, started_at, retry_intervals)
|
|
52
81
|
receive_count = sqs_msg.attributes['ApproximateReceiveCount'].to_i
|
|
53
82
|
|
|
@@ -3,9 +3,20 @@
|
|
|
3
3
|
module Shoryuken
|
|
4
4
|
module Middleware
|
|
5
5
|
module Server
|
|
6
|
+
# Middleware that logs timing information for message processing.
|
|
7
|
+
# Records start time, completion time, and warns if processing
|
|
8
|
+
# exceeds the queue's visibility timeout.
|
|
6
9
|
class Timing
|
|
7
10
|
include Util
|
|
8
11
|
|
|
12
|
+
# Processes a message while logging timing information
|
|
13
|
+
#
|
|
14
|
+
# @param _worker [Object] the worker instance (unused)
|
|
15
|
+
# @param queue [String] the queue name
|
|
16
|
+
# @param _sqs_msg [Shoryuken::Message] the message being processed (unused)
|
|
17
|
+
# @param _body [Object] the parsed message body (unused)
|
|
18
|
+
# @yield continues to the next middleware in the chain
|
|
19
|
+
# @return [void]
|
|
9
20
|
def call(_worker, queue, _sqs_msg, _body)
|
|
10
21
|
started_at = Time.now
|
|
11
22
|
|
data/lib/shoryuken/options.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Shoryuken
|
|
4
|
+
# Stores and manages all Shoryuken configuration options.
|
|
5
|
+
# This class is used internally to hold settings for workers, queues,
|
|
6
|
+
# middleware, and other runtime configurations.
|
|
4
7
|
class Options
|
|
8
|
+
# Default configuration values for Shoryuken
|
|
5
9
|
DEFAULTS = {
|
|
6
10
|
thread_priority: -1,
|
|
7
11
|
concurrency: 25,
|
|
@@ -19,14 +23,31 @@ module Shoryuken
|
|
|
19
23
|
}
|
|
20
24
|
}.freeze
|
|
21
25
|
|
|
26
|
+
# @return [Boolean] whether to enable ActiveJob queue name prefixing
|
|
27
|
+
# @return [Boolean] whether to cache SQS visibility timeout
|
|
28
|
+
# @return [Hash{String => Hash}] the configured processing groups
|
|
29
|
+
# @return [Object] the executor used to launch workers
|
|
30
|
+
# @return [Proc] the code reloader proc for development environments
|
|
31
|
+
# @return [Boolean] whether code reloading is enabled
|
|
32
|
+
# @return [Proc, nil] callback to execute when server starts
|
|
33
|
+
# @return [Proc, nil] callback to execute when server stops
|
|
34
|
+
# @return [Class] the executor class for running workers
|
|
35
|
+
# @return [Shoryuken::WorkerRegistry] the registry for worker classes
|
|
36
|
+
# @return [Array<#call>] handlers for processing exceptions
|
|
22
37
|
attr_accessor :active_job_queue_name_prefixing, :cache_visibility_timeout,
|
|
23
38
|
:groups, :launcher_executor, :reloader, :enable_reloading,
|
|
24
39
|
:start_callback, :stop_callback, :worker_executor, :worker_registry,
|
|
25
40
|
:exception_handlers
|
|
26
41
|
|
|
42
|
+
# @return [Hash] the default options for workers
|
|
43
|
+
# @return [Aws::SQS::Client] the SQS client instance
|
|
44
|
+
# @return [Logger] the logger instance
|
|
27
45
|
attr_writer :default_worker_options, :sqs_client, :logger
|
|
46
|
+
|
|
47
|
+
# @return [Hash] options passed to SQS receive_message calls
|
|
28
48
|
attr_reader :sqs_client_receive_message_opts
|
|
29
49
|
|
|
50
|
+
# Initializes a new Options instance with default values
|
|
30
51
|
def initialize
|
|
31
52
|
self.groups = {}
|
|
32
53
|
self.worker_registry = DefaultWorkerRegistry.new
|
|
@@ -40,10 +61,19 @@ module Shoryuken
|
|
|
40
61
|
@sqs_client_receive_message_opts ||= {}
|
|
41
62
|
end
|
|
42
63
|
|
|
64
|
+
# Checks if ActiveJob is available
|
|
65
|
+
#
|
|
66
|
+
# @return [Boolean] true if ActiveJob is defined
|
|
43
67
|
def active_job?
|
|
44
68
|
defined?(::ActiveJob)
|
|
45
69
|
end
|
|
46
70
|
|
|
71
|
+
# Adds a processing group with the specified concurrency and delay
|
|
72
|
+
#
|
|
73
|
+
# @param group [String] the name of the group
|
|
74
|
+
# @param concurrency [Integer, nil] the number of concurrent workers for the group
|
|
75
|
+
# @param delay [Float, nil] the delay between polling cycles
|
|
76
|
+
# @return [Hash] the group configuration
|
|
47
77
|
def add_group(group, concurrency = nil, delay: nil)
|
|
48
78
|
concurrency ||= options[:concurrency]
|
|
49
79
|
delay ||= options[:delay]
|
|
@@ -55,16 +85,29 @@ module Shoryuken
|
|
|
55
85
|
}
|
|
56
86
|
end
|
|
57
87
|
|
|
88
|
+
# Adds a queue to a processing group with the specified weight
|
|
89
|
+
#
|
|
90
|
+
# @param queue [String] the name of the queue
|
|
91
|
+
# @param weight [Integer] the weight (priority) of the queue
|
|
92
|
+
# @param group [String] the name of the group to add the queue to
|
|
93
|
+
# @return [void]
|
|
58
94
|
def add_queue(queue, weight, group)
|
|
59
95
|
weight.times do
|
|
60
96
|
groups[group][:queues] << queue
|
|
61
97
|
end
|
|
62
98
|
end
|
|
63
99
|
|
|
100
|
+
# Returns all queues from all groups
|
|
101
|
+
#
|
|
102
|
+
# @return [Array<String>] flat array of all queue names
|
|
64
103
|
def ungrouped_queues
|
|
65
104
|
groups.values.flat_map { |options| options[:queues] }
|
|
66
105
|
end
|
|
67
106
|
|
|
107
|
+
# Returns the polling strategy class for a group
|
|
108
|
+
#
|
|
109
|
+
# @param group [String] the name of the group
|
|
110
|
+
# @return [Class] the polling strategy class to use
|
|
68
111
|
def polling_strategy(group)
|
|
69
112
|
strategy = (group == 'default' ? options : options[:groups].to_h[group]).to_h[:polling_strategy]
|
|
70
113
|
case strategy
|
|
@@ -76,61 +119,111 @@ module Shoryuken
|
|
|
76
119
|
begin
|
|
77
120
|
Object.const_get(strategy)
|
|
78
121
|
rescue NameError
|
|
79
|
-
raise
|
|
122
|
+
raise Errors::InvalidPollingStrategyError, "#{strategy} is not a valid polling_strategy"
|
|
80
123
|
end
|
|
81
124
|
when Class
|
|
82
125
|
strategy
|
|
83
126
|
end
|
|
84
127
|
end
|
|
85
128
|
|
|
129
|
+
# Returns the polling delay for a group
|
|
130
|
+
#
|
|
131
|
+
# @param group [String] the name of the group
|
|
132
|
+
# @return [Float] the delay in seconds
|
|
86
133
|
def delay(group)
|
|
87
134
|
groups[group].to_h.fetch(:delay, options[:delay]).to_f
|
|
88
135
|
end
|
|
89
136
|
|
|
137
|
+
# Returns the SQS client, initializing a default one if needed.
|
|
138
|
+
# Uses AWS configuration from options[:aws] if available.
|
|
139
|
+
#
|
|
140
|
+
# @return [Aws::SQS::Client] the SQS client
|
|
90
141
|
def sqs_client
|
|
91
|
-
@sqs_client ||= Aws::SQS::Client.new
|
|
142
|
+
@sqs_client ||= Aws::SQS::Client.new(options[:aws])
|
|
92
143
|
end
|
|
93
144
|
|
|
145
|
+
# Sets the SQS client receive message options for the default group
|
|
146
|
+
#
|
|
147
|
+
# @param sqs_client_receive_message_opts [Hash] the options hash
|
|
148
|
+
# @return [Hash] the options hash
|
|
94
149
|
def sqs_client_receive_message_opts=(sqs_client_receive_message_opts)
|
|
95
150
|
@sqs_client_receive_message_opts['default'] = sqs_client_receive_message_opts
|
|
96
151
|
end
|
|
97
152
|
|
|
153
|
+
# Returns the global options hash
|
|
154
|
+
#
|
|
155
|
+
# @return [Hash] the options hash
|
|
98
156
|
def options
|
|
99
157
|
@options ||= DEFAULTS.dup
|
|
100
158
|
end
|
|
101
159
|
|
|
160
|
+
# Returns the logger instance
|
|
161
|
+
#
|
|
162
|
+
# @return [Logger] the logger
|
|
102
163
|
def logger
|
|
103
164
|
@logger ||= Shoryuken::Logging.logger
|
|
104
165
|
end
|
|
105
166
|
|
|
167
|
+
# Returns the thread priority setting
|
|
168
|
+
#
|
|
169
|
+
# @return [Integer] the thread priority
|
|
106
170
|
def thread_priority
|
|
107
171
|
@thread_priority ||= options[:thread_priority]
|
|
108
172
|
end
|
|
109
173
|
|
|
174
|
+
# Sets the thread priority
|
|
175
|
+
#
|
|
176
|
+
# @param value [Integer] the thread priority value
|
|
177
|
+
# @return [Integer] the thread priority
|
|
178
|
+
attr_writer :thread_priority
|
|
179
|
+
|
|
180
|
+
# Registers a worker class with the worker registry
|
|
181
|
+
#
|
|
182
|
+
# @param args [Array] arguments to pass to the registry
|
|
183
|
+
# @return [void]
|
|
110
184
|
def register_worker(*args)
|
|
111
185
|
worker_registry.register_worker(*args)
|
|
112
186
|
end
|
|
113
187
|
|
|
188
|
+
# Yields self if running as a server for server-specific configuration
|
|
189
|
+
#
|
|
190
|
+
# @yield [Shoryuken::Options] the options instance
|
|
191
|
+
# @return [void]
|
|
114
192
|
def configure_server
|
|
115
193
|
yield self if server?
|
|
116
194
|
end
|
|
117
195
|
|
|
196
|
+
# Returns the server middleware chain
|
|
197
|
+
#
|
|
198
|
+
# @yield [Shoryuken::Middleware::Chain] the middleware chain for configuration
|
|
199
|
+
# @return [Shoryuken::Middleware::Chain] the server middleware chain
|
|
118
200
|
def server_middleware
|
|
119
201
|
@_server_chain ||= default_server_middleware
|
|
120
202
|
yield @_server_chain if block_given?
|
|
121
203
|
@_server_chain
|
|
122
204
|
end
|
|
123
205
|
|
|
206
|
+
# Yields self unless running as a server for client-specific configuration
|
|
207
|
+
#
|
|
208
|
+
# @yield [Shoryuken::Options] the options instance
|
|
209
|
+
# @return [void]
|
|
124
210
|
def configure_client
|
|
125
211
|
yield self unless server?
|
|
126
212
|
end
|
|
127
213
|
|
|
214
|
+
# Returns the client middleware chain
|
|
215
|
+
#
|
|
216
|
+
# @yield [Shoryuken::Middleware::Chain] the middleware chain for configuration
|
|
217
|
+
# @return [Shoryuken::Middleware::Chain] the client middleware chain
|
|
128
218
|
def client_middleware
|
|
129
219
|
@_client_chain ||= default_client_middleware
|
|
130
220
|
yield @_client_chain if block_given?
|
|
131
221
|
@_client_chain
|
|
132
222
|
end
|
|
133
223
|
|
|
224
|
+
# Returns the default worker options hash
|
|
225
|
+
#
|
|
226
|
+
# @return [Hash{String => Object}] the default worker options
|
|
134
227
|
def default_worker_options
|
|
135
228
|
@default_worker_options ||= {
|
|
136
229
|
'queue' => 'default',
|
|
@@ -142,43 +235,70 @@ module Shoryuken
|
|
|
142
235
|
}
|
|
143
236
|
end
|
|
144
237
|
|
|
238
|
+
# Registers a callback to run when the server starts
|
|
239
|
+
#
|
|
240
|
+
# @param block [Proc] the block to execute on start
|
|
241
|
+
# @return [void]
|
|
242
|
+
# @yield the block to execute on start
|
|
145
243
|
def on_start(&block)
|
|
146
244
|
self.start_callback = block
|
|
147
245
|
end
|
|
148
246
|
|
|
247
|
+
# Registers a callback to run when the server stops
|
|
248
|
+
#
|
|
249
|
+
# @param block [Proc] the block to execute on stop
|
|
250
|
+
# @return [void]
|
|
251
|
+
# @yield the block to execute on stop
|
|
149
252
|
def on_stop(&block)
|
|
150
253
|
self.stop_callback = block
|
|
151
254
|
end
|
|
152
255
|
|
|
153
|
-
#
|
|
154
|
-
# :startup, :quiet, :shutdown or :stopped are valid events.
|
|
256
|
+
# Registers a block to run at a point in the Shoryuken lifecycle.
|
|
155
257
|
#
|
|
258
|
+
# @param event [Symbol] the lifecycle event (:startup, :quiet, :shutdown, or :stopped)
|
|
259
|
+
# @param block [Proc] the block to execute for the event
|
|
260
|
+
# @return [void]
|
|
261
|
+
# @raise [ArgumentError] if event is not a Symbol or not a valid event name
|
|
262
|
+
# @yield the block to execute for the event
|
|
263
|
+
# @example
|
|
156
264
|
# Shoryuken.configure_server do |config|
|
|
157
265
|
# config.on(:shutdown) do
|
|
158
266
|
# puts "Goodbye cruel world!"
|
|
159
267
|
# end
|
|
160
268
|
# end
|
|
161
269
|
def on(event, &block)
|
|
162
|
-
|
|
163
|
-
|
|
270
|
+
raise Errors::InvalidEventError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
|
|
271
|
+
raise Errors::InvalidEventError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
|
|
164
272
|
|
|
165
273
|
options[:lifecycle_events][event] << block
|
|
166
274
|
end
|
|
167
275
|
|
|
276
|
+
# Checks if running as a server (CLI mode)
|
|
277
|
+
#
|
|
278
|
+
# @return [Boolean] true if Shoryuken::CLI is defined
|
|
168
279
|
def server?
|
|
169
280
|
defined?(Shoryuken::CLI)
|
|
170
281
|
end
|
|
171
282
|
|
|
283
|
+
# Checks if visibility timeout caching is enabled
|
|
284
|
+
#
|
|
285
|
+
# @return [Boolean] true if caching is enabled
|
|
172
286
|
def cache_visibility_timeout?
|
|
173
287
|
@cache_visibility_timeout
|
|
174
288
|
end
|
|
175
289
|
|
|
290
|
+
# Checks if ActiveJob queue name prefixing is enabled
|
|
291
|
+
#
|
|
292
|
+
# @return [Boolean] true if prefixing is enabled
|
|
176
293
|
def active_job_queue_name_prefixing?
|
|
177
294
|
@active_job_queue_name_prefixing
|
|
178
295
|
end
|
|
179
296
|
|
|
180
297
|
private
|
|
181
298
|
|
|
299
|
+
# Creates the default server middleware chain
|
|
300
|
+
#
|
|
301
|
+
# @return [Shoryuken::Middleware::Chain] the default middleware chain
|
|
182
302
|
def default_server_middleware
|
|
183
303
|
Middleware::Chain.new do |m|
|
|
184
304
|
m.add Middleware::Server::Timing
|
|
@@ -192,6 +312,9 @@ module Shoryuken
|
|
|
192
312
|
end
|
|
193
313
|
end
|
|
194
314
|
|
|
315
|
+
# Creates the default client middleware chain
|
|
316
|
+
#
|
|
317
|
+
# @return [Shoryuken::Middleware::Chain] an empty middleware chain
|
|
195
318
|
def default_client_middleware
|
|
196
319
|
Middleware::Chain.new
|
|
197
320
|
end
|
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module Shoryuken
|
|
4
4
|
module Polling
|
|
5
|
+
# A polling strategy that processes queues in strict priority order.
|
|
6
|
+
# Higher priority queues are always processed before lower priority queues.
|
|
7
|
+
# Queues are temporarily paused when no messages are found.
|
|
5
8
|
class StrictPriority < BaseStrategy
|
|
9
|
+
# Initializes a new StrictPriority polling strategy
|
|
10
|
+
#
|
|
11
|
+
# @param queues [Array<String>] array of queue names, with higher priority queues appearing more frequently
|
|
12
|
+
# @param delay [Float, nil] delay in seconds before unpausing empty queues
|
|
6
13
|
def initialize(queues, delay = nil)
|
|
7
14
|
# Priority ordering of the queues, highest priority first
|
|
8
15
|
@queues = queues
|
|
@@ -19,11 +26,19 @@ module Shoryuken
|
|
|
19
26
|
reset_next_queue
|
|
20
27
|
end
|
|
21
28
|
|
|
29
|
+
# Returns the next queue to poll based on strict priority
|
|
30
|
+
#
|
|
31
|
+
# @return [QueueConfiguration, nil] the next queue configuration or nil if all paused
|
|
22
32
|
def next_queue
|
|
23
33
|
next_queue = next_active_queue
|
|
24
34
|
next_queue.nil? ? nil : QueueConfiguration.new(next_queue, {})
|
|
25
35
|
end
|
|
26
36
|
|
|
37
|
+
# Handles the result of polling a queue
|
|
38
|
+
#
|
|
39
|
+
# @param queue [String] the queue name
|
|
40
|
+
# @param messages_found [Integer] number of messages found
|
|
41
|
+
# @return [void]
|
|
27
42
|
def messages_found(queue, messages_found)
|
|
28
43
|
if messages_found == 0
|
|
29
44
|
pause(queue)
|
|
@@ -32,6 +47,9 @@ module Shoryuken
|
|
|
32
47
|
end
|
|
33
48
|
end
|
|
34
49
|
|
|
50
|
+
# Returns the list of active (non-paused) queues with their priorities
|
|
51
|
+
#
|
|
52
|
+
# @return [Array<Array>] array of [queue_name, priority] pairs
|
|
35
53
|
def active_queues
|
|
36
54
|
@queues
|
|
37
55
|
.reverse
|
|
@@ -40,6 +58,10 @@ module Shoryuken
|
|
|
40
58
|
.reverse
|
|
41
59
|
end
|
|
42
60
|
|
|
61
|
+
# Called when a message from a queue has been processed
|
|
62
|
+
#
|
|
63
|
+
# @param queue [String] the queue name
|
|
64
|
+
# @return [void]
|
|
43
65
|
def message_processed(queue)
|
|
44
66
|
if queue_paused?(queue)
|
|
45
67
|
logger.debug "Unpausing #{queue}"
|
|
@@ -49,6 +71,9 @@ module Shoryuken
|
|
|
49
71
|
|
|
50
72
|
private
|
|
51
73
|
|
|
74
|
+
# Finds the next active (non-paused) queue
|
|
75
|
+
#
|
|
76
|
+
# @return [String, nil] the queue name or nil if all paused
|
|
52
77
|
def next_active_queue
|
|
53
78
|
reset_next_queue if queues_unpaused_since?
|
|
54
79
|
|
|
@@ -62,6 +87,9 @@ module Shoryuken
|
|
|
62
87
|
nil
|
|
63
88
|
end
|
|
64
89
|
|
|
90
|
+
# Checks if any queues have been unpaused since last check
|
|
91
|
+
#
|
|
92
|
+
# @return [Boolean] true if queues were unpaused
|
|
65
93
|
def queues_unpaused_since?
|
|
66
94
|
last = @last_unpause_check
|
|
67
95
|
now = @last_unpause_check = Time.now
|
|
@@ -69,14 +97,25 @@ module Shoryuken
|
|
|
69
97
|
last && @paused_until.values.any? { |t| t > last && t <= now }
|
|
70
98
|
end
|
|
71
99
|
|
|
100
|
+
# Resets the next queue index to start from the highest priority
|
|
101
|
+
#
|
|
102
|
+
# @return [void]
|
|
72
103
|
def reset_next_queue
|
|
73
104
|
@next_queue_index = 0
|
|
74
105
|
end
|
|
75
106
|
|
|
107
|
+
# Checks if a queue is currently paused
|
|
108
|
+
#
|
|
109
|
+
# @param queue [String] the queue name
|
|
110
|
+
# @return [Boolean] true if the queue is paused
|
|
76
111
|
def queue_paused?(queue)
|
|
77
112
|
@paused_until[queue] > Time.now
|
|
78
113
|
end
|
|
79
114
|
|
|
115
|
+
# Pauses a queue for the configured delay time
|
|
116
|
+
#
|
|
117
|
+
# @param queue [String] the queue name to pause
|
|
118
|
+
# @return [void]
|
|
80
119
|
def pause(queue)
|
|
81
120
|
return unless delay > 0
|
|
82
121
|
|