shoryuken 5.3.0 → 6.2.1
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/.devcontainer/Dockerfile +17 -0
- data/.devcontainer/base.Dockerfile +43 -0
- data/.devcontainer/devcontainer.json +35 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/specs.yml +5 -2
- data/.github/workflows/stale.yml +20 -0
- data/Appraisals +6 -0
- data/CHANGELOG.md +144 -0
- data/Gemfile +5 -2
- data/README.md +19 -1
- data/bin/cli/sqs.rb +1 -1
- data/gemfiles/aws_sdk_core_2.gemfile +1 -1
- data/gemfiles/rails_7_0.gemfile +22 -0
- data/lib/shoryuken/default_exception_handler.rb +10 -0
- data/lib/shoryuken/environment_loader.rb +22 -3
- data/lib/shoryuken/extensions/active_job_adapter.rb +5 -2
- data/lib/shoryuken/extensions/active_job_extensions.rb +1 -1
- data/lib/shoryuken/launcher.rb +18 -3
- data/lib/shoryuken/logging.rb +2 -2
- data/lib/shoryuken/manager.rb +24 -9
- data/lib/shoryuken/middleware/server/active_record.rb +5 -1
- data/lib/shoryuken/options.rb +14 -6
- data/lib/shoryuken/polling/strict_priority.rb +4 -2
- data/lib/shoryuken/polling/weighted_round_robin.rb +3 -5
- data/lib/shoryuken/processor.rb +14 -6
- data/lib/shoryuken/queue.rb +5 -3
- data/lib/shoryuken/runner.rb +0 -3
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken.rb +7 -0
- data/shoryuken.gemspec +1 -1
- data/spec/shared_examples_for_active_job.rb +4 -2
- data/spec/shoryuken/default_exception_handler_spec.rb +71 -0
- data/spec/shoryuken/environment_loader_spec.rb +42 -9
- data/spec/shoryuken/extensions/active_job_base_spec.rb +1 -1
- data/spec/shoryuken/fetcher_spec.rb +12 -12
- data/spec/shoryuken/launcher_spec.rb +46 -1
- data/spec/shoryuken/manager_spec.rb +10 -6
- data/spec/shoryuken/polling/weighted_round_robin_spec.rb +31 -6
- data/spec/shoryuken/processor_spec.rb +38 -0
- data/spec/shoryuken/queue_spec.rb +10 -5
- data/spec/shoryuken/util_spec.rb +24 -4
- data/spec/shoryuken/worker/default_executor_spec.rb +48 -48
- data/spec/spec_helper.rb +2 -0
- metadata +16 -7
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
**I'm looking for Shoryuken maintainers, are you interested on helping to maintain Shoryuken?
|
1
|
+
**I'm looking for Shoryuken maintainers, are you interested on helping to maintain Shoryuken? [Join our Slack](https://join.slack.com/t/shoryuken/shared_invite/zt-19xjq3iqc-KmoJ6eU6~qvZNqcLzIrjww)**
|
2
2
|
|
3
3
|
# Shoryuken
|
4
4
|
|
@@ -86,3 +86,21 @@ To run integration specs, start a mock SQS server on `localhost:5000`. One such
|
|
86
86
|
```sh
|
87
87
|
bundle exec rake spec:integration
|
88
88
|
```
|
89
|
+
|
90
|
+
### To release a new version
|
91
|
+
|
92
|
+
Compare latest tag with HEAD:
|
93
|
+
|
94
|
+
```sh
|
95
|
+
git log $(git describe --tags --abbrev=0)..HEAD --oneline
|
96
|
+
```
|
97
|
+
|
98
|
+
then update CHANGELOG.md.
|
99
|
+
|
100
|
+
Update version in `lib/shoryuken/version.rb` with the appropriate version number [SEMVER](https://semver.org/).
|
101
|
+
|
102
|
+
then run:
|
103
|
+
|
104
|
+
```sh
|
105
|
+
bundle exec rake release
|
106
|
+
```
|
data/bin/cli/sqs.rb
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
group :test do
|
6
|
+
gem "activejob", "~> 7.0"
|
7
|
+
gem "aws-sdk-core", "~> 3"
|
8
|
+
gem "aws-sdk-sqs"
|
9
|
+
gem "codeclimate-test-reporter", require: nil
|
10
|
+
gem "httparty"
|
11
|
+
gem "multi_xml"
|
12
|
+
gem "simplecov"
|
13
|
+
end
|
14
|
+
|
15
|
+
group :development do
|
16
|
+
gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
|
17
|
+
gem "rubocop"
|
18
|
+
gem "pry", ">= 0.14.2"
|
19
|
+
gem "pry-byebug", ">= 3.10.1"
|
20
|
+
end
|
21
|
+
|
22
|
+
gemspec path: "../"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Shoryuken
|
2
|
+
class DefaultExceptionHandler
|
3
|
+
extend Util
|
4
|
+
|
5
|
+
def self.call(exception, _queue, _sqs_msg)
|
6
|
+
logger.error { "Processor failed: #{exception.message}" }
|
7
|
+
logger.error { exception.backtrace.join("\n") } if exception.backtrace
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -18,12 +18,12 @@ module Shoryuken
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def setup_options
|
21
|
+
initialize_rails if load_rails?
|
21
22
|
initialize_options
|
22
23
|
initialize_logger
|
23
24
|
end
|
24
25
|
|
25
26
|
def load
|
26
|
-
load_rails if Shoryuken.options[:rails]
|
27
27
|
prefix_active_job_queue_names
|
28
28
|
parse_queues
|
29
29
|
require_workers
|
@@ -55,7 +55,7 @@ module Shoryuken
|
|
55
55
|
Shoryuken.logger.level = Logger::DEBUG if Shoryuken.options[:verbose]
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
58
|
+
def initialize_rails
|
59
59
|
# Adapted from: https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/cli.rb
|
60
60
|
|
61
61
|
require 'rails'
|
@@ -70,6 +70,13 @@ module Shoryuken
|
|
70
70
|
::Rails.application.config.eager_load = true
|
71
71
|
end
|
72
72
|
end
|
73
|
+
::Rails::Application.initializer 'shoryuken.set_reloader_hook' do |app|
|
74
|
+
Shoryuken.reloader = proc do |&block|
|
75
|
+
app.reloader.wrap do
|
76
|
+
block.call
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
73
80
|
if Shoryuken.active_job?
|
74
81
|
require 'shoryuken/extensions/active_job_extensions'
|
75
82
|
require 'shoryuken/extensions/active_job_adapter'
|
@@ -79,6 +86,10 @@ module Shoryuken
|
|
79
86
|
end
|
80
87
|
end
|
81
88
|
|
89
|
+
def load_rails?
|
90
|
+
options[:rails]
|
91
|
+
end
|
92
|
+
|
82
93
|
def prefix_active_job_queue_name(queue_name, weight)
|
83
94
|
return [queue_name, weight] if queue_name.start_with?('https://', 'arn:')
|
84
95
|
|
@@ -159,9 +170,17 @@ module Shoryuken
|
|
159
170
|
|
160
171
|
return if non_existent_queues.none?
|
161
172
|
|
173
|
+
# NOTE: HEREDOC's ~ operator removes indents, but is only available Ruby 2.3+
|
174
|
+
# See github PR: https://github.com/ruby-shoryuken/shoryuken/pull/691#issuecomment-1007653595
|
175
|
+
error_msg = <<-MSG.gsub(/^\s+/, '')
|
176
|
+
The specified queue(s) #{non_existent_queues.join(', ')} do not exist.
|
177
|
+
Try 'shoryuken sqs create QUEUE-NAME' for creating a queue with default settings.
|
178
|
+
It's also possible that you don't have permission to access the specified queues.
|
179
|
+
MSG
|
180
|
+
|
162
181
|
fail(
|
163
182
|
ArgumentError,
|
164
|
-
|
183
|
+
error_msg
|
165
184
|
)
|
166
185
|
end
|
167
186
|
|
@@ -66,8 +66,11 @@ module ActiveJob
|
|
66
66
|
}
|
67
67
|
|
68
68
|
if queue.fifo?
|
69
|
-
# See https://github.com/
|
70
|
-
|
69
|
+
# See https://github.com/ruby-shoryuken/shoryuken/issues/457 and
|
70
|
+
# https://github.com/ruby-shoryuken/shoryuken/pull/750#issuecomment-1781317929
|
71
|
+
msg[:message_deduplication_id] = Digest::SHA256.hexdigest(
|
72
|
+
JSON.dump(body.except('job_id', 'enqueued_at'))
|
73
|
+
)
|
71
74
|
end
|
72
75
|
|
73
76
|
msg.merge(job_params.except(:message_attributes))
|
@@ -11,7 +11,7 @@ module Shoryuken
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
# Initializes SQS SendMessage parameters on instances of
|
14
|
+
# Initializes SQS SendMessage parameters on instances of ActiveJob::Base
|
15
15
|
# to the empty hash, and populates it whenever `#enqueue` is called, such
|
16
16
|
# as when using ActiveJob::Base.set.
|
17
17
|
module SQSSendMessageParametersSupport
|
data/lib/shoryuken/launcher.rb
CHANGED
@@ -16,11 +16,13 @@ module Shoryuken
|
|
16
16
|
def stop!
|
17
17
|
initiate_stop
|
18
18
|
|
19
|
-
|
19
|
+
# Don't await here so the timeout below is not delayed
|
20
|
+
stop_new_dispatching
|
20
21
|
|
21
|
-
|
22
|
+
executor.shutdown
|
23
|
+
executor.kill unless executor.wait_for_termination(Shoryuken.options[:timeout])
|
22
24
|
|
23
|
-
|
25
|
+
fire_event(:stopped)
|
24
26
|
end
|
25
27
|
|
26
28
|
def stop
|
@@ -28,8 +30,13 @@ module Shoryuken
|
|
28
30
|
|
29
31
|
initiate_stop
|
30
32
|
|
33
|
+
stop_new_dispatching
|
34
|
+
await_dispatching_in_progress
|
35
|
+
|
31
36
|
executor.shutdown
|
32
37
|
executor.wait_for_termination
|
38
|
+
|
39
|
+
fire_event(:stopped)
|
33
40
|
end
|
34
41
|
|
35
42
|
def healthy?
|
@@ -41,6 +48,14 @@ module Shoryuken
|
|
41
48
|
|
42
49
|
private
|
43
50
|
|
51
|
+
def stop_new_dispatching
|
52
|
+
@managers.each(&:stop_new_dispatching)
|
53
|
+
end
|
54
|
+
|
55
|
+
def await_dispatching_in_progress
|
56
|
+
@managers.each(&:await_dispatching_in_progress)
|
57
|
+
end
|
58
|
+
|
44
59
|
def executor
|
45
60
|
@_executor ||= Shoryuken.launcher_executor || Concurrent.global_io_executor
|
46
61
|
end
|
data/lib/shoryuken/logging.rb
CHANGED
@@ -30,11 +30,11 @@ module Shoryuken
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def self.logger
|
33
|
-
@logger
|
33
|
+
@logger ||= initialize_logger
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.logger=(log)
|
37
|
-
@logger = (log
|
37
|
+
@logger = (log || Logger.new('/dev/null'))
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/lib/shoryuken/manager.rb
CHANGED
@@ -9,13 +9,15 @@ module Shoryuken
|
|
9
9
|
attr_reader :group
|
10
10
|
|
11
11
|
def initialize(group, fetcher, polling_strategy, concurrency, executor)
|
12
|
-
@group
|
13
|
-
@fetcher
|
14
|
-
@polling_strategy
|
15
|
-
@max_processors
|
16
|
-
@busy_processors
|
17
|
-
@executor
|
18
|
-
@running
|
12
|
+
@group = group
|
13
|
+
@fetcher = fetcher
|
14
|
+
@polling_strategy = polling_strategy
|
15
|
+
@max_processors = concurrency
|
16
|
+
@busy_processors = Concurrent::AtomicFixnum.new(0)
|
17
|
+
@executor = executor
|
18
|
+
@running = Concurrent::AtomicBoolean.new(true)
|
19
|
+
@stop_new_dispatching = Concurrent::AtomicBoolean.new(false)
|
20
|
+
@dispatching_release_signal = ::Queue.new
|
19
21
|
end
|
20
22
|
|
21
23
|
def start
|
@@ -23,6 +25,17 @@ module Shoryuken
|
|
23
25
|
dispatch_loop
|
24
26
|
end
|
25
27
|
|
28
|
+
def stop_new_dispatching
|
29
|
+
@stop_new_dispatching.make_true
|
30
|
+
end
|
31
|
+
|
32
|
+
def await_dispatching_in_progress
|
33
|
+
# There might still be a dispatching on-going, as the response from SQS could take some time
|
34
|
+
# We don't want to stop the process before processing incoming messages, as they would stay "in-flight" for some time on SQS
|
35
|
+
# We use a queue, as the dispatch_loop is running on another thread, and this is a efficient way of communicating between threads.
|
36
|
+
@dispatching_release_signal.pop
|
37
|
+
end
|
38
|
+
|
26
39
|
def running?
|
27
40
|
@running.true? && @executor.running?
|
28
41
|
end
|
@@ -30,7 +43,10 @@ module Shoryuken
|
|
30
43
|
private
|
31
44
|
|
32
45
|
def dispatch_loop
|
33
|
-
|
46
|
+
if @stop_new_dispatching.true? || !running?
|
47
|
+
@dispatching_release_signal << 1
|
48
|
+
return
|
49
|
+
end
|
34
50
|
|
35
51
|
@executor.post { dispatch }
|
36
52
|
end
|
@@ -94,7 +110,6 @@ module Shoryuken
|
|
94
110
|
|
95
111
|
def dispatch_single_messages(queue)
|
96
112
|
messages = @fetcher.fetch(queue, ready)
|
97
|
-
|
98
113
|
@polling_strategy.messages_found(queue.name, messages.size)
|
99
114
|
messages.each { |message| assign(queue.name, message) }
|
100
115
|
end
|
@@ -5,7 +5,11 @@ module Shoryuken
|
|
5
5
|
def call(*_args)
|
6
6
|
yield
|
7
7
|
ensure
|
8
|
-
::ActiveRecord::
|
8
|
+
if ::ActiveRecord.version >= Gem::Version.new('7.1')
|
9
|
+
::ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
|
10
|
+
else
|
11
|
+
::ActiveRecord::Base.clear_active_connections!
|
12
|
+
end
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
data/lib/shoryuken/options.rb
CHANGED
@@ -11,22 +11,26 @@ module Shoryuken
|
|
11
11
|
dispatch: [],
|
12
12
|
utilization_update: [],
|
13
13
|
quiet: [],
|
14
|
-
shutdown: []
|
14
|
+
shutdown: [],
|
15
|
+
stopped: []
|
15
16
|
}
|
16
17
|
}.freeze
|
17
18
|
|
18
19
|
attr_accessor :active_job_queue_name_prefixing, :cache_visibility_timeout, :groups,
|
19
|
-
:launcher_executor,
|
20
|
-
:start_callback, :stop_callback, :worker_executor, :worker_registry
|
20
|
+
:launcher_executor, :reloader, :enable_reloading,
|
21
|
+
:start_callback, :stop_callback, :worker_executor, :worker_registry, :exception_handlers
|
21
22
|
attr_writer :default_worker_options, :sqs_client
|
22
23
|
attr_reader :sqs_client_receive_message_opts
|
23
24
|
|
24
25
|
def initialize
|
25
26
|
self.groups = {}
|
26
27
|
self.worker_registry = DefaultWorkerRegistry.new
|
28
|
+
self.exception_handlers = [DefaultExceptionHandler]
|
27
29
|
self.active_job_queue_name_prefixing = false
|
28
30
|
self.worker_executor = Worker::DefaultExecutor
|
29
31
|
self.cache_visibility_timeout = false
|
32
|
+
self.reloader = proc { |&block| block.call }
|
33
|
+
self.enable_reloading ||= false
|
30
34
|
# this is needed for keeping backward compatibility
|
31
35
|
@sqs_client_receive_message_opts ||= {}
|
32
36
|
end
|
@@ -63,10 +67,14 @@ module Shoryuken
|
|
63
67
|
Polling::WeightedRoundRobin
|
64
68
|
when 'StrictPriority'
|
65
69
|
Polling::StrictPriority
|
70
|
+
when String
|
71
|
+
begin
|
72
|
+
Object.const_get(strategy)
|
73
|
+
rescue NameError
|
74
|
+
raise ArgumentError, "#{strategy} is not a valid polling_strategy"
|
75
|
+
end
|
66
76
|
when Class
|
67
77
|
strategy
|
68
|
-
else
|
69
|
-
raise ArgumentError, "#{strategy} is not a valid polling_strategy"
|
70
78
|
end
|
71
79
|
end
|
72
80
|
|
@@ -134,7 +142,7 @@ module Shoryuken
|
|
134
142
|
end
|
135
143
|
|
136
144
|
# Register a block to run at a point in the Shoryuken lifecycle.
|
137
|
-
# :startup, :quiet or :
|
145
|
+
# :startup, :quiet, :shutdown or :stopped are valid events.
|
138
146
|
#
|
139
147
|
# Shoryuken.configure_server do |config|
|
140
148
|
# config.on(:shutdown) do
|
@@ -39,8 +39,10 @@ module Shoryuken
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def message_processed(queue)
|
42
|
-
|
43
|
-
|
42
|
+
if queue_paused?(queue)
|
43
|
+
logger.debug "Unpausing #{queue}"
|
44
|
+
@paused_until[queue] = Time.at 0
|
45
|
+
end
|
44
46
|
end
|
45
47
|
|
46
48
|
private
|
@@ -36,12 +36,10 @@ module Shoryuken
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def message_processed(queue)
|
39
|
-
|
39
|
+
paused_queue = @paused_queues.find { |_time, name| name == queue }
|
40
|
+
return unless paused_queue
|
40
41
|
|
41
|
-
|
42
|
-
@paused_queues.reject! { |_time, name| name == queue }
|
43
|
-
@queues << queue
|
44
|
-
@queues.uniq!
|
42
|
+
paused_queue[0] = Time.at 0
|
45
43
|
end
|
46
44
|
|
47
45
|
private
|
data/lib/shoryuken/processor.rb
CHANGED
@@ -14,16 +14,24 @@ module Shoryuken
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def process
|
17
|
-
|
17
|
+
worker_perform = proc do
|
18
|
+
return logger.error { "No worker found for #{queue}" } unless worker
|
19
|
+
Shoryuken::Logging.with_context("#{worker_name(worker.class, sqs_msg, body)}/#{queue}/#{sqs_msg.message_id}") do
|
20
|
+
worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
|
21
|
+
worker.perform(sqs_msg, body)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
18
25
|
|
19
|
-
Shoryuken
|
20
|
-
|
21
|
-
|
26
|
+
if Shoryuken.enable_reloading
|
27
|
+
Shoryuken.reloader.call do
|
28
|
+
worker_perform.call
|
22
29
|
end
|
30
|
+
else
|
31
|
+
worker_perform.call
|
23
32
|
end
|
24
33
|
rescue Exception => ex
|
25
|
-
|
26
|
-
logger.error { ex.backtrace.join("\n") } unless ex.backtrace.nil?
|
34
|
+
Array(Shoryuken.exception_handlers).each { |handler| handler.call(ex, queue, sqs_msg) }
|
27
35
|
|
28
36
|
raise
|
29
37
|
end
|
data/lib/shoryuken/queue.rb
CHANGED
@@ -21,9 +21,10 @@ module Shoryuken
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def delete_messages(options)
|
24
|
-
client.delete_message_batch(
|
24
|
+
failed_messages = client.delete_message_batch(
|
25
25
|
options.merge(queue_url: url)
|
26
|
-
).failed
|
26
|
+
).failed || []
|
27
|
+
failed_messages.any? do |failure|
|
27
28
|
logger.error do
|
28
29
|
"Could not delete #{failure.id}, code: '#{failure.code}', message: '#{failure.message}', sender_fault: #{failure.sender_fault}"
|
29
30
|
end
|
@@ -43,7 +44,8 @@ module Shoryuken
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def receive_messages(options)
|
46
|
-
client.receive_message(options.merge(queue_url: url)).messages
|
47
|
+
messages = client.receive_message(options.merge(queue_url: url)).messages || []
|
48
|
+
messages.map { |m| Message.new(client, self, m) }
|
47
49
|
end
|
48
50
|
|
49
51
|
def fifo?
|
data/lib/shoryuken/runner.rb
CHANGED
data/lib/shoryuken/version.rb
CHANGED
data/lib/shoryuken.rb
CHANGED
@@ -23,6 +23,7 @@ require 'shoryuken/worker/default_executor'
|
|
23
23
|
require 'shoryuken/worker/inline_executor'
|
24
24
|
require 'shoryuken/worker_registry'
|
25
25
|
require 'shoryuken/default_worker_registry'
|
26
|
+
require 'shoryuken/default_exception_handler'
|
26
27
|
require 'shoryuken/middleware/chain'
|
27
28
|
require 'shoryuken/middleware/server/auto_delete'
|
28
29
|
Shoryuken::Middleware::Server.autoload :AutoExtendVisibility, 'shoryuken/middleware/server/auto_extend_visibility'
|
@@ -73,6 +74,8 @@ module Shoryuken
|
|
73
74
|
:sqs_client=,
|
74
75
|
:sqs_client_receive_message_opts,
|
75
76
|
:sqs_client_receive_message_opts=,
|
77
|
+
:exception_handlers,
|
78
|
+
:exception_handlers=,
|
76
79
|
:options,
|
77
80
|
:logger,
|
78
81
|
:register_worker,
|
@@ -88,6 +91,10 @@ module Shoryuken
|
|
88
91
|
:on,
|
89
92
|
:cache_visibility_timeout?,
|
90
93
|
:cache_visibility_timeout=,
|
94
|
+
:reloader,
|
95
|
+
:reloader=,
|
96
|
+
:enable_reloading,
|
97
|
+
:enable_reloading=,
|
91
98
|
:delay
|
92
99
|
)
|
93
100
|
end
|
data/shoryuken.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ['Pablo Cantero']
|
10
10
|
spec.email = ['pablo@pablocantero.com']
|
11
11
|
spec.description = spec.summary = 'Shoryuken is a super efficient AWS SQS thread based message processor'
|
12
|
-
spec.homepage = 'https://github.com/
|
12
|
+
spec.homepage = 'https://github.com/ruby-shoryuken/shoryuken'
|
13
13
|
spec.license = 'LGPL-3.0'
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -42,9 +42,11 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
42
42
|
context 'when fifo' do
|
43
43
|
let(:fifo) { true }
|
44
44
|
|
45
|
-
it 'does not include job_id in the deduplication_id' do
|
45
|
+
it 'does not include job_id and enqueued_at in the deduplication_id' do
|
46
46
|
expect(queue).to receive(:send_message) do |hash|
|
47
|
-
message_deduplication_id = Digest::SHA256.hexdigest(
|
47
|
+
message_deduplication_id = Digest::SHA256.hexdigest(
|
48
|
+
JSON.dump(job.serialize.except('job_id', 'enqueued_at'))
|
49
|
+
)
|
48
50
|
|
49
51
|
expect(hash[:message_deduplication_id]).to eq(message_deduplication_id)
|
50
52
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/BlockLength
|
4
|
+
RSpec.describe Shoryuken::DefaultExceptionHandler do
|
5
|
+
class CustomErrorHandler
|
6
|
+
extend Shoryuken::Util
|
7
|
+
|
8
|
+
def self.call(_ex, queue, _msg)
|
9
|
+
logger.error("#{queue.to_s} failed to process the message")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
Shoryuken.worker_executor = Shoryuken::Worker::InlineExecutor
|
15
|
+
allow(manager).to receive(:async).and_return(manager)
|
16
|
+
allow(manager).to receive(:real_thread)
|
17
|
+
allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
|
18
|
+
end
|
19
|
+
|
20
|
+
after do
|
21
|
+
Shoryuken.worker_executor = Shoryuken::Worker::DefaultExecutor
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:manager) { double Shoryuken::Manager }
|
25
|
+
let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 30 }
|
26
|
+
let(:queue) { 'default' }
|
27
|
+
|
28
|
+
let(:sqs_msg) do
|
29
|
+
double(
|
30
|
+
Shoryuken::Message,
|
31
|
+
queue_url: queue,
|
32
|
+
body: 'test',
|
33
|
+
message_attributes: {},
|
34
|
+
message_id: SecureRandom.uuid,
|
35
|
+
receipt_handle: SecureRandom.uuid
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
subject { Shoryuken::Processor.new(queue, sqs_msg) }
|
40
|
+
|
41
|
+
context "with default handler" do
|
42
|
+
before do
|
43
|
+
Shoryuken.exception_handlers = described_class
|
44
|
+
end
|
45
|
+
|
46
|
+
it "logs an error message" do
|
47
|
+
expect(Shoryuken::Logging.logger).to receive(:error).twice
|
48
|
+
|
49
|
+
allow_any_instance_of(TestWorker).to receive(:perform).and_raise(StandardError, "error")
|
50
|
+
allow(sqs_msg).to receive(:body)
|
51
|
+
|
52
|
+
expect { subject.process }.to raise_error(StandardError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with custom handler" do
|
57
|
+
before do
|
58
|
+
Shoryuken.exception_handlers = [described_class, CustomErrorHandler]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "logs default and custom error messages" do
|
62
|
+
expect(Shoryuken::Logging.logger).to receive(:error).twice
|
63
|
+
expect(Shoryuken::Logging.logger).to receive(:error).with("default failed to process the message").once
|
64
|
+
|
65
|
+
allow_any_instance_of(TestWorker).to receive(:perform).and_raise(StandardError, "error")
|
66
|
+
allow(sqs_msg).to receive(:body)
|
67
|
+
|
68
|
+
expect { subject.process }.to raise_error(StandardError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|