shoryuken 5.3.0 → 6.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|