shoryuken 6.2.1 → 7.0.2
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 +36 -0
- data/.github/workflows/specs.yml +49 -44
- data/.github/workflows/verify-action-pins.yml +16 -0
- data/.gitignore +4 -1
- data/.rspec +3 -1
- data/.rubocop.yml +6 -1
- data/.ruby-version +1 -0
- data/.yard-lint.yml +279 -0
- data/CHANGELOG.md +308 -139
- data/Gemfile +1 -8
- data/Gemfile.lint +9 -0
- data/Gemfile.lint.lock +69 -0
- data/README.md +16 -33
- data/Rakefile +6 -10
- data/bin/clean_sqs +52 -0
- data/bin/cli/base.rb +22 -2
- data/bin/cli/sqs.rb +74 -7
- data/bin/integrations +275 -0
- data/bin/scenario +154 -0
- data/bin/shoryuken +3 -2
- data/docker-compose.yml +6 -0
- data/lib/{shoryuken/extensions/active_job_extensions.rb → active_job/extensions.rb} +20 -6
- 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 +11 -1
- data/lib/shoryuken/client.rb +16 -0
- data/lib/shoryuken/default_exception_handler.rb +11 -0
- data/lib/shoryuken/default_worker_registry.rb +39 -11
- data/lib/shoryuken/environment_loader.rb +85 -15
- data/lib/shoryuken/errors.rb +36 -0
- data/lib/shoryuken/fetcher.rb +41 -3
- data/lib/shoryuken/helpers/atomic_boolean.rb +58 -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 +80 -0
- data/lib/shoryuken/inline_message.rb +22 -0
- data/lib/shoryuken/launcher.rb +55 -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 +43 -15
- data/lib/shoryuken/manager.rb +84 -5
- data/lib/shoryuken/message.rb +116 -1
- data/lib/shoryuken/middleware/chain.rb +141 -43
- data/lib/shoryuken/middleware/entry.rb +30 -0
- data/lib/shoryuken/middleware/server/active_record.rb +10 -0
- data/lib/shoryuken/middleware/server/auto_delete.rb +12 -0
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +37 -11
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +34 -3
- data/lib/shoryuken/middleware/server/non_retryable_exception.rb +95 -0
- data/lib/shoryuken/middleware/server/timing.rb +13 -0
- data/lib/shoryuken/options.rb +154 -13
- data/lib/shoryuken/polling/base_strategy.rb +127 -0
- data/lib/shoryuken/polling/queue_configuration.rb +103 -0
- data/lib/shoryuken/polling/strict_priority.rb +41 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +44 -0
- data/lib/shoryuken/processor.rb +37 -3
- data/lib/shoryuken/queue.rb +99 -8
- data/lib/shoryuken/runner.rb +54 -16
- data/lib/shoryuken/util.rb +32 -7
- data/lib/shoryuken/version.rb +4 -1
- data/lib/shoryuken/worker/default_executor.rb +23 -1
- data/lib/shoryuken/worker/inline_executor.rb +33 -2
- data/lib/shoryuken/worker.rb +224 -0
- data/lib/shoryuken/worker_registry.rb +35 -0
- data/lib/shoryuken.rb +27 -38
- data/renovate.json +62 -0
- data/shoryuken.gemspec +8 -4
- 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/keyword_arguments/keyword_arguments_spec.rb +63 -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/custom_group_polling_strategy/custom_group_polling_strategy_spec.rb +87 -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/non_retryable_exception/non_retryable_exception_spec.rb +149 -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 +225 -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} +5 -4
- data/spec/{shoryuken/extensions/active_job_wrapper_spec.rb → lib/shoryuken/active_job/job_wrapper_spec.rb} +6 -5
- data/spec/{shoryuken → lib/shoryuken}/body_parser_spec.rb +2 -4
- data/spec/{shoryuken → lib/shoryuken}/client_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/default_exception_handler_spec.rb +9 -10
- data/spec/{shoryuken → lib/shoryuken}/default_worker_registry_spec.rb +1 -2
- data/spec/{shoryuken → lib/shoryuken}/environment_loader_spec.rb +10 -9
- data/spec/{shoryuken → lib/shoryuken}/fetcher_spec.rb +23 -26
- data/spec/lib/shoryuken/helpers/atomic_boolean_spec.rb +196 -0
- data/spec/lib/shoryuken/helpers/atomic_counter_spec.rb +177 -0
- data/spec/lib/shoryuken/helpers/atomic_hash_spec.rb +307 -0
- data/spec/lib/shoryuken/helpers/hash_utils_spec.rb +145 -0
- data/spec/lib/shoryuken/helpers/string_utils_spec.rb +124 -0
- data/spec/lib/shoryuken/helpers/timer_task_spec.rb +298 -0
- data/spec/lib/shoryuken/helpers_integration_spec.rb +96 -0
- data/spec/lib/shoryuken/inline_message_spec.rb +196 -0
- data/spec/{shoryuken → lib/shoryuken}/launcher_spec.rb +23 -2
- data/spec/lib/shoryuken/logging_spec.rb +242 -0
- data/spec/{shoryuken → lib/shoryuken}/manager_spec.rb +1 -2
- data/spec/lib/shoryuken/message_spec.rb +109 -0
- data/spec/{shoryuken → lib/shoryuken}/middleware/chain_spec.rb +1 -1
- 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_delete_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/auto_extend_visibility_spec.rb +51 -1
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/exponential_backoff_retry_spec.rb +1 -1
- data/spec/lib/shoryuken/middleware/server/non_retryable_exception_spec.rb +214 -0
- data/spec/{shoryuken → lib/shoryuken}/middleware/server/timing_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/options_spec.rb +49 -6
- data/spec/lib/shoryuken/polling/base_strategy_spec.rb +280 -0
- data/spec/lib/shoryuken/polling/queue_configuration_spec.rb +195 -0
- data/spec/{shoryuken → lib/shoryuken}/polling/strict_priority_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/polling/weighted_round_robin_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/processor_spec.rb +1 -1
- data/spec/{shoryuken → lib/shoryuken}/queue_spec.rb +2 -3
- data/spec/{shoryuken → lib/shoryuken}/runner_spec.rb +1 -3
- data/spec/{shoryuken → lib/shoryuken}/util_spec.rb +2 -2
- data/spec/lib/shoryuken/version_spec.rb +17 -0
- data/spec/{shoryuken → lib/shoryuken}/worker/default_executor_spec.rb +1 -1
- data/spec/lib/shoryuken/worker/inline_executor_spec.rb +105 -0
- data/spec/lib/shoryuken/worker_registry_spec.rb +63 -0
- data/spec/{shoryuken → lib/shoryuken}/worker_spec.rb +15 -11
- data/spec/{shoryuken_spec.rb → lib/shoryuken_spec.rb} +1 -1
- data/spec/shared_examples_for_active_job.rb +40 -15
- data/spec/spec_helper.rb +48 -2
- metadata +295 -101
- data/.codeclimate.yml +0 -20
- data/.devcontainer/Dockerfile +0 -17
- data/.devcontainer/base.Dockerfile +0 -43
- data/.devcontainer/devcontainer.json +0 -35
- data/.github/FUNDING.yml +0 -12
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/stale.yml +0 -20
- data/.reek.yml +0 -5
- data/Appraisals +0 -42
- data/gemfiles/.gitignore +0 -1
- data/gemfiles/aws_sdk_core_2.gemfile +0 -21
- data/gemfiles/rails_4_2.gemfile +0 -20
- data/gemfiles/rails_5_2.gemfile +0 -21
- data/gemfiles/rails_6_0.gemfile +0 -21
- data/gemfiles/rails_6_1.gemfile +0 -21
- data/gemfiles/rails_7_0.gemfile +0 -22
- data/lib/shoryuken/core_ext.rb +0 -69
- data/lib/shoryuken/extensions/active_job_adapter.rb +0 -103
- data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +0 -50
- data/lib/shoryuken/polling/base.rb +0 -67
- data/shoryuken.jpg +0 -0
- data/spec/integration/launcher_spec.rb +0 -128
- data/spec/shoryuken/core_ext_spec.rb +0 -40
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +0 -7
- data/spec/shoryuken/extensions/active_job_base_spec.rb +0 -84
- data/spec/shoryuken/worker/inline_executor_spec.rb +0 -49
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Scheduled ActiveJob integration test
|
|
4
|
+
# Tests jobs scheduled with set(wait:) are delivered after the delay
|
|
5
|
+
|
|
6
|
+
setup_sqs
|
|
7
|
+
setup_active_job
|
|
8
|
+
|
|
9
|
+
queue_name = DT.queue
|
|
10
|
+
create_test_queue(queue_name)
|
|
11
|
+
|
|
12
|
+
class ScheduledTestJob < ActiveJob::Base
|
|
13
|
+
def perform(label)
|
|
14
|
+
DT[:executions] << {
|
|
15
|
+
label: label,
|
|
16
|
+
job_id: job_id,
|
|
17
|
+
executed_at: Time.now
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
ScheduledTestJob.queue_as(queue_name)
|
|
23
|
+
|
|
24
|
+
Shoryuken.add_group('default', 1)
|
|
25
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
26
|
+
Shoryuken.register_worker(queue_name, Shoryuken::ActiveJob::JobWrapper)
|
|
27
|
+
|
|
28
|
+
immediate_enqueue_time = Time.now
|
|
29
|
+
ScheduledTestJob.perform_later('immediate')
|
|
30
|
+
DT[:timestamps] << { label: 'immediate', time: immediate_enqueue_time }
|
|
31
|
+
|
|
32
|
+
delayed_enqueue_time = Time.now
|
|
33
|
+
ScheduledTestJob.set(wait: 3.seconds).perform_later('delayed_3s')
|
|
34
|
+
DT[:timestamps] << { label: 'delayed_3s', time: delayed_enqueue_time }
|
|
35
|
+
|
|
36
|
+
delayed_5s_enqueue_time = Time.now
|
|
37
|
+
ScheduledTestJob.set(wait: 5.seconds).perform_later('delayed_5s')
|
|
38
|
+
DT[:timestamps] << { label: 'delayed_5s', time: delayed_5s_enqueue_time }
|
|
39
|
+
|
|
40
|
+
poll_queues_until(timeout: 30) do
|
|
41
|
+
DT[:executions].size >= 3
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
assert_equal(3, DT[:executions].size, "Expected 3 job executions")
|
|
45
|
+
|
|
46
|
+
# Find each job's execution
|
|
47
|
+
immediate_job = DT[:executions].find { |e| e[:label] == 'immediate' }
|
|
48
|
+
delayed_3s_job = DT[:executions].find { |e| e[:label] == 'delayed_3s' }
|
|
49
|
+
delayed_5s_job = DT[:executions].find { |e| e[:label] == 'delayed_5s' }
|
|
50
|
+
|
|
51
|
+
assert(immediate_job, "Immediate job should have executed")
|
|
52
|
+
assert(delayed_3s_job, "3s delayed job should have executed")
|
|
53
|
+
assert(delayed_5s_job, "5s delayed job should have executed")
|
|
54
|
+
|
|
55
|
+
def enqueue_time(label)
|
|
56
|
+
DT[:timestamps].find { |t| t[:label] == label }[:time]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
immediate_delay = immediate_job[:executed_at] - enqueue_time('immediate')
|
|
60
|
+
assert(immediate_delay < 10, "Immediate job should execute within 10 seconds, took #{immediate_delay}s")
|
|
61
|
+
|
|
62
|
+
# Using 2 seconds tolerance for SQS delivery variation
|
|
63
|
+
delayed_3s_actual_delay = delayed_3s_job[:executed_at] - enqueue_time('delayed_3s')
|
|
64
|
+
assert(delayed_3s_actual_delay >= 2, "3s delayed job should execute after at least 2s, took #{delayed_3s_actual_delay}s")
|
|
65
|
+
|
|
66
|
+
delayed_5s_actual_delay = delayed_5s_job[:executed_at] - enqueue_time('delayed_5s')
|
|
67
|
+
assert(delayed_5s_actual_delay >= 4, "5s delayed job should execute after at least 4s, took #{delayed_5s_actual_delay}s")
|
|
68
|
+
|
|
69
|
+
assert(
|
|
70
|
+
immediate_job[:executed_at] <= delayed_3s_job[:executed_at],
|
|
71
|
+
"Immediate job should execute before 3s delayed job"
|
|
72
|
+
)
|
|
73
|
+
assert(
|
|
74
|
+
delayed_3s_job[:executed_at] <= delayed_5s_job[:executed_at],
|
|
75
|
+
"3s delayed job should execute before 5s delayed job"
|
|
76
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests the ActiveRecord middleware functionality.
|
|
4
|
+
# The middleware clears database connections after each message is processed.
|
|
5
|
+
|
|
6
|
+
setup_sqs
|
|
7
|
+
|
|
8
|
+
queue_name = DT.queue
|
|
9
|
+
create_test_queue(queue_name)
|
|
10
|
+
Shoryuken.add_group('default', 1)
|
|
11
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
12
|
+
|
|
13
|
+
# Mock ActiveRecord module to track connection clearing
|
|
14
|
+
module ActiveRecord
|
|
15
|
+
VERSION = Gem::Version.new('7.2.0')
|
|
16
|
+
|
|
17
|
+
def self.version
|
|
18
|
+
VERSION
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Base
|
|
22
|
+
class << self
|
|
23
|
+
attr_accessor :connections_cleared
|
|
24
|
+
|
|
25
|
+
def connection_handler
|
|
26
|
+
@connection_handler ||= ConnectionHandler.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
self.connections_cleared = []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class ConnectionHandler
|
|
34
|
+
def clear_active_connections!(scope)
|
|
35
|
+
ActiveRecord::Base.connections_cleared << { scope: scope, time: Time.now }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Add the ActiveRecord middleware to the chain
|
|
41
|
+
require 'shoryuken/middleware/server/active_record'
|
|
42
|
+
Shoryuken.configure_server do |config|
|
|
43
|
+
config.server_middleware do |chain|
|
|
44
|
+
chain.add Shoryuken::Middleware::Server::ActiveRecord
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
worker_class = Class.new do
|
|
49
|
+
include Shoryuken::Worker
|
|
50
|
+
|
|
51
|
+
shoryuken_options auto_delete: true, batch: false
|
|
52
|
+
|
|
53
|
+
def perform(sqs_msg, body)
|
|
54
|
+
DT[:processed] << { message_id: sqs_msg.message_id, body: body }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
worker_class.get_shoryuken_options['queue'] = queue_name
|
|
59
|
+
Shoryuken.register_worker(queue_name, worker_class)
|
|
60
|
+
|
|
61
|
+
# Clear any prior connection clearing records
|
|
62
|
+
ActiveRecord::Base.connections_cleared.clear
|
|
63
|
+
|
|
64
|
+
# Send multiple messages
|
|
65
|
+
3.times { |i| Shoryuken::Client.queues(queue_name).send_message(message_body: "ar-test-#{i}") }
|
|
66
|
+
|
|
67
|
+
sleep 1
|
|
68
|
+
|
|
69
|
+
poll_queues_until { DT[:processed].size >= 3 }
|
|
70
|
+
|
|
71
|
+
# Verify all messages were processed
|
|
72
|
+
assert_equal(3, DT[:processed].size)
|
|
73
|
+
|
|
74
|
+
# Verify ActiveRecord connections were cleared after each message
|
|
75
|
+
# The middleware should have called clear_active_connections! for each message
|
|
76
|
+
assert(
|
|
77
|
+
ActiveRecord::Base.connections_cleared.size >= 3,
|
|
78
|
+
"ActiveRecord connections should be cleared after each message (cleared #{ActiveRecord::Base.connections_cleared.size} times)"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Verify the :all scope was used (Rails 7.1+ behavior)
|
|
82
|
+
ActiveRecord::Base.connections_cleared.each do |record|
|
|
83
|
+
assert_equal(:all, record[:scope], 'Should use :all scope for Rails 7.1+')
|
|
84
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests the auto_delete middleware functionality.
|
|
4
|
+
# When auto_delete: true, messages should be automatically deleted after successful processing.
|
|
5
|
+
|
|
6
|
+
setup_sqs
|
|
7
|
+
|
|
8
|
+
queue_name = DT.queue
|
|
9
|
+
create_test_queue(queue_name)
|
|
10
|
+
Shoryuken.add_group('default', 1)
|
|
11
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
12
|
+
|
|
13
|
+
auto_delete_worker = Class.new do
|
|
14
|
+
include Shoryuken::Worker
|
|
15
|
+
|
|
16
|
+
shoryuken_options auto_delete: true
|
|
17
|
+
|
|
18
|
+
def perform(sqs_msg, body)
|
|
19
|
+
DT[:auto_delete_processed] << { message_id: sqs_msg.message_id, body: body }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
auto_delete_worker.get_shoryuken_options['queue'] = queue_name
|
|
24
|
+
Shoryuken.register_worker(queue_name, auto_delete_worker)
|
|
25
|
+
|
|
26
|
+
queue_url = Shoryuken::Client.sqs.get_queue_url(queue_name: queue_name).queue_url
|
|
27
|
+
|
|
28
|
+
# Send a message
|
|
29
|
+
Shoryuken::Client.sqs.send_message(
|
|
30
|
+
queue_url: queue_url,
|
|
31
|
+
message_body: 'auto delete test'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
sleep 1
|
|
35
|
+
|
|
36
|
+
# Process the message
|
|
37
|
+
poll_queues_until { DT[:auto_delete_processed].size >= 1 }
|
|
38
|
+
|
|
39
|
+
assert_equal(1, DT[:auto_delete_processed].size)
|
|
40
|
+
assert_equal('auto delete test', DT[:auto_delete_processed].first[:body])
|
|
41
|
+
|
|
42
|
+
# Wait a moment for deletion to complete
|
|
43
|
+
sleep 2
|
|
44
|
+
|
|
45
|
+
# Verify message was deleted - queue should be empty
|
|
46
|
+
attributes = Shoryuken::Client.sqs.get_queue_attributes(
|
|
47
|
+
queue_url: queue_url,
|
|
48
|
+
attribute_names: ['ApproximateNumberOfMessages', 'ApproximateNumberOfMessagesNotVisible']
|
|
49
|
+
).attributes
|
|
50
|
+
|
|
51
|
+
total_messages = attributes['ApproximateNumberOfMessages'].to_i +
|
|
52
|
+
attributes['ApproximateNumberOfMessagesNotVisible'].to_i
|
|
53
|
+
assert_equal(0, total_messages, "Message should be deleted when auto_delete: true")
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests the auto_visibility_timeout middleware functionality.
|
|
4
|
+
# When auto_visibility_timeout: true, the message visibility timeout should be
|
|
5
|
+
# automatically extended during long-running job processing to prevent re-delivery.
|
|
6
|
+
|
|
7
|
+
setup_sqs
|
|
8
|
+
|
|
9
|
+
queue_name = DT.uuid
|
|
10
|
+
|
|
11
|
+
# Create queue with short visibility timeout (10 seconds)
|
|
12
|
+
create_test_queue(queue_name, attributes: { 'VisibilityTimeout' => '10' })
|
|
13
|
+
Shoryuken.add_group('default', 1)
|
|
14
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
15
|
+
|
|
16
|
+
# Worker with auto_visibility_timeout enabled that takes longer than visibility timeout
|
|
17
|
+
auto_visibility_worker = Class.new do
|
|
18
|
+
include Shoryuken::Worker
|
|
19
|
+
|
|
20
|
+
shoryuken_options auto_visibility_timeout: true, auto_delete: true
|
|
21
|
+
|
|
22
|
+
def perform(sqs_msg, body)
|
|
23
|
+
DT[:processing_started] << Time.now
|
|
24
|
+
# Sleep longer than the queue's visibility timeout (10s)
|
|
25
|
+
# The middleware should extend visibility before it expires
|
|
26
|
+
sleep 12
|
|
27
|
+
DT[:processing_completed] << Time.now
|
|
28
|
+
DT[:processed_messages] << { message_id: sqs_msg.message_id, body: body }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
auto_visibility_worker.get_shoryuken_options['queue'] = queue_name
|
|
33
|
+
Shoryuken.register_worker(queue_name, auto_visibility_worker)
|
|
34
|
+
|
|
35
|
+
queue_url = Shoryuken::Client.sqs.get_queue_url(queue_name: queue_name).queue_url
|
|
36
|
+
|
|
37
|
+
# Send a message
|
|
38
|
+
Shoryuken::Client.sqs.send_message(
|
|
39
|
+
queue_url: queue_url,
|
|
40
|
+
message_body: 'long running job'
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
sleep 1
|
|
44
|
+
|
|
45
|
+
# Process the message - this should take ~12 seconds but not fail
|
|
46
|
+
poll_queues_until(timeout: 30) { DT[:processed_messages].size >= 1 }
|
|
47
|
+
|
|
48
|
+
# Verify message was processed exactly once (visibility was extended, not re-delivered)
|
|
49
|
+
assert_equal(1, DT[:processed_messages].size, "Message should be processed exactly once")
|
|
50
|
+
assert_equal('long running job', DT[:processed_messages].first[:body])
|
|
51
|
+
|
|
52
|
+
# Verify processing took longer than the visibility timeout
|
|
53
|
+
processing_time = DT[:processing_completed].first - DT[:processing_started].first
|
|
54
|
+
assert(processing_time >= 12, "Processing should have taken at least 12 seconds")
|
|
55
|
+
|
|
56
|
+
# Cleanup
|
|
57
|
+
delete_test_queue(queue_name)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests that AWS configuration from Shoryuken.options[:aws]
|
|
4
|
+
# is properly passed to the SQS client initialization.
|
|
5
|
+
# This verifies the fix for issue #815: PORO setup does not load AWS config
|
|
6
|
+
|
|
7
|
+
# Reset any cached SQS client to ensure fresh initialization
|
|
8
|
+
Shoryuken.sqs_client = nil
|
|
9
|
+
|
|
10
|
+
# Configure AWS options programmatically (simulating PORO setup with config file)
|
|
11
|
+
Shoryuken.options[:aws] = {
|
|
12
|
+
region: 'us-east-1',
|
|
13
|
+
endpoint: 'http://localhost:9324',
|
|
14
|
+
access_key_id: 'test-key-from-config',
|
|
15
|
+
secret_access_key: 'test-secret-from-config'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Get the SQS client - this should use the AWS config from options
|
|
19
|
+
client = Shoryuken.sqs_client
|
|
20
|
+
|
|
21
|
+
# Verify the client was configured with our options
|
|
22
|
+
config = client.config
|
|
23
|
+
|
|
24
|
+
assert_equal('us-east-1', config.region, "Region should be set from options[:aws]")
|
|
25
|
+
assert_equal('http://localhost:9324', config.endpoint.to_s, "Endpoint should be set from options[:aws]")
|
|
26
|
+
|
|
27
|
+
# Verify the client actually works by creating a queue
|
|
28
|
+
queue_name = "aws-config-test-#{SecureRandom.hex(6)}"
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
result = client.create_queue(queue_name: queue_name)
|
|
32
|
+
assert(result.queue_url.include?(queue_name), "Should be able to create queue with configured client")
|
|
33
|
+
|
|
34
|
+
# Clean up
|
|
35
|
+
client.delete_queue(queue_url: result.queue_url)
|
|
36
|
+
rescue Aws::SQS::Errors::ServiceError => e
|
|
37
|
+
raise "SQS client should work with configured AWS options: #{e.message}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Test 2: Verify that reconfiguring options and resetting client works
|
|
41
|
+
Shoryuken.sqs_client = nil
|
|
42
|
+
Shoryuken.options[:aws][:region] = 'us-west-2'
|
|
43
|
+
|
|
44
|
+
client2 = Shoryuken.sqs_client
|
|
45
|
+
assert_equal('us-west-2', client2.config.region, "New client should use updated region")
|
|
46
|
+
|
|
47
|
+
# Test 3: Verify that credentials from options are used
|
|
48
|
+
# Reset and reconfigure with explicit credentials
|
|
49
|
+
Shoryuken.sqs_client = nil
|
|
50
|
+
Shoryuken.options[:aws] = {
|
|
51
|
+
region: 'us-east-1',
|
|
52
|
+
endpoint: 'http://localhost:9324',
|
|
53
|
+
access_key_id: 'another-key',
|
|
54
|
+
secret_access_key: 'another-secret'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
client3 = Shoryuken.sqs_client
|
|
58
|
+
assert(client3.is_a?(Aws::SQS::Client), "Client should be created with new credentials")
|
|
59
|
+
assert_equal('us-east-1', client3.config.region, "Region should match configured value")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests batch processing including batch message reception (up to 10
|
|
4
|
+
# messages), batch vs single worker behavior differences, JSON body parsing in
|
|
5
|
+
# batch mode, and maximum batch size handling.
|
|
6
|
+
|
|
7
|
+
setup_sqs
|
|
8
|
+
|
|
9
|
+
queue_name = DT.queue
|
|
10
|
+
create_test_queue(queue_name)
|
|
11
|
+
Shoryuken.add_group('default', 1)
|
|
12
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
13
|
+
|
|
14
|
+
worker_class = Class.new do
|
|
15
|
+
include Shoryuken::Worker
|
|
16
|
+
|
|
17
|
+
def perform(sqs_msgs, bodies)
|
|
18
|
+
msgs = Array(sqs_msgs)
|
|
19
|
+
DT[:batch_sizes] << msgs.size
|
|
20
|
+
DT[:messages].concat(Array(bodies))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
worker_class.get_shoryuken_options['queue'] = queue_name
|
|
25
|
+
worker_class.get_shoryuken_options['auto_delete'] = true
|
|
26
|
+
worker_class.get_shoryuken_options['batch'] = true
|
|
27
|
+
Shoryuken.register_worker(queue_name, worker_class)
|
|
28
|
+
|
|
29
|
+
entries = 5.times.map { |i| { id: SecureRandom.uuid, message_body: "message-#{i}" } }
|
|
30
|
+
Shoryuken::Client.queues(queue_name).send_messages(entries: entries)
|
|
31
|
+
|
|
32
|
+
sleep 1
|
|
33
|
+
|
|
34
|
+
poll_queues_until { DT[:messages].size >= 5 }
|
|
35
|
+
|
|
36
|
+
assert_equal(5, DT[:messages].size)
|
|
37
|
+
assert(DT[:batch_sizes].any? { |size| size > 1 }, "Expected at least one batch with size > 1")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests the body_parser option with :json setting
|
|
4
|
+
# Verifies that JSON messages are automatically parsed into Ruby hashes
|
|
5
|
+
|
|
6
|
+
setup_sqs
|
|
7
|
+
|
|
8
|
+
queue_name = DT.uuid
|
|
9
|
+
create_test_queue(queue_name)
|
|
10
|
+
Shoryuken.add_group('default', 1)
|
|
11
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
12
|
+
|
|
13
|
+
worker_class = Class.new do
|
|
14
|
+
include Shoryuken::Worker
|
|
15
|
+
|
|
16
|
+
shoryuken_options body_parser: :json
|
|
17
|
+
|
|
18
|
+
def perform(sqs_msg, body)
|
|
19
|
+
DT[:parsed_bodies] << body
|
|
20
|
+
DT[:body_classes] << body.class.name
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
worker_class.get_shoryuken_options['queue'] = queue_name
|
|
25
|
+
worker_class.get_shoryuken_options['auto_delete'] = true
|
|
26
|
+
Shoryuken.register_worker(queue_name, worker_class)
|
|
27
|
+
|
|
28
|
+
queue_url = Shoryuken::Client.sqs.get_queue_url(queue_name: queue_name).queue_url
|
|
29
|
+
|
|
30
|
+
# Send a JSON message
|
|
31
|
+
json_body = { 'name' => 'test', 'value' => 42, 'nested' => { 'key' => 'val' } }
|
|
32
|
+
Shoryuken::Client.sqs.send_message(
|
|
33
|
+
queue_url: queue_url,
|
|
34
|
+
message_body: JSON.dump(json_body)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
sleep 1
|
|
38
|
+
|
|
39
|
+
poll_queues_until { DT[:parsed_bodies].size >= 1 }
|
|
40
|
+
|
|
41
|
+
assert_equal(1, DT[:parsed_bodies].size)
|
|
42
|
+
assert_equal('Hash', DT[:body_classes].first)
|
|
43
|
+
assert_equal('test', DT[:parsed_bodies].first['name'])
|
|
44
|
+
assert_equal(42, DT[:parsed_bodies].first['value'])
|
|
45
|
+
assert_equal('val', DT[:parsed_bodies].first['nested']['key'])
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests the body_parser option with a custom Proc
|
|
4
|
+
# Verifies that custom parsing logic can be applied to messages
|
|
5
|
+
|
|
6
|
+
setup_sqs
|
|
7
|
+
|
|
8
|
+
queue_name = DT.uuid
|
|
9
|
+
create_test_queue(queue_name)
|
|
10
|
+
Shoryuken.add_group('default', 1)
|
|
11
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
12
|
+
|
|
13
|
+
# Custom parser that uppercases the body and adds metadata
|
|
14
|
+
custom_parser = proc do |sqs_msg|
|
|
15
|
+
{
|
|
16
|
+
original: sqs_msg.body,
|
|
17
|
+
transformed: sqs_msg.body.upcase,
|
|
18
|
+
message_id: sqs_msg.message_id
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
worker_class = Class.new do
|
|
23
|
+
include Shoryuken::Worker
|
|
24
|
+
|
|
25
|
+
def perform(sqs_msg, body)
|
|
26
|
+
DT[:parsed_bodies] << body
|
|
27
|
+
DT[:body_classes] << body.class.name
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
worker_class.get_shoryuken_options['queue'] = queue_name
|
|
32
|
+
worker_class.get_shoryuken_options['auto_delete'] = true
|
|
33
|
+
worker_class.get_shoryuken_options['body_parser'] = custom_parser
|
|
34
|
+
Shoryuken.register_worker(queue_name, worker_class)
|
|
35
|
+
|
|
36
|
+
queue_url = Shoryuken::Client.sqs.get_queue_url(queue_name: queue_name).queue_url
|
|
37
|
+
|
|
38
|
+
# Send a message to be processed by custom parser
|
|
39
|
+
Shoryuken::Client.sqs.send_message(
|
|
40
|
+
queue_url: queue_url,
|
|
41
|
+
message_body: 'hello world'
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
sleep 1
|
|
45
|
+
|
|
46
|
+
poll_queues_until { DT[:parsed_bodies].size >= 1 }
|
|
47
|
+
|
|
48
|
+
assert_equal(1, DT[:parsed_bodies].size)
|
|
49
|
+
assert_equal('Hash', DT[:body_classes].first)
|
|
50
|
+
|
|
51
|
+
parsed = DT[:parsed_bodies].first
|
|
52
|
+
assert_equal('hello world', parsed[:original])
|
|
53
|
+
assert_equal('HELLO WORLD', parsed[:transformed])
|
|
54
|
+
assert(parsed[:message_id], "Should include message_id from sqs_msg")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests the body_parser option with :text setting (default)
|
|
4
|
+
# Verifies that messages are returned as plain strings
|
|
5
|
+
|
|
6
|
+
setup_sqs
|
|
7
|
+
|
|
8
|
+
queue_name = DT.uuid
|
|
9
|
+
create_test_queue(queue_name)
|
|
10
|
+
Shoryuken.add_group('default', 1)
|
|
11
|
+
Shoryuken.add_queue(queue_name, 1, 'default')
|
|
12
|
+
|
|
13
|
+
worker_class = Class.new do
|
|
14
|
+
include Shoryuken::Worker
|
|
15
|
+
|
|
16
|
+
shoryuken_options body_parser: :text
|
|
17
|
+
|
|
18
|
+
def perform(sqs_msg, body)
|
|
19
|
+
DT[:parsed_bodies] << body
|
|
20
|
+
DT[:body_classes] << body.class.name
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
worker_class.get_shoryuken_options['queue'] = queue_name
|
|
25
|
+
worker_class.get_shoryuken_options['auto_delete'] = true
|
|
26
|
+
Shoryuken.register_worker(queue_name, worker_class)
|
|
27
|
+
|
|
28
|
+
queue_url = Shoryuken::Client.sqs.get_queue_url(queue_name: queue_name).queue_url
|
|
29
|
+
|
|
30
|
+
# Send a plain text message
|
|
31
|
+
text_body = 'Hello, this is a plain text message!'
|
|
32
|
+
Shoryuken::Client.sqs.send_message(
|
|
33
|
+
queue_url: queue_url,
|
|
34
|
+
message_body: text_body
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
sleep 1
|
|
38
|
+
|
|
39
|
+
poll_queues_until { DT[:parsed_bodies].size >= 1 }
|
|
40
|
+
|
|
41
|
+
assert_equal(1, DT[:parsed_bodies].size)
|
|
42
|
+
assert_equal('String', DT[:body_classes].first)
|
|
43
|
+
assert_equal(text_body, DT[:parsed_bodies].first)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests concurrent message processing with multiple processors.
|
|
4
|
+
|
|
5
|
+
require 'concurrent'
|
|
6
|
+
|
|
7
|
+
setup_sqs
|
|
8
|
+
|
|
9
|
+
queue_name = DT.queue
|
|
10
|
+
create_test_queue(queue_name)
|
|
11
|
+
Shoryuken.add_group('concurrent', 5) # 5 concurrent processors
|
|
12
|
+
Shoryuken.add_queue(queue_name, 1, 'concurrent')
|
|
13
|
+
|
|
14
|
+
# Atomic counters for tracking concurrency
|
|
15
|
+
concurrent_count = Concurrent::AtomicFixnum.new(0)
|
|
16
|
+
max_concurrent = Concurrent::AtomicFixnum.new(0)
|
|
17
|
+
|
|
18
|
+
worker_class = Class.new do
|
|
19
|
+
include Shoryuken::Worker
|
|
20
|
+
|
|
21
|
+
shoryuken_options auto_delete: true, batch: false
|
|
22
|
+
|
|
23
|
+
define_method(:perform) do |sqs_msg, body|
|
|
24
|
+
concurrent_count.increment
|
|
25
|
+
current = concurrent_count.value
|
|
26
|
+
max_concurrent.update { |max| [max, current].max }
|
|
27
|
+
|
|
28
|
+
sleep 0.5 # Simulate work
|
|
29
|
+
|
|
30
|
+
DT[:processing_times] << Time.now
|
|
31
|
+
|
|
32
|
+
concurrent_count.decrement
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
worker_class.get_shoryuken_options['queue'] = queue_name
|
|
37
|
+
Shoryuken.register_worker(queue_name, worker_class)
|
|
38
|
+
|
|
39
|
+
10.times { |i| Shoryuken::Client.queues(queue_name).send_message(message_body: "msg-#{i}") }
|
|
40
|
+
|
|
41
|
+
poll_queues_until(timeout: 20) { DT[:processing_times].size >= 10 }
|
|
42
|
+
|
|
43
|
+
assert_equal(10, DT[:processing_times].size)
|
|
44
|
+
# With multiple processors, we should see concurrency > 1
|
|
45
|
+
assert(max_concurrent.value > 1, "Expected concurrency > 1, got #{max_concurrent.value}")
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This spec tests that a custom polling strategy can be configured per-group
|
|
4
|
+
# via the add_group API.
|
|
5
|
+
#
|
|
6
|
+
# Bug #925: Cannot configure custom polling_strategy from YAML config
|
|
7
|
+
# The add_group method does not accept a polling_strategy parameter,
|
|
8
|
+
# making it impossible to set a per-group polling strategy programmatically.
|
|
9
|
+
# Additionally, polling_strategy() reads from options[:groups] (raw config)
|
|
10
|
+
# rather than from groups (the processed hash populated by add_group).
|
|
11
|
+
|
|
12
|
+
# Define a custom polling strategy
|
|
13
|
+
class CustomRoundRobin < Shoryuken::Polling::BaseStrategy
|
|
14
|
+
def initialize(queues, delay = nil)
|
|
15
|
+
@queues = queues.dup.uniq
|
|
16
|
+
@delay = delay
|
|
17
|
+
@index = 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def next_queue
|
|
21
|
+
return nil if @queues.empty?
|
|
22
|
+
|
|
23
|
+
queue = @queues[@index % @queues.length]
|
|
24
|
+
@index += 1
|
|
25
|
+
Shoryuken::Polling::QueueConfiguration.new(queue, {})
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def messages_found(_queue, _count)
|
|
29
|
+
# noop
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def active_queues
|
|
33
|
+
@queues.map { |q| [q, 1] }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# ---- Part 1: API assertions (no SQS needed) ----
|
|
38
|
+
# add_group accepts polling_strategy: keyword argument
|
|
39
|
+
Shoryuken.add_group('custom_group', 1, polling_strategy: CustomRoundRobin)
|
|
40
|
+
|
|
41
|
+
# polling_strategy() returns our custom strategy for the group
|
|
42
|
+
strategy = Shoryuken.polling_strategy('custom_group')
|
|
43
|
+
assert_equal(
|
|
44
|
+
CustomRoundRobin,
|
|
45
|
+
strategy,
|
|
46
|
+
"Expected polling_strategy('custom_group') to return CustomRoundRobin, got #{strategy.inspect}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# add_group raises InvalidPollingStrategyError for invalid types
|
|
50
|
+
begin
|
|
51
|
+
Shoryuken.add_group('bad_group', 1, polling_strategy: 123)
|
|
52
|
+
raise 'Expected InvalidPollingStrategyError to be raised'
|
|
53
|
+
rescue Shoryuken::Errors::InvalidPollingStrategyError
|
|
54
|
+
# expected
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# ---- Part 2: End-to-end with SQS ----
|
|
58
|
+
setup_sqs
|
|
59
|
+
|
|
60
|
+
queue_name = DT.queues[0]
|
|
61
|
+
create_test_queue(queue_name)
|
|
62
|
+
|
|
63
|
+
Shoryuken.groups.clear
|
|
64
|
+
Shoryuken.add_group('custom_group', 1, polling_strategy: CustomRoundRobin)
|
|
65
|
+
Shoryuken.add_queue(queue_name, 1, 'custom_group')
|
|
66
|
+
|
|
67
|
+
worker_class = Class.new do
|
|
68
|
+
include Shoryuken::Worker
|
|
69
|
+
|
|
70
|
+
shoryuken_options auto_delete: true, batch: false
|
|
71
|
+
|
|
72
|
+
def perform(_sqs_msg, body)
|
|
73
|
+
DT[:processed] << body
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
worker_class.get_shoryuken_options['queue'] = queue_name
|
|
78
|
+
Shoryuken.register_worker(queue_name, worker_class)
|
|
79
|
+
|
|
80
|
+
Shoryuken::Client.queues(queue_name).send_message(message_body: 'custom-strategy-msg')
|
|
81
|
+
|
|
82
|
+
sleep 1
|
|
83
|
+
|
|
84
|
+
poll_queues_until { DT[:processed].size >= 1 }
|
|
85
|
+
|
|
86
|
+
assert_equal(1, DT[:processed].size)
|
|
87
|
+
assert_equal('custom-strategy-msg', DT[:processed].first)
|