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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +17 -0
  3. data/.devcontainer/base.Dockerfile +43 -0
  4. data/.devcontainer/devcontainer.json +35 -0
  5. data/.github/dependabot.yml +6 -0
  6. data/.github/workflows/specs.yml +65 -0
  7. data/.github/workflows/stale.yml +20 -0
  8. data/.gitignore +1 -1
  9. data/.reek.yml +5 -0
  10. data/.rubocop.yml +1 -1
  11. data/Appraisals +42 -0
  12. data/CHANGELOG.md +114 -0
  13. data/Gemfile +8 -3
  14. data/README.md +41 -1
  15. data/Rakefile +15 -1
  16. data/bin/cli/sqs.rb +51 -6
  17. data/gemfiles/.gitignore +1 -0
  18. data/gemfiles/aws_sdk_core_2.gemfile +21 -0
  19. data/gemfiles/rails_4_2.gemfile +20 -0
  20. data/gemfiles/rails_5_2.gemfile +21 -0
  21. data/gemfiles/rails_6_0.gemfile +21 -0
  22. data/gemfiles/rails_6_1.gemfile +21 -0
  23. data/gemfiles/rails_7_0.gemfile +22 -0
  24. data/lib/shoryuken/default_exception_handler.rb +10 -0
  25. data/lib/shoryuken/environment_loader.rb +22 -4
  26. data/lib/shoryuken/extensions/active_job_adapter.rb +30 -20
  27. data/lib/shoryuken/extensions/active_job_extensions.rb +38 -0
  28. data/lib/shoryuken/launcher.rb +26 -3
  29. data/lib/shoryuken/logging.rb +2 -2
  30. data/lib/shoryuken/manager.rb +50 -14
  31. data/lib/shoryuken/message.rb +11 -28
  32. data/lib/shoryuken/options.rb +6 -3
  33. data/lib/shoryuken/polling/base.rb +2 -0
  34. data/lib/shoryuken/polling/strict_priority.rb +8 -0
  35. data/lib/shoryuken/polling/weighted_round_robin.rb +9 -0
  36. data/lib/shoryuken/processor.rb +1 -2
  37. data/lib/shoryuken/queue.rb +5 -3
  38. data/lib/shoryuken/runner.rb +4 -3
  39. data/lib/shoryuken/version.rb +1 -1
  40. data/lib/shoryuken.rb +8 -0
  41. data/shoryuken.gemspec +1 -2
  42. data/spec/integration/launcher_spec.rb +29 -2
  43. data/spec/shared_examples_for_active_job.rb +230 -11
  44. data/spec/shoryuken/default_exception_handler_spec.rb +71 -0
  45. data/spec/shoryuken/environment_loader_spec.rb +62 -9
  46. data/spec/shoryuken/extensions/active_job_adapter_spec.rb +1 -1
  47. data/spec/shoryuken/extensions/active_job_base_spec.rb +84 -0
  48. data/spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb +4 -0
  49. data/spec/shoryuken/extensions/active_job_wrapper_spec.rb +20 -0
  50. data/spec/shoryuken/fetcher_spec.rb +12 -12
  51. data/spec/shoryuken/launcher_spec.rb +105 -0
  52. data/spec/shoryuken/manager_spec.rb +61 -1
  53. data/spec/shoryuken/polling/strict_priority_spec.rb +10 -0
  54. data/spec/shoryuken/polling/weighted_round_robin_spec.rb +35 -0
  55. data/spec/shoryuken/queue_spec.rb +10 -5
  56. data/spec/shoryuken/worker/default_executor_spec.rb +48 -48
  57. data/spec/shoryuken_spec.rb +9 -0
  58. data/spec/spec_helper.rb +7 -9
  59. metadata +32 -24
  60. data/.travis.yml +0 -30
  61. 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/phstc/shoryuken'
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
- describe 'Consuming messages', slow: true do
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
- Aws.config[:region] = 'us-east-1'
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(:job) { double 'Job', id: '123', queue_name: 'queue' }
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(JSON.dump(job.serialize.except('job_id')))
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 '#parse_queues loads default queues' do
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(:load_rails)
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] = [[ 'custom', { queues: ['queue3'], delay: 25 }]]
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(:load_rails)
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 (:cli_queues) { { "queue1"=> 10, "queue2" => 20 } }
79
- let (:config_queues) { [["queue1", 8], ["queue2", 4]] }
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