shoryuken 5.0.1 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +12 -0
- data/.github/workflows/specs.yml +57 -0
- data/.reek.yml +5 -0
- data/.rubocop.yml +3 -6
- data/Appraisals +28 -0
- data/CHANGELOG.md +44 -0
- data/Gemfile +3 -1
- data/README.md +19 -3
- data/Rakefile +3 -0
- data/bin/cli/sqs.rb +9 -1
- data/gemfiles/.gitignore +1 -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/lib/shoryuken/default_worker_registry.rb +1 -1
- data/lib/shoryuken/environment_loader.rb +5 -13
- data/lib/shoryuken/extensions/active_job_adapter.rb +21 -16
- data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +50 -0
- data/lib/shoryuken/extensions/active_job_extensions.rb +37 -0
- data/lib/shoryuken/manager.rb +10 -4
- data/lib/shoryuken/options.rb +4 -2
- data/lib/shoryuken/polling/base.rb +2 -0
- data/lib/shoryuken/polling/strict_priority.rb +6 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +11 -0
- data/lib/shoryuken/queue.rb +39 -11
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken.rb +5 -1
- data/shoryuken.gemspec +0 -1
- data/spec/shared_examples_for_active_job.rb +279 -0
- data/spec/shoryuken/environment_loader_spec.rb +24 -0
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +2 -59
- data/spec/shoryuken/extensions/active_job_base_spec.rb +73 -0
- data/spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb +35 -0
- data/spec/shoryuken/manager_spec.rb +24 -0
- data/spec/shoryuken/polling/strict_priority_spec.rb +10 -0
- data/spec/shoryuken/polling/weighted_round_robin_spec.rb +10 -0
- data/spec/shoryuken/queue_spec.rb +23 -0
- data/spec/spec_helper.rb +6 -6
- metadata +23 -22
- data/.travis.yml +0 -34
|
@@ -35,10 +35,20 @@ module Shoryuken
|
|
|
35
35
|
unparse_queues(@queues)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
def message_processed(queue)
|
|
39
|
+
return if @paused_queues.empty?
|
|
40
|
+
|
|
41
|
+
logger.debug "Unpausing #{queue}"
|
|
42
|
+
@paused_queues.reject! { |_time, name| name == queue }
|
|
43
|
+
@queues << queue
|
|
44
|
+
@queues.uniq!
|
|
45
|
+
end
|
|
46
|
+
|
|
38
47
|
private
|
|
39
48
|
|
|
40
49
|
def pause(queue)
|
|
41
50
|
return unless @queues.delete(queue)
|
|
51
|
+
|
|
42
52
|
@paused_queues << [Time.now + delay, queue]
|
|
43
53
|
logger.debug "Paused #{queue}"
|
|
44
54
|
end
|
|
@@ -46,6 +56,7 @@ module Shoryuken
|
|
|
46
56
|
def unpause_queues
|
|
47
57
|
return if @paused_queues.empty?
|
|
48
58
|
return if Time.now < @paused_queues.first[0]
|
|
59
|
+
|
|
49
60
|
pause = @paused_queues.shift
|
|
50
61
|
@queues << pause[1]
|
|
51
62
|
logger.debug "Unpaused #{pause[1]}"
|
data/lib/shoryuken/queue.rb
CHANGED
|
@@ -8,9 +8,9 @@ module Shoryuken
|
|
|
8
8
|
|
|
9
9
|
attr_accessor :name, :client, :url
|
|
10
10
|
|
|
11
|
-
def initialize(client,
|
|
11
|
+
def initialize(client, name_or_url_or_arn)
|
|
12
12
|
self.client = client
|
|
13
|
-
set_name_and_url(
|
|
13
|
+
set_name_and_url(name_or_url_or_arn)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def visibility_timeout
|
|
@@ -50,32 +50,60 @@ module Shoryuken
|
|
|
50
50
|
# Make sure the memoization work with boolean to avoid multiple calls to SQS
|
|
51
51
|
# see https://github.com/phstc/shoryuken/pull/529
|
|
52
52
|
return @_fifo if defined?(@_fifo)
|
|
53
|
+
|
|
53
54
|
@_fifo = queue_attributes.attributes[FIFO_ATTR] == 'true'
|
|
55
|
+
@_fifo
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
private
|
|
57
59
|
|
|
58
|
-
def
|
|
60
|
+
def initialize_fifo_attribute
|
|
61
|
+
# calling fifo? will also initialize it
|
|
62
|
+
fifo?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def set_by_name(name) # rubocop:disable Naming/AccessorMethodName
|
|
59
66
|
self.name = name
|
|
60
67
|
self.url = client.get_queue_url(queue_name: name).queue_url
|
|
61
68
|
end
|
|
62
69
|
|
|
63
|
-
def set_by_url(url)
|
|
70
|
+
def set_by_url(url) # rubocop:disable Naming/AccessorMethodName
|
|
64
71
|
self.name = url.split('/').last
|
|
65
72
|
self.url = url
|
|
66
73
|
end
|
|
67
74
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
def arn_to_url(arn_str)
|
|
76
|
+
*, region, account_id, resource = arn_str.split(':')
|
|
77
|
+
|
|
78
|
+
required = [region, account_id, resource].map(&:to_s)
|
|
79
|
+
valid = required.none?(&:empty?)
|
|
80
|
+
|
|
81
|
+
abort "Invalid ARN: #{arn_str}. A valid ARN must include: region, account_id and resource." unless valid
|
|
82
|
+
|
|
83
|
+
"https://sqs.#{region}.amazonaws.com/#{account_id}/#{resource}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def set_name_and_url(name_or_url_or_arn) # rubocop:disable Naming/AccessorMethodName
|
|
87
|
+
if name_or_url_or_arn.include?('://')
|
|
88
|
+
set_by_url(name_or_url_or_arn)
|
|
89
|
+
|
|
90
|
+
# anticipate the fifo? checker for validating the queue URL
|
|
91
|
+
initialize_fifo_attribute
|
|
92
|
+
return
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if name_or_url_or_arn.start_with?('arn:')
|
|
96
|
+
url = arn_to_url(name_or_url_or_arn)
|
|
97
|
+
set_by_url(url)
|
|
71
98
|
|
|
72
99
|
# anticipate the fifo? checker for validating the queue URL
|
|
73
|
-
|
|
100
|
+
initialize_fifo_attribute
|
|
101
|
+
return
|
|
74
102
|
end
|
|
75
103
|
|
|
76
|
-
set_by_name(
|
|
77
|
-
rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue =>
|
|
78
|
-
raise
|
|
104
|
+
set_by_name(name_or_url_or_arn)
|
|
105
|
+
rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => e
|
|
106
|
+
raise e, "The specified queue #{name_or_url_or_arn} does not exist."
|
|
79
107
|
end
|
|
80
108
|
|
|
81
109
|
def queue_attributes
|
data/lib/shoryuken/version.rb
CHANGED
data/lib/shoryuken.rb
CHANGED
|
@@ -88,4 +88,8 @@ module Shoryuken
|
|
|
88
88
|
)
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
if Shoryuken.active_job?
|
|
92
|
+
require 'shoryuken/extensions/active_job_extensions'
|
|
93
|
+
require 'shoryuken/extensions/active_job_adapter'
|
|
94
|
+
require 'shoryuken/extensions/active_job_concurrent_send_adapter'
|
|
95
|
+
end
|
data/shoryuken.gemspec
CHANGED
|
@@ -0,0 +1,279 @@
|
|
|
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
|
+
|
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
|
8
|
+
RSpec.shared_examples 'active_job_adapters' do
|
|
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
|
|
15
|
+
let(:fifo) { false }
|
|
16
|
+
let(:queue) { double 'Queue', fifo?: fifo }
|
|
17
|
+
|
|
18
|
+
before do
|
|
19
|
+
allow(Shoryuken::Client).to receive(:queues).with(job.queue_name).and_return(queue)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe '#enqueue' do
|
|
23
|
+
specify do
|
|
24
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
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'])
|
|
29
|
+
end
|
|
30
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
|
31
|
+
|
|
32
|
+
subject.enqueue(job)
|
|
33
|
+
end
|
|
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
|
+
|
|
42
|
+
context 'when fifo' do
|
|
43
|
+
let(:fifo) { true }
|
|
44
|
+
|
|
45
|
+
it 'does not include job_id in the deduplication_id' do
|
|
46
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
47
|
+
message_deduplication_id = Digest::SHA256.hexdigest(JSON.dump(job.serialize.except('job_id')))
|
|
48
|
+
|
|
49
|
+
expect(hash[:message_deduplication_id]).to eq(message_deduplication_id)
|
|
50
|
+
end
|
|
51
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
|
52
|
+
|
|
53
|
+
subject.enqueue(job)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context 'with message_deduplication_id' do
|
|
57
|
+
context 'when message_deduplication_id is specified in options' do
|
|
58
|
+
it 'should enqueue a message with the deduplication_id specified in options' do
|
|
59
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
60
|
+
expect(hash[:message_deduplication_id]).to eq('options-dedupe-id')
|
|
61
|
+
end
|
|
62
|
+
subject.enqueue(job, message_deduplication_id: 'options-dedupe-id')
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context 'when message_deduplication_id is specified on the job' do
|
|
67
|
+
let(:job_sqs_send_message_parameters) { { message_deduplication_id: 'job-dedupe-id' } }
|
|
68
|
+
|
|
69
|
+
it 'should enqueue a message with the deduplication_id specified on the job' do
|
|
70
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
71
|
+
expect(hash[:message_deduplication_id]).to eq('job-dedupe-id')
|
|
72
|
+
end
|
|
73
|
+
subject.enqueue job
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context 'when message_deduplication_id is specified on the job and also in options' do
|
|
78
|
+
let(:job_sqs_send_message_parameters) { { message_deduplication_id: 'job-dedupe-id' } }
|
|
79
|
+
|
|
80
|
+
it 'should enqueue a message with the deduplication_id specified in options' do
|
|
81
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
82
|
+
expect(hash[:message_deduplication_id]).to eq('options-dedupe-id')
|
|
83
|
+
end
|
|
84
|
+
subject.enqueue(job, message_deduplication_id: 'options-dedupe-id')
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
context 'with message_group_id' do
|
|
91
|
+
context 'when message_group_id is specified in options' do
|
|
92
|
+
it 'should enqueue a message with the group_id specified in options' do
|
|
93
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
94
|
+
expect(hash[:message_group_id]).to eq('options-group-id')
|
|
95
|
+
end
|
|
96
|
+
subject.enqueue(job, message_group_id: 'options-group-id')
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context 'when message_group_id is specified on the job' do
|
|
101
|
+
let(:job_sqs_send_message_parameters) { { message_group_id: 'job-group-id' } }
|
|
102
|
+
|
|
103
|
+
it 'should enqueue a message with the group_id specified on the job' do
|
|
104
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
105
|
+
expect(hash[:message_group_id]).to eq('job-group-id')
|
|
106
|
+
end
|
|
107
|
+
subject.enqueue job
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context 'when message_group_id is specified on the job and also in options' do
|
|
112
|
+
let(:job_sqs_send_message_parameters) { { message_group_id: 'job-group-id' } }
|
|
113
|
+
|
|
114
|
+
it 'should enqueue a message with the group_id specified in options' do
|
|
115
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
116
|
+
expect(hash[:message_group_id]).to eq('options-group-id')
|
|
117
|
+
end
|
|
118
|
+
subject.enqueue(job, message_group_id: 'options-group-id')
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context 'with additional message attributes' do
|
|
124
|
+
it 'should combine with activejob attributes' do
|
|
125
|
+
custom_message_attributes = {
|
|
126
|
+
'tracer_id' => {
|
|
127
|
+
string_value: SecureRandom.hex,
|
|
128
|
+
data_type: 'String'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
133
|
+
expect(hash[:message_attributes]['shoryuken_class'][:string_value]).to eq(described_class::JobWrapper.to_s)
|
|
134
|
+
expect(hash[:message_attributes]['shoryuken_class'][:data_type]).to eq("String")
|
|
135
|
+
expect(hash[:message_attributes]['tracer_id'][:string_value]).to eq(custom_message_attributes['tracer_id'][:string_value])
|
|
136
|
+
expect(hash[:message_attributes]['tracer_id'][:data_type]).to eq("String")
|
|
137
|
+
end
|
|
138
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
|
139
|
+
|
|
140
|
+
subject.enqueue(job, message_attributes: custom_message_attributes)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context 'when message_attributes are specified on the job' do
|
|
144
|
+
let(:job_sqs_send_message_parameters) do
|
|
145
|
+
{
|
|
146
|
+
message_attributes: {
|
|
147
|
+
'tracer_id' => {
|
|
148
|
+
data_type: 'String',
|
|
149
|
+
string_value: 'job-value'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it 'should enqueue a message with the message_attributes specified on the job' do
|
|
156
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
157
|
+
expect(hash[:message_attributes]['tracer_id']).to eq({ data_type: 'String', string_value: 'job-value' })
|
|
158
|
+
expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String', string_value: described_class::JobWrapper.to_s })
|
|
159
|
+
end
|
|
160
|
+
subject.enqueue job
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context 'when message_attributes are specified on the job and also in options' do
|
|
165
|
+
let(:job_sqs_send_message_parameters) do
|
|
166
|
+
{
|
|
167
|
+
message_attributes: {
|
|
168
|
+
'tracer_id' => {
|
|
169
|
+
data_type: 'String',
|
|
170
|
+
string_value: 'job-value'
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'should enqueue a message with the message_attributes speficied in options' do
|
|
177
|
+
custom_message_attributes = {
|
|
178
|
+
'options_tracer_id' => {
|
|
179
|
+
string_value: 'options-value',
|
|
180
|
+
data_type: 'String'
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
185
|
+
expect(hash[:message_attributes]['tracer_id']).to be_nil
|
|
186
|
+
expect(hash[:message_attributes]['options_tracer_id']).to eq({ data_type: 'String', string_value: 'options-value' })
|
|
187
|
+
expect(hash[:message_attributes]['shoryuken_class']).to eq({ data_type: 'String', string_value: described_class::JobWrapper.to_s })
|
|
188
|
+
end
|
|
189
|
+
subject.enqueue job, message_attributes: custom_message_attributes
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
context 'with message_system_attributes' do
|
|
196
|
+
context 'when message_system_attributes are specified in options' do
|
|
197
|
+
it 'should enqueue a message with message_system_attributes specified in options' do
|
|
198
|
+
system_attributes = {
|
|
199
|
+
'AWSTraceHeader' => {
|
|
200
|
+
string_value: 'trace_id',
|
|
201
|
+
data_type: 'String'
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
205
|
+
expect(hash[:message_system_attributes]['AWSTraceHeader'][:string_value]).to eq('trace_id')
|
|
206
|
+
expect(hash[:message_system_attributes]['AWSTraceHeader'][:data_type]).to eq('String')
|
|
207
|
+
end
|
|
208
|
+
subject.enqueue(job, message_system_attributes: system_attributes)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
context 'when message_system_attributes are specified on the job' do
|
|
213
|
+
let(:job_sqs_send_message_parameters) do
|
|
214
|
+
{
|
|
215
|
+
message_system_attributes: {
|
|
216
|
+
'AWSTraceHeader' => {
|
|
217
|
+
string_value: 'job-value',
|
|
218
|
+
data_type: 'String'
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it 'should enqueue a message with the message_system_attributes specified on the job' do
|
|
225
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
226
|
+
expect(hash[:message_system_attributes]['AWSTraceHeader']).to eq({ data_type: 'String', string_value: 'job-value' })
|
|
227
|
+
end
|
|
228
|
+
subject.enqueue job
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
context 'when message_system_attributes are specified on the job and also in options' do
|
|
233
|
+
let(:job_sqs_send_message_parameters) do
|
|
234
|
+
{
|
|
235
|
+
message_system_attributes: {
|
|
236
|
+
'job_trace_header' => {
|
|
237
|
+
string_value: 'job-value',
|
|
238
|
+
data_type: 'String'
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it 'should enqueue a message with the message_system_attributes speficied in options' do
|
|
245
|
+
custom_message_attributes = {
|
|
246
|
+
'options_trace_header' => {
|
|
247
|
+
string_value: 'options-value',
|
|
248
|
+
data_type: 'String'
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
253
|
+
expect(hash[:message_system_attributes]['job_trace_header']).to be_nil
|
|
254
|
+
expect(hash[:message_system_attributes]['options_trace_header']).to eq({ data_type: 'String', string_value: 'options-value' })
|
|
255
|
+
end
|
|
256
|
+
subject.enqueue job, message_system_attributes: custom_message_attributes
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
describe '#enqueue_at' do
|
|
262
|
+
specify do
|
|
263
|
+
delay = 1
|
|
264
|
+
|
|
265
|
+
expect(queue).to receive(:send_message) do |hash|
|
|
266
|
+
expect(hash[:message_deduplication_id]).to_not be
|
|
267
|
+
expect(hash[:delay_seconds]).to eq(delay)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
|
271
|
+
|
|
272
|
+
# need to figure out what to require Time.current and N.minutes to remove the stub
|
|
273
|
+
allow(subject).to receive(:calculate_delay).and_return(delay)
|
|
274
|
+
|
|
275
|
+
subject.enqueue_at(job, nil)
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
# rubocop:enable Metrics/BlockLength
|
|
@@ -74,4 +74,28 @@ RSpec.describe Shoryuken::EnvironmentLoader do
|
|
|
74
74
|
expect(Shoryuken.groups['group1'][:queues]).to eq(%w[test_group1_queue1 test_group1_queue2])
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
|
+
describe "#setup_options" do
|
|
78
|
+
let (:cli_queues) { { "queue1"=> 10, "queue2" => 20 } }
|
|
79
|
+
let (:config_queues) { [["queue1", 8], ["queue2", 4]] }
|
|
80
|
+
context "when given queues through config and CLI" do
|
|
81
|
+
specify do
|
|
82
|
+
allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
|
|
83
|
+
Shoryuken::EnvironmentLoader.setup_options(queues: cli_queues)
|
|
84
|
+
expect(Shoryuken.options[:queues]).to eq(cli_queues)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
context "when given queues through config only" do
|
|
88
|
+
specify do
|
|
89
|
+
allow_any_instance_of(Shoryuken::EnvironmentLoader).to receive(:config_file_options).and_return({ queues: config_queues })
|
|
90
|
+
Shoryuken::EnvironmentLoader.setup_options({})
|
|
91
|
+
expect(Shoryuken.options[:queues]).to eq(config_queues)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
context "when given queues through CLI only" do
|
|
95
|
+
specify do
|
|
96
|
+
Shoryuken::EnvironmentLoader.setup_options(queues: cli_queues)
|
|
97
|
+
expect(Shoryuken.options[:queues]).to eq(cli_queues)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
77
101
|
end
|
|
@@ -1,64 +1,7 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require '
|
|
2
|
+
require 'shared_examples_for_active_job'
|
|
3
3
|
require 'shoryuken/extensions/active_job_adapter'
|
|
4
4
|
|
|
5
5
|
RSpec.describe ActiveJob::QueueAdapters::ShoryukenAdapter do
|
|
6
|
-
|
|
7
|
-
let(:fifo) { false }
|
|
8
|
-
let(:queue) { double 'Queue', fifo?: fifo }
|
|
9
|
-
|
|
10
|
-
before do
|
|
11
|
-
allow(Shoryuken::Client).to receive(:queues).with(job.queue_name).and_return(queue)
|
|
12
|
-
allow(job).to receive(:serialize).and_return(
|
|
13
|
-
'job_class' => 'Worker',
|
|
14
|
-
'job_id' => job.id,
|
|
15
|
-
'queue_name' => job.queue_name,
|
|
16
|
-
'arguments' => nil,
|
|
17
|
-
'locale' => nil
|
|
18
|
-
)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
describe '#enqueue' do
|
|
22
|
-
specify do
|
|
23
|
-
expect(queue).to receive(:send_message) do |hash|
|
|
24
|
-
expect(hash[:message_deduplication_id]).to_not be
|
|
25
|
-
end
|
|
26
|
-
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
|
27
|
-
|
|
28
|
-
subject.enqueue(job)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
context 'when fifo' do
|
|
32
|
-
let(:fifo) { true }
|
|
33
|
-
|
|
34
|
-
it 'does not include job_id in the deduplication_id' do
|
|
35
|
-
expect(queue).to receive(:send_message) do |hash|
|
|
36
|
-
message_deduplication_id = Digest::SHA256.hexdigest(JSON.dump(job.serialize.except('job_id')))
|
|
37
|
-
|
|
38
|
-
expect(hash[:message_deduplication_id]).to eq(message_deduplication_id)
|
|
39
|
-
end
|
|
40
|
-
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
|
41
|
-
|
|
42
|
-
subject.enqueue(job)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
describe '#enqueue_at' do
|
|
48
|
-
specify do
|
|
49
|
-
delay = 1
|
|
50
|
-
|
|
51
|
-
expect(queue).to receive(:send_message) do |hash|
|
|
52
|
-
expect(hash[:message_deduplication_id]).to_not be
|
|
53
|
-
expect(hash[:delay_seconds]).to eq(delay)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
expect(Shoryuken).to receive(:register_worker).with(job.queue_name, described_class::JobWrapper)
|
|
57
|
-
|
|
58
|
-
# need to figure out what to require Time.current and N.minutes to remove the stub
|
|
59
|
-
allow(subject).to receive(:calculate_delay).and_return(delay)
|
|
60
|
-
|
|
61
|
-
subject.enqueue_at(job, nil)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
6
|
+
include_examples 'active_job_adapters'
|
|
64
7
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
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_later' do
|
|
21
|
+
it 'calls enqueue on the adapter with the expected job' do
|
|
22
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
|
23
|
+
expect(job.arguments).to eq([1, 2])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
subject.perform_later 1, 2
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'passes message_group_id to the queue_adapter' do
|
|
30
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
|
31
|
+
expect(job.sqs_send_message_parameters[:message_group_id]).to eq('group-2')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
subject.set(message_group_id: 'group-2').perform_later 1, 2
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'passes message_deduplication_id to the queue_adapter' do
|
|
38
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
|
39
|
+
expect(job.sqs_send_message_parameters[:message_deduplication_id]).to eq('dedupe-id')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
subject.set(message_deduplication_id: 'dedupe-id').perform_later 1, 2
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'passes message_attributes to the queue_adapter' do
|
|
46
|
+
message_attributes = {
|
|
47
|
+
'custom_tracing_id' => {
|
|
48
|
+
string_value: 'value',
|
|
49
|
+
data_type: 'String'
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
|
53
|
+
expect(job.sqs_send_message_parameters[:message_attributes]).to eq(message_attributes)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
subject.set(message_attributes: message_attributes).perform_later 1, 2
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'passes message_system_attributes to the queue_adapter' do
|
|
60
|
+
message_system_attributes = {
|
|
61
|
+
'AWSTraceHeader' => {
|
|
62
|
+
string_value: 'trace_id',
|
|
63
|
+
data_type: 'String'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
expect(queue_adapter).to receive(:enqueue) do |job|
|
|
67
|
+
expect(job.sqs_send_message_parameters[:message_system_attributes]).to eq(message_system_attributes)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
subject.set(message_system_attributes: message_system_attributes).perform_later 1, 2
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'shared_examples_for_active_job'
|
|
3
|
+
require 'shoryuken/extensions/active_job_adapter'
|
|
4
|
+
require 'shoryuken/extensions/active_job_concurrent_send_adapter'
|
|
5
|
+
|
|
6
|
+
RSpec.describe ActiveJob::QueueAdapters::ShoryukenConcurrentSendAdapter do
|
|
7
|
+
include_examples 'active_job_adapters'
|
|
8
|
+
|
|
9
|
+
let(:options) { {} }
|
|
10
|
+
let(:error_handler) { -> {} }
|
|
11
|
+
let(:success_handler) { -> {} }
|
|
12
|
+
|
|
13
|
+
subject { described_class.new(success_handler, error_handler) }
|
|
14
|
+
|
|
15
|
+
context 'when success' do
|
|
16
|
+
it 'calls success_handler' do
|
|
17
|
+
response = true
|
|
18
|
+
allow(queue).to receive(:send_message).and_return(response)
|
|
19
|
+
expect(success_handler).to receive(:call).with(response, job, options)
|
|
20
|
+
|
|
21
|
+
subject.enqueue(job, options)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'when failure' do
|
|
26
|
+
it 'calls error_handler' do
|
|
27
|
+
response = Aws::SQS::Errors::InternalError.new('error', 'error')
|
|
28
|
+
|
|
29
|
+
allow(queue).to receive(:send_message).and_raise(response)
|
|
30
|
+
expect(error_handler).to receive(:call).with(response, job, options).and_call_original
|
|
31
|
+
|
|
32
|
+
subject.enqueue(job, options)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -139,4 +139,28 @@ RSpec.describe Shoryuken::Manager do
|
|
|
139
139
|
subject.send(:dispatch_single_messages, q)
|
|
140
140
|
end
|
|
141
141
|
end
|
|
142
|
+
|
|
143
|
+
describe '#processor_done' do
|
|
144
|
+
let(:sqs_queue) { double Shoryuken::Queue }
|
|
145
|
+
|
|
146
|
+
before do
|
|
147
|
+
allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
context 'when queue.fifo? is true' do
|
|
151
|
+
it 'calls message_processed on strategy' do
|
|
152
|
+
expect(sqs_queue).to receive(:fifo?).and_return(true)
|
|
153
|
+
expect(polling_strategy).to receive(:message_processed).with(queue)
|
|
154
|
+
subject.send(:processor_done, queue)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
context 'when queue.fifo? is false' do
|
|
159
|
+
it 'does not call message_processed on strategy' do
|
|
160
|
+
expect(sqs_queue).to receive(:fifo?).and_return(false)
|
|
161
|
+
expect(polling_strategy).to_not receive(:message_processed)
|
|
162
|
+
subject.send(:processor_done, queue)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
142
166
|
end
|
|
@@ -145,4 +145,14 @@ RSpec.describe Shoryuken::Polling::StrictPriority do
|
|
|
145
145
|
expect(subject.next_queue).to eq(queue3)
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
|
+
|
|
149
|
+
describe '#message_processed' do
|
|
150
|
+
it 'removes paused queue, adds to active queues' do
|
|
151
|
+
strategy = Shoryuken::Polling::StrictPriority.new([queue1, queue2])
|
|
152
|
+
strategy.send(:pause, queue1)
|
|
153
|
+
expect(strategy.active_queues).to eq([[queue2, 1]])
|
|
154
|
+
strategy.message_processed(queue1)
|
|
155
|
+
expect(strategy.active_queues).to eq([[queue1, 2], [queue2, 1]])
|
|
156
|
+
end
|
|
157
|
+
end
|
|
148
158
|
end
|
|
@@ -104,4 +104,14 @@ RSpec.describe Shoryuken::Polling::WeightedRoundRobin do
|
|
|
104
104
|
expect(subject.delay).to eq(1.0)
|
|
105
105
|
end
|
|
106
106
|
end
|
|
107
|
+
|
|
108
|
+
describe '#message_processed' do
|
|
109
|
+
it 'removes paused queue, adds to active queues' do
|
|
110
|
+
strategy = Shoryuken::Polling::WeightedRoundRobin.new([queue1, queue2])
|
|
111
|
+
strategy.send(:pause, queue1)
|
|
112
|
+
expect(strategy.active_queues).to eq([[queue2, 1]])
|
|
113
|
+
strategy.message_processed(queue1)
|
|
114
|
+
expect(strategy.active_queues).to eq([[queue2, 1], [queue1, 1]])
|
|
115
|
+
end
|
|
116
|
+
end
|
|
107
117
|
end
|