shoryuken 5.0.5 → 6.1.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 +65 -0
- data/.github/workflows/stale.yml +20 -0
- data/.gitignore +1 -1
- data/.reek.yml +5 -0
- data/.rubocop.yml +1 -1
- data/Appraisals +42 -0
- data/CHANGELOG.md +114 -0
- data/Gemfile +8 -3
- data/README.md +41 -1
- data/Rakefile +15 -1
- data/bin/cli/sqs.rb +51 -6
- data/gemfiles/.gitignore +1 -0
- data/gemfiles/aws_sdk_core_2.gemfile +21 -0
- data/gemfiles/rails_4_2.gemfile +20 -0
- data/gemfiles/rails_5_2.gemfile +21 -0
- data/gemfiles/rails_6_0.gemfile +21 -0
- data/gemfiles/rails_6_1.gemfile +21 -0
- data/gemfiles/rails_7_0.gemfile +22 -0
- data/lib/shoryuken/default_exception_handler.rb +10 -0
- data/lib/shoryuken/environment_loader.rb +22 -4
- data/lib/shoryuken/extensions/active_job_adapter.rb +30 -20
- data/lib/shoryuken/extensions/active_job_extensions.rb +38 -0
- data/lib/shoryuken/launcher.rb +26 -3
- data/lib/shoryuken/logging.rb +2 -2
- data/lib/shoryuken/manager.rb +50 -14
- data/lib/shoryuken/message.rb +11 -28
- data/lib/shoryuken/options.rb +6 -3
- data/lib/shoryuken/polling/base.rb +2 -0
- data/lib/shoryuken/polling/strict_priority.rb +8 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +9 -0
- data/lib/shoryuken/processor.rb +1 -2
- data/lib/shoryuken/queue.rb +5 -3
- data/lib/shoryuken/runner.rb +4 -3
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken.rb +8 -0
- data/shoryuken.gemspec +1 -2
- data/spec/integration/launcher_spec.rb +29 -2
- data/spec/shared_examples_for_active_job.rb +230 -11
- data/spec/shoryuken/default_exception_handler_spec.rb +71 -0
- data/spec/shoryuken/environment_loader_spec.rb +62 -9
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +1 -1
- data/spec/shoryuken/extensions/active_job_base_spec.rb +84 -0
- data/spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb +4 -0
- data/spec/shoryuken/extensions/active_job_wrapper_spec.rb +20 -0
- data/spec/shoryuken/fetcher_spec.rb +12 -12
- data/spec/shoryuken/launcher_spec.rb +105 -0
- data/spec/shoryuken/manager_spec.rb +61 -1
- data/spec/shoryuken/polling/strict_priority_spec.rb +10 -0
- data/spec/shoryuken/polling/weighted_round_robin_spec.rb +35 -0
- data/spec/shoryuken/queue_spec.rb +10 -5
- data/spec/shoryuken/worker/default_executor_spec.rb +48 -48
- data/spec/shoryuken_spec.rb +9 -0
- data/spec/spec_helper.rb +7 -9
- metadata +32 -24
- data/.travis.yml +0 -30
- data/Gemfile.aws-sdk-core-v2 +0 -13
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'
|
@@ -45,6 +46,10 @@ module Shoryuken
|
|
45
46
|
@_shoryuken_options ||= Shoryuken::Options.new
|
46
47
|
end
|
47
48
|
|
49
|
+
def self.healthy?
|
50
|
+
Shoryuken::Runner.instance.healthy?
|
51
|
+
end
|
52
|
+
|
48
53
|
def_delegators(
|
49
54
|
:shoryuken_options,
|
50
55
|
:active_job?,
|
@@ -69,6 +74,8 @@ module Shoryuken
|
|
69
74
|
:sqs_client=,
|
70
75
|
:sqs_client_receive_message_opts,
|
71
76
|
:sqs_client_receive_message_opts=,
|
77
|
+
:exception_handlers,
|
78
|
+
:exception_handlers=,
|
72
79
|
:options,
|
73
80
|
:logger,
|
74
81
|
:register_worker,
|
@@ -89,6 +96,7 @@ module Shoryuken
|
|
89
96
|
end
|
90
97
|
|
91
98
|
if Shoryuken.active_job?
|
99
|
+
require 'shoryuken/extensions/active_job_extensions'
|
92
100
|
require 'shoryuken/extensions/active_job_adapter'
|
93
101
|
require 'shoryuken/extensions/active_job_concurrent_send_adapter'
|
94
102
|
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")
|
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ['lib']
|
19
19
|
|
20
20
|
spec.add_development_dependency 'dotenv'
|
21
|
-
spec.add_development_dependency 'pry-byebug', '3.9.0'
|
22
21
|
spec.add_development_dependency 'rake'
|
23
22
|
spec.add_development_dependency 'rspec'
|
24
23
|
|
@@ -4,10 +4,37 @@ require 'shoryuken/launcher'
|
|
4
4
|
require 'securerandom'
|
5
5
|
|
6
6
|
RSpec.describe Shoryuken::Launcher do
|
7
|
-
|
7
|
+
let(:sqs_client) do
|
8
|
+
Aws::SQS::Client.new(
|
9
|
+
region: 'us-east-1',
|
10
|
+
endpoint: 'http://localhost:5000',
|
11
|
+
access_key_id: 'fake',
|
12
|
+
secret_access_key: 'fake'
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:executor) do
|
17
|
+
# We can't use Concurrent.global_io_executor in these tests since once you
|
18
|
+
# shut down a thread pool, you can't start it back up. Instead, we create
|
19
|
+
# one new thread pool executor for each spec. We use a new
|
20
|
+
# CachedThreadPool, since that most closely resembles
|
21
|
+
# Concurrent.global_io_executor
|
22
|
+
Concurrent::CachedThreadPool.new auto_terminate: true
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'Consuming messages' do
|
8
26
|
before do
|
9
27
|
Aws.config[:stub_responses] = false
|
10
|
-
|
28
|
+
|
29
|
+
allow(Shoryuken).to receive(:launcher_executor).and_return(executor)
|
30
|
+
|
31
|
+
Shoryuken.configure_client do |config|
|
32
|
+
config.sqs_client = sqs_client
|
33
|
+
end
|
34
|
+
|
35
|
+
Shoryuken.configure_server do |config|
|
36
|
+
config.sqs_client = sqs_client
|
37
|
+
end
|
11
38
|
|
12
39
|
StandardWorker.received_messages = 0
|
13
40
|
|
@@ -1,36 +1,52 @@
|
|
1
|
+
require 'active_job'
|
2
|
+
require 'shoryuken/extensions/active_job_extensions'
|
3
|
+
|
4
|
+
# Stand-in for a job class specified by the user
|
5
|
+
class TestJob < ActiveJob::Base; end
|
6
|
+
|
1
7
|
# rubocop:disable Metrics/BlockLength
|
2
8
|
RSpec.shared_examples 'active_job_adapters' do
|
3
|
-
let(:
|
9
|
+
let(:job_sqs_send_message_parameters) { {} }
|
10
|
+
let(:job) do
|
11
|
+
job = TestJob.new
|
12
|
+
job.sqs_send_message_parameters = job_sqs_send_message_parameters
|
13
|
+
job
|
14
|
+
end
|
4
15
|
let(:fifo) { false }
|
5
16
|
let(:queue) { double 'Queue', fifo?: fifo }
|
6
17
|
|
7
18
|
before do
|
8
19
|
allow(Shoryuken::Client).to receive(:queues).with(job.queue_name).and_return(queue)
|
9
|
-
allow(job).to receive(:serialize).and_return(
|
10
|
-
'job_class' => 'Worker',
|
11
|
-
'job_id' => job.id,
|
12
|
-
'queue_name' => job.queue_name,
|
13
|
-
'arguments' => nil,
|
14
|
-
'locale' => nil
|
15
|
-
)
|
16
20
|
end
|
17
21
|
|
18
22
|
describe '#enqueue' do
|
19
23
|
specify do
|
20
24
|
expect(queue).to receive(:send_message) do |hash|
|
21
25
|
expect(hash[:message_deduplication_id]).to_not be
|
26
|
+
expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(described_class::JobWrapper.to_s)
|
27
|
+
expect(hash[:message_attributes]['shoryuken_class'][:data_type]).to eq("String")
|
28
|
+
expect(hash[:message_attributes].keys).to eq(['shoryuken_class'])
|
22
29
|
end
|
23
30
|
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
24
31
|
|
25
32
|
subject.enqueue(job)
|
26
33
|
end
|
27
34
|
|
35
|
+
it "should mutate the job's sqs_send_message_parameters reference to match those sent to the queue" do
|
36
|
+
expect(queue).to receive(:send_message) do |options|
|
37
|
+
expect(options).to be(job.sqs_send_message_parameters)
|
38
|
+
end
|
39
|
+
subject.enqueue(job)
|
40
|
+
end
|
41
|
+
|
28
42
|
context 'when fifo' do
|
29
43
|
let(:fifo) { true }
|
30
44
|
|
31
|
-
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
|
32
46
|
expect(queue).to receive(:send_message) do |hash|
|
33
|
-
message_deduplication_id = Digest::SHA256.hexdigest(
|
47
|
+
message_deduplication_id = Digest::SHA256.hexdigest(
|
48
|
+
JSON.dump(job.serialize.except('job_id', 'enqueued_at'))
|
49
|
+
)
|
34
50
|
|
35
51
|
expect(hash[:message_deduplication_id]).to eq(message_deduplication_id)
|
36
52
|
end
|
@@ -38,6 +54,209 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
38
54
|
|
39
55
|
subject.enqueue(job)
|
40
56
|
end
|
57
|
+
|
58
|
+
context 'with message_deduplication_id' do
|
59
|
+
context 'when message_deduplication_id is specified in options' do
|
60
|
+
it 'should enqueue a message with the deduplication_id specified in options' do
|
61
|
+
expect(queue).to receive(:send_message) do |hash|
|
62
|
+
expect(hash[:message_deduplication_id]).to eq('options-dedupe-id')
|
63
|
+
end
|
64
|
+
subject.enqueue(job, message_deduplication_id: 'options-dedupe-id')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when message_deduplication_id is specified on the job' do
|
69
|
+
let(:job_sqs_send_message_parameters) { { message_deduplication_id: 'job-dedupe-id' } }
|
70
|
+
|
71
|
+
it 'should enqueue a message with the deduplication_id specified on the job' do
|
72
|
+
expect(queue).to receive(:send_message) do |hash|
|
73
|
+
expect(hash[:message_deduplication_id]).to eq('job-dedupe-id')
|
74
|
+
end
|
75
|
+
subject.enqueue job
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when message_deduplication_id is specified on the job and also in options' do
|
80
|
+
let(:job_sqs_send_message_parameters) { { message_deduplication_id: 'job-dedupe-id' } }
|
81
|
+
|
82
|
+
it 'should enqueue a message with the deduplication_id specified in options' do
|
83
|
+
expect(queue).to receive(:send_message) do |hash|
|
84
|
+
expect(hash[:message_deduplication_id]).to eq('options-dedupe-id')
|
85
|
+
end
|
86
|
+
subject.enqueue(job, message_deduplication_id: 'options-dedupe-id')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with message_group_id' do
|
93
|
+
context 'when message_group_id is specified in options' do
|
94
|
+
it 'should enqueue a message with the group_id specified in options' do
|
95
|
+
expect(queue).to receive(:send_message) do |hash|
|
96
|
+
expect(hash[:message_group_id]).to eq('options-group-id')
|
97
|
+
end
|
98
|
+
subject.enqueue(job, message_group_id: 'options-group-id')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when message_group_id is specified on the job' do
|
103
|
+
let(:job_sqs_send_message_parameters) { { message_group_id: 'job-group-id' } }
|
104
|
+
|
105
|
+
it 'should enqueue a message with the group_id specified on the job' do
|
106
|
+
expect(queue).to receive(:send_message) do |hash|
|
107
|
+
expect(hash[:message_group_id]).to eq('job-group-id')
|
108
|
+
end
|
109
|
+
subject.enqueue job
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when message_group_id is specified on the job and also in options' do
|
114
|
+
let(:job_sqs_send_message_parameters) { { message_group_id: 'job-group-id' } }
|
115
|
+
|
116
|
+
it 'should enqueue a message with the group_id specified in options' do
|
117
|
+
expect(queue).to receive(:send_message) do |hash|
|
118
|
+
expect(hash[:message_group_id]).to eq('options-group-id')
|
119
|
+
end
|
120
|
+
subject.enqueue(job, message_group_id: 'options-group-id')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'with additional message attributes' do
|
126
|
+
it 'should combine with activejob attributes' do
|
127
|
+
custom_message_attributes = {
|
128
|
+
'tracer_id' => {
|
129
|
+
string_value: SecureRandom.hex,
|
130
|
+
data_type: 'String'
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
expect(queue).to receive(:send_message) do |hash|
|
135
|
+
expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(described_class::JobWrapper.to_s)
|
136
|
+
expect(hash[:message_attributes]['shoryuken_class'][:data_type]).to eq("String")
|
137
|
+
expect(hash[:message_attributes]['tracer_id'][:string_value]).to eq(custom_message_attributes['tracer_id'][:string_value])
|
138
|
+
expect(hash[:message_attributes]['tracer_id'][:data_type]).to eq("String")
|
139
|
+
end
|
140
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
141
|
+
|
142
|
+
subject.enqueue(job, message_attributes: custom_message_attributes)
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'when message_attributes are specified on the job' do
|
146
|
+
let(:job_sqs_send_message_parameters) do
|
147
|
+
{
|
148
|
+
message_attributes: {
|
149
|
+
'tracer_id' => {
|
150
|
+
data_type: 'String',
|
151
|
+
string_value: 'job-value'
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should enqueue a message with the message_attributes specified on the job' do
|
158
|
+
expect(queue).to receive(:send_message) do |hash|
|
159
|
+
expect(hash[:message_attributes]['tracer_id']).to eq({ data_type: 'String', string_value: 'job-value' })
|
160
|
+
expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String', string_value: described_class::JobWrapper.to_s })
|
161
|
+
end
|
162
|
+
subject.enqueue job
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'when message_attributes are specified on the job and also in options' do
|
167
|
+
let(:job_sqs_send_message_parameters) do
|
168
|
+
{
|
169
|
+
message_attributes: {
|
170
|
+
'tracer_id' => {
|
171
|
+
data_type: 'String',
|
172
|
+
string_value: 'job-value'
|
173
|
+
}
|
174
|
+
}
|
175
|
+
}
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should enqueue a message with the message_attributes speficied in options' do
|
179
|
+
custom_message_attributes = {
|
180
|
+
'options_tracer_id' => {
|
181
|
+
string_value: 'options-value',
|
182
|
+
data_type: 'String'
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
expect(queue).to receive(:send_message) do |hash|
|
187
|
+
expect(hash[:message_attributes]['tracer_id']).to be_nil
|
188
|
+
expect(hash[:message_attributes]['options_tracer_id']).to eq({ data_type: 'String', string_value: 'options-value' })
|
189
|
+
expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String', string_value: described_class::JobWrapper.to_s })
|
190
|
+
end
|
191
|
+
subject.enqueue job, message_attributes: custom_message_attributes
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'with message_system_attributes' do
|
198
|
+
context 'when message_system_attributes are specified in options' do
|
199
|
+
it 'should enqueue a message with message_system_attributes specified in options' do
|
200
|
+
system_attributes = {
|
201
|
+
'AWSTraceHeader' => {
|
202
|
+
string_value: 'trace_id',
|
203
|
+
data_type: 'String'
|
204
|
+
}
|
205
|
+
}
|
206
|
+
expect(queue).to receive(:send_message) do |hash|
|
207
|
+
expect(hash[:message_system_attributes]['AWSTraceHeader'][:string_value]).to eq('trace_id')
|
208
|
+
expect(hash[:message_system_attributes]['AWSTraceHeader'][:data_type]).to eq('String')
|
209
|
+
end
|
210
|
+
subject.enqueue(job, message_system_attributes: system_attributes)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context 'when message_system_attributes are specified on the job' do
|
215
|
+
let(:job_sqs_send_message_parameters) do
|
216
|
+
{
|
217
|
+
message_system_attributes: {
|
218
|
+
'AWSTraceHeader' => {
|
219
|
+
string_value: 'job-value',
|
220
|
+
data_type: 'String'
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should enqueue a message with the message_system_attributes specified on the job' do
|
227
|
+
expect(queue).to receive(:send_message) do |hash|
|
228
|
+
expect(hash[:message_system_attributes]['AWSTraceHeader']).to eq({ data_type: 'String', string_value: 'job-value' })
|
229
|
+
end
|
230
|
+
subject.enqueue job
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context 'when message_system_attributes are specified on the job and also in options' do
|
235
|
+
let(:job_sqs_send_message_parameters) do
|
236
|
+
{
|
237
|
+
message_system_attributes: {
|
238
|
+
'job_trace_header' => {
|
239
|
+
string_value: 'job-value',
|
240
|
+
data_type: 'String'
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should enqueue a message with the message_system_attributes speficied in options' do
|
247
|
+
custom_message_attributes = {
|
248
|
+
'options_trace_header' => {
|
249
|
+
string_value: 'options-value',
|
250
|
+
data_type: 'String'
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
expect(queue).to receive(:send_message) do |hash|
|
255
|
+
expect(hash[:message_system_attributes]['job_trace_header']).to be_nil
|
256
|
+
expect(hash[:message_system_attributes]['options_trace_header']).to eq({ data_type: 'String', string_value: 'options-value' })
|
257
|
+
end
|
258
|
+
subject.enqueue job, message_system_attributes: custom_message_attributes
|
259
|
+
end
|
41
260
|
end
|
42
261
|
end
|
43
262
|
|
@@ -59,4 +278,4 @@ RSpec.shared_examples 'active_job_adapters' do
|
|
59
278
|
end
|
60
279
|
end
|
61
280
|
end
|
62
|
-
# rubocop:enable Metrics/BlockLength
|
281
|
+
# rubocop:enable Metrics/BlockLength
|
@@ -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
|
@@ -4,11 +4,41 @@ require 'active_job'
|
|
4
4
|
RSpec.describe Shoryuken::EnvironmentLoader do
|
5
5
|
subject { described_class.new({}) }
|
6
6
|
|
7
|
-
describe '#
|
7
|
+
describe '#load' do
|
8
8
|
before do
|
9
|
+
Shoryuken.groups.clear
|
10
|
+
# See issue: https://stackoverflow.com/a/63699568 for stubbing AWS errors
|
11
|
+
allow(Shoryuken::Client)
|
12
|
+
.to receive(:queues)
|
13
|
+
.with('stubbed_queue')
|
14
|
+
.and_raise(Aws::SQS::Errors::NonExistentQueue.new(nil, nil))
|
9
15
|
allow(subject).to receive(:load_rails)
|
10
16
|
allow(subject).to receive(:prefix_active_job_queue_names)
|
11
17
|
allow(subject).to receive(:require_workers)
|
18
|
+
allow(subject).to receive(:validate_workers)
|
19
|
+
allow(subject).to receive(:patch_deprecated_workers)
|
20
|
+
Shoryuken.options[:groups] = [['custom', { queues: ['stubbed_queue'] }]]
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when given queues don't exist" do
|
24
|
+
specify do
|
25
|
+
expect { subject.load }.to raise_error(
|
26
|
+
ArgumentError,
|
27
|
+
<<-MSG.gsub(/^\s+/, '')
|
28
|
+
The specified queue(s) stubbed_queue do not exist.
|
29
|
+
Try 'shoryuken sqs create QUEUE-NAME' for creating a queue with default settings.
|
30
|
+
It's also possible that you don't have permission to access the specified queues.
|
31
|
+
MSG
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#parse_queues loads default queues' do
|
38
|
+
before do
|
39
|
+
allow(subject).to receive(:initialize_rails)
|
40
|
+
allow(subject).to receive(:prefix_active_job_queue_names)
|
41
|
+
allow(subject).to receive(:require_workers)
|
12
42
|
allow(subject).to receive(:validate_queues)
|
13
43
|
allow(subject).to receive(:validate_workers)
|
14
44
|
allow(subject).to receive(:patch_deprecated_workers)
|
@@ -24,7 +54,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
24
54
|
|
25
55
|
describe '#parse_queues includes delay per groups' do
|
26
56
|
before do
|
27
|
-
allow(subject).to receive(:
|
57
|
+
allow(subject).to receive(:initialize_rails)
|
28
58
|
allow(subject).to receive(:prefix_active_job_queue_names)
|
29
59
|
allow(subject).to receive(:require_workers)
|
30
60
|
allow(subject).to receive(:validate_queues)
|
@@ -34,7 +64,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
34
64
|
|
35
65
|
specify do
|
36
66
|
Shoryuken.options[:queues] = ['queue1', 'queue2'] # default queues
|
37
|
-
Shoryuken.options[:groups] = [[
|
67
|
+
Shoryuken.options[:groups] = [['custom', { queues: ['queue3'], delay: 25 }]]
|
38
68
|
subject.load
|
39
69
|
|
40
70
|
expect(Shoryuken.groups['default'][:queues]).to eq(%w[queue1 queue2])
|
@@ -44,10 +74,9 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
44
74
|
end
|
45
75
|
end
|
46
76
|
|
47
|
-
|
48
77
|
describe '#prefix_active_job_queue_names' do
|
49
78
|
before do
|
50
|
-
allow(subject).to receive(:
|
79
|
+
allow(subject).to receive(:initialize_rails)
|
51
80
|
allow(subject).to receive(:require_workers)
|
52
81
|
allow(subject).to receive(:validate_queues)
|
53
82
|
allow(subject).to receive(:validate_workers)
|
@@ -57,11 +86,11 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
57
86
|
ActiveJob::Base.queue_name_delimiter = '_'
|
58
87
|
|
59
88
|
allow(Shoryuken).to receive(:active_job?).and_return(true)
|
60
|
-
end
|
61
89
|
|
62
|
-
specify do
|
63
90
|
Shoryuken.active_job_queue_name_prefixing = true
|
91
|
+
end
|
64
92
|
|
93
|
+
specify do
|
65
94
|
Shoryuken.options[:queues] = ['queue1', ['queue2', 2]]
|
66
95
|
|
67
96
|
Shoryuken.options[:groups] = {
|
@@ -73,10 +102,32 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
73
102
|
expect(Shoryuken.groups['default'][:queues]).to eq(%w[test_queue1 test_queue2 test_queue2])
|
74
103
|
expect(Shoryuken.groups['group1'][:queues]).to eq(%w[test_group1_queue1 test_group1_queue2])
|
75
104
|
end
|
105
|
+
|
106
|
+
it 'does not prefix url-based queues' do
|
107
|
+
Shoryuken.options[:queues] = ['https://example.com/test_queue1']
|
108
|
+
Shoryuken.options[:groups] = { 'group1' => { queues: ['https://example.com/test_group1_queue1'] } }
|
109
|
+
|
110
|
+
subject.load
|
111
|
+
|
112
|
+
expect(Shoryuken.groups['default'][:queues]).to(eq(['https://example.com/test_queue1']))
|
113
|
+
expect(Shoryuken.groups['group1'][:queues]).to(eq(['https://example.com/test_group1_queue1']))
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'does not prefix arn-based queues' do
|
117
|
+
Shoryuken.options[:queues] = ['arn:aws:sqs:fake-region-1:1234:test_queue1']
|
118
|
+
Shoryuken.options[:groups] = { 'group1' => { queues: ['arn:aws:sqs:fake-region-1:1234:test_group1_queue1'] } }
|
119
|
+
|
120
|
+
subject.load
|
121
|
+
|
122
|
+
expect(Shoryuken.groups['default'][:queues]).to(eq(['arn:aws:sqs:fake-region-1:1234:test_queue1']))
|
123
|
+
expect(Shoryuken.groups['group1'][:queues]).to(eq(['arn:aws:sqs:fake-region-1:1234:test_group1_queue1']))
|
124
|
+
end
|
76
125
|
end
|
126
|
+
|
77
127
|
describe "#setup_options" do
|
78
|
-
let
|
79
|
-
let
|
128
|
+
let(:cli_queues) { { "queue1" => 10, "queue2" => 20 } }
|
129
|
+
let(:config_queues) { [["queue1", 8], ["queue2", 4]] }
|
130
|
+
|
80
131
|
context "when given queues through config and CLI" do
|
81
132
|
specify do
|
82
133
|
allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
|
@@ -84,6 +135,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
84
135
|
expect(Shoryuken.options[:queues]).to eq(cli_queues)
|
85
136
|
end
|
86
137
|
end
|
138
|
+
|
87
139
|
context "when given queues through config only" do
|
88
140
|
specify do
|
89
141
|
allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
|
@@ -91,6 +143,7 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
91
143
|
expect(Shoryuken.options[:queues]).to eq(config_queues)
|
92
144
|
end
|
93
145
|
end
|
146
|
+
|
94
147
|
context "when given queues through CLI only" do
|
95
148
|
specify do
|
96
149
|
Shoryuken::EnvironmentLoader.setup_options(queues: cli_queues)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'shoryuken/extensions/active_job_adapter'
|
3
2
|
require 'shared_examples_for_active_job'
|
3
|
+
require 'shoryuken/extensions/active_job_adapter'
|
4
4
|
|
5
5
|
RSpec.describe ActiveJob::QueueAdapters::ShoryukenAdapter do
|
6
6
|
include_examples 'active_job_adapters'
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_job'
|
3
|
+
require 'shoryuken/extensions/active_job_extensions'
|
4
|
+
require 'shoryuken/extensions/active_job_adapter'
|
5
|
+
|
6
|
+
RSpec.describe ActiveJob::Base do
|
7
|
+
let(:queue_adapter) { ActiveJob::QueueAdapters::ShoryukenAdapter.new }
|
8
|
+
|
9
|
+
subject do
|
10
|
+
worker_class = Class.new(described_class)
|
11
|
+
Object.const_set :MyWorker, worker_class
|
12
|
+
worker_class.queue_adapter = queue_adapter
|
13
|
+
worker_class
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
Object.send :remove_const, :MyWorker
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#perform_now' do
|
21
|
+
it 'allows keyword args' do
|
22
|
+
collaborator = double 'worker collaborator'
|
23
|
+
subject.send(:define_method, :perform) do |**kwargs|
|
24
|
+
collaborator.foo(**kwargs)
|
25
|
+
end
|
26
|
+
expect(collaborator).to receive(:foo).with(foo: 'bar')
|
27
|
+
subject.perform_now foo: 'bar'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#perform_later' do
|
32
|
+
it 'calls enqueue on the adapter with the expected job' do
|
33
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
34
|
+
expect(job.arguments).to eq([1, 2])
|
35
|
+
end
|
36
|
+
|
37
|
+
subject.perform_later 1, 2
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'passes message_group_id to the queue_adapter' do
|
41
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
42
|
+
expect(job.sqs_send_message_parameters[:message_group_id]).to eq('group-2')
|
43
|
+
end
|
44
|
+
|
45
|
+
subject.set(message_group_id: 'group-2').perform_later 1, 2
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'passes message_deduplication_id to the queue_adapter' do
|
49
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
50
|
+
expect(job.sqs_send_message_parameters[:message_deduplication_id]).to eq('dedupe-id')
|
51
|
+
end
|
52
|
+
|
53
|
+
subject.set(message_deduplication_id: 'dedupe-id').perform_later 1, 2
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'passes message_attributes to the queue_adapter' do
|
57
|
+
message_attributes = {
|
58
|
+
'custom_tracing_id' => {
|
59
|
+
string_value: 'value',
|
60
|
+
data_type: 'String'
|
61
|
+
}
|
62
|
+
}
|
63
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
64
|
+
expect(job.sqs_send_message_parameters[:message_attributes]).to eq(message_attributes)
|
65
|
+
end
|
66
|
+
|
67
|
+
subject.set(message_attributes: message_attributes).perform_later 1, 2
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'passes message_system_attributes to the queue_adapter' do
|
71
|
+
message_system_attributes = {
|
72
|
+
'AWSTraceHeader' => {
|
73
|
+
string_value: 'trace_id',
|
74
|
+
data_type: 'String'
|
75
|
+
}
|
76
|
+
}
|
77
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
78
|
+
expect(job.sqs_send_message_parameters[:message_system_attributes]).to eq(message_system_attributes)
|
79
|
+
end
|
80
|
+
|
81
|
+
subject.set(message_system_attributes: message_system_attributes).perform_later 1, 2
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -10,6 +10,10 @@ RSpec.describe ActiveJob::QueueAdapters::ShoryukenConcurrentSendAdapter do
|
|
10
10
|
let(:error_handler) { -> {} }
|
11
11
|
let(:success_handler) { -> {} }
|
12
12
|
|
13
|
+
before do
|
14
|
+
allow(Concurrent).to receive(:global_io_executor).and_return(Concurrent::ImmediateExecutor.new)
|
15
|
+
end
|
16
|
+
|
13
17
|
subject { described_class.new(success_handler, error_handler) }
|
14
18
|
|
15
19
|
context 'when success' do
|