shoryuken 3.0.6 → 4.0.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/.rubocop.yml +90 -24
- data/.travis.yml +17 -5
- data/CHANGELOG.md +265 -62
- data/Gemfile +9 -1
- data/Gemfile.aws-sdk-core-v2 +13 -0
- data/README.md +19 -113
- data/Rakefile +1 -1
- data/bin/cli/base.rb +0 -3
- data/bin/cli/sqs.rb +42 -16
- data/bin/shoryuken +4 -9
- data/examples/bootstrap_queues.rb +3 -3
- data/examples/default_worker.rb +2 -2
- data/lib/shoryuken/body_parser.rb +27 -0
- data/lib/shoryuken/client.rb +6 -2
- data/lib/shoryuken/core_ext.rb +1 -1
- data/lib/shoryuken/default_worker_registry.rb +2 -2
- data/lib/shoryuken/environment_loader.rb +60 -24
- data/lib/shoryuken/extensions/active_job_adapter.rb +21 -11
- data/lib/shoryuken/fetcher.rb +58 -19
- data/lib/shoryuken/launcher.rb +70 -7
- data/lib/shoryuken/logging.rb +1 -6
- data/lib/shoryuken/manager.rb +50 -80
- data/lib/shoryuken/middleware/chain.rb +4 -0
- data/lib/shoryuken/middleware/server/active_record.rb +1 -1
- data/lib/shoryuken/middleware/server/auto_delete.rb +4 -9
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +6 -9
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +9 -3
- data/lib/shoryuken/middleware/server/timing.rb +12 -16
- data/lib/shoryuken/options.rb +225 -0
- data/lib/shoryuken/polling/base.rb +67 -0
- data/lib/shoryuken/polling/strict_priority.rb +77 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +66 -0
- data/lib/shoryuken/processor.rb +30 -39
- data/lib/shoryuken/queue.rb +41 -10
- data/lib/shoryuken/runner.rb +13 -17
- data/lib/shoryuken/util.rb +3 -3
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker/default_executor.rb +33 -0
- data/lib/shoryuken/worker/inline_executor.rb +37 -0
- data/lib/shoryuken/worker.rb +76 -31
- data/lib/shoryuken/worker_registry.rb +4 -4
- data/lib/shoryuken.rb +54 -173
- data/shoryuken.gemspec +6 -6
- data/spec/integration/launcher_spec.rb +14 -8
- data/spec/shoryuken/body_parser_spec.rb +89 -0
- data/spec/shoryuken/client_spec.rb +1 -1
- data/spec/shoryuken/core_ext_spec.rb +6 -6
- data/spec/shoryuken/default_worker_registry_spec.rb +2 -4
- data/spec/shoryuken/environment_loader_spec.rb +32 -12
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +64 -0
- data/spec/shoryuken/fetcher_spec.rb +101 -18
- data/spec/shoryuken/manager_spec.rb +54 -26
- data/spec/shoryuken/middleware/chain_spec.rb +17 -5
- data/spec/shoryuken/middleware/server/auto_delete_spec.rb +9 -7
- data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +4 -4
- data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +6 -4
- data/spec/shoryuken/middleware/server/timing_spec.rb +5 -3
- data/spec/shoryuken/options_spec.rb +180 -0
- data/spec/shoryuken/{polling_spec.rb → polling/strict_priority_spec.rb} +2 -101
- data/spec/shoryuken/polling/weighted_round_robin_spec.rb +99 -0
- data/spec/shoryuken/processor_spec.rb +26 -127
- data/spec/shoryuken/queue_spec.rb +115 -41
- data/spec/shoryuken/runner_spec.rb +3 -4
- data/spec/shoryuken/util_spec.rb +24 -0
- data/spec/shoryuken/worker/default_executor_spec.rb +105 -0
- data/spec/shoryuken/worker/inline_executor_spec.rb +49 -0
- data/spec/shoryuken/worker_spec.rb +35 -96
- data/spec/shoryuken_spec.rb +0 -59
- data/spec/spec_helper.rb +14 -3
- data/test_workers/endless_interruptive_worker.rb +2 -2
- data/test_workers/endless_uninterruptive_worker.rb +4 -4
- metadata +31 -12
- data/lib/shoryuken/polling.rb +0 -204
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Shoryuken::Options do
|
|
4
|
+
describe '.add_group' do
|
|
5
|
+
before do
|
|
6
|
+
Shoryuken.groups.clear
|
|
7
|
+
Shoryuken.add_group('group1', 25)
|
|
8
|
+
Shoryuken.add_group('group2', 25)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
specify do
|
|
12
|
+
described_class.add_queue('queue1', 1, 'group1')
|
|
13
|
+
described_class.add_queue('queue2', 2, 'group2')
|
|
14
|
+
|
|
15
|
+
expect(described_class.groups['group1'][:queues]).to eq(%w[queue1])
|
|
16
|
+
expect(described_class.groups['group2'][:queues]).to eq(%w[queue2 queue2])
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '.ungrouped_queues' do
|
|
21
|
+
before do
|
|
22
|
+
Shoryuken.groups.clear
|
|
23
|
+
Shoryuken.add_group('group1', 25)
|
|
24
|
+
Shoryuken.add_group('group2', 25)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
specify do
|
|
28
|
+
described_class.add_queue('queue1', 1, 'group1')
|
|
29
|
+
described_class.add_queue('queue2', 2, 'group2')
|
|
30
|
+
|
|
31
|
+
expect(described_class.ungrouped_queues).to eq(%w[queue1 queue2 queue2])
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '.sqs_client_receive_message_opts' do
|
|
36
|
+
before do
|
|
37
|
+
Shoryuken.sqs_client_receive_message_opts
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
specify do
|
|
41
|
+
Shoryuken.sqs_client_receive_message_opts = { test: 1 }
|
|
42
|
+
expect(Shoryuken.sqs_client_receive_message_opts).to eq('default' => { test: 1 })
|
|
43
|
+
|
|
44
|
+
Shoryuken.sqs_client_receive_message_opts['group1'] = { test: 2 }
|
|
45
|
+
|
|
46
|
+
expect(Shoryuken.sqs_client_receive_message_opts).to eq(
|
|
47
|
+
'default' => { test: 1 },
|
|
48
|
+
'group1' => { test: 2 }
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe '.register_worker' do
|
|
54
|
+
it 'registers a worker' do
|
|
55
|
+
described_class.worker_registry.clear
|
|
56
|
+
described_class.register_worker('default', TestWorker)
|
|
57
|
+
expect(described_class.worker_registry.workers('default')).to eq([TestWorker])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'registers a batchable worker' do
|
|
61
|
+
described_class.worker_registry.clear
|
|
62
|
+
TestWorker.get_shoryuken_options['batch'] = true
|
|
63
|
+
described_class.register_worker('default', TestWorker)
|
|
64
|
+
expect(described_class.worker_registry.workers('default')).to eq([TestWorker])
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'allows multiple workers' do
|
|
68
|
+
described_class.worker_registry.clear
|
|
69
|
+
described_class.register_worker('default', TestWorker)
|
|
70
|
+
expect(described_class.worker_registry.workers('default')).to eq([TestWorker])
|
|
71
|
+
|
|
72
|
+
class Test2Worker
|
|
73
|
+
include Shoryuken::Worker
|
|
74
|
+
|
|
75
|
+
shoryuken_options queue: 'default'
|
|
76
|
+
|
|
77
|
+
def perform(sqs_msg, body); end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
expect(described_class.worker_registry.workers('default')).to eq([Test2Worker])
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'raises an exception when mixing batchable with non batchable' do
|
|
84
|
+
described_class.worker_registry.clear
|
|
85
|
+
TestWorker.get_shoryuken_options['batch'] = true
|
|
86
|
+
described_class.register_worker('default', TestWorker)
|
|
87
|
+
|
|
88
|
+
expect {
|
|
89
|
+
class BatchableWorker
|
|
90
|
+
include Shoryuken::Worker
|
|
91
|
+
|
|
92
|
+
shoryuken_options queue: 'default', batch: true
|
|
93
|
+
|
|
94
|
+
def perform(sqs_msg, body); end
|
|
95
|
+
end
|
|
96
|
+
}.to raise_error('Could not register BatchableWorker for default, because TestWorker is already registered for this queue, ' \
|
|
97
|
+
"and Shoryuken doesn't support a batchable worker for a queue with multiple workers")
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe '.polling_strategy' do
|
|
102
|
+
context 'when not set' do
|
|
103
|
+
specify do
|
|
104
|
+
expect(Shoryuken.polling_strategy('default')).to eq Shoryuken::Polling::WeightedRoundRobin
|
|
105
|
+
expect(Shoryuken.polling_strategy('group1')).to eq Shoryuken::Polling::WeightedRoundRobin
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'when set to StrictPriority string' do
|
|
110
|
+
before do
|
|
111
|
+
Shoryuken.options[:polling_strategy] = 'StrictPriority'
|
|
112
|
+
|
|
113
|
+
Shoryuken.options[:groups] = {
|
|
114
|
+
'group1' => {
|
|
115
|
+
polling_strategy: 'StrictPriority'
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
specify do
|
|
121
|
+
expect(Shoryuken.polling_strategy('default')).to eq Shoryuken::Polling::StrictPriority
|
|
122
|
+
expect(Shoryuken.polling_strategy('group1')).to eq Shoryuken::Polling::StrictPriority
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context 'when set to WeightedRoundRobin string' do
|
|
127
|
+
before do
|
|
128
|
+
Shoryuken.options[:polling_strategy] = 'WeightedRoundRobin'
|
|
129
|
+
|
|
130
|
+
Shoryuken.options[:groups] = {
|
|
131
|
+
'group1' => {
|
|
132
|
+
polling_strategy: 'WeightedRoundRobin'
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
specify do
|
|
138
|
+
expect(Shoryuken.polling_strategy('default')).to eq Shoryuken::Polling::WeightedRoundRobin
|
|
139
|
+
expect(Shoryuken.polling_strategy('group1')).to eq Shoryuken::Polling::WeightedRoundRobin
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context 'when set to non-existent string' do
|
|
144
|
+
before do
|
|
145
|
+
Shoryuken.options[:polling_strategy] = 'NonExistent1'
|
|
146
|
+
|
|
147
|
+
Shoryuken.options[:groups] = {
|
|
148
|
+
'group1' => {
|
|
149
|
+
polling_strategy: 'NonExistent2'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
specify do
|
|
155
|
+
expect { Shoryuken.polling_strategy('default') }.to raise_error(ArgumentError)
|
|
156
|
+
expect { Shoryuken.polling_strategy('group1') }.to raise_error(ArgumentError)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context 'when set to a class' do
|
|
161
|
+
before do
|
|
162
|
+
class Foo < Shoryuken::Polling::BaseStrategy; end
|
|
163
|
+
class Bar < Shoryuken::Polling::BaseStrategy; end
|
|
164
|
+
|
|
165
|
+
Shoryuken.options[:polling_strategy] = Foo
|
|
166
|
+
|
|
167
|
+
Shoryuken.options[:groups] = {
|
|
168
|
+
'group1' => {
|
|
169
|
+
polling_strategy: Bar
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
specify do
|
|
175
|
+
expect(Shoryuken.polling_strategy('default')).to eq Foo
|
|
176
|
+
expect(Shoryuken.polling_strategy('group1')).to eq Bar
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -1,109 +1,10 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'shoryuken/polling'
|
|
3
2
|
|
|
4
|
-
describe Shoryuken::Polling::
|
|
5
|
-
let(:queue1) { 'shoryuken' }
|
|
6
|
-
let(:queue2) { 'uppercut' }
|
|
7
|
-
let(:queues) { Array.new }
|
|
8
|
-
subject { Shoryuken::Polling::WeightedRoundRobin.new(queues) }
|
|
9
|
-
|
|
10
|
-
describe '#next_queue' do
|
|
11
|
-
it 'cycles' do
|
|
12
|
-
# [shoryuken, 2]
|
|
13
|
-
# [uppercut, 1]
|
|
14
|
-
queues << queue1
|
|
15
|
-
queues << queue1
|
|
16
|
-
queues << queue2
|
|
17
|
-
|
|
18
|
-
expect(subject.next_queue).to eq(queue1)
|
|
19
|
-
expect(subject.next_queue).to eq(queue2)
|
|
20
|
-
expect(subject.next_queue).to eq(queue1)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'returns nil if there are no active queues' do
|
|
24
|
-
expect(subject.next_queue).to eq(nil)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'unpauses queues whose pause is expired' do
|
|
28
|
-
# [shoryuken, 2]
|
|
29
|
-
# [uppercut, 1]
|
|
30
|
-
queues << queue1
|
|
31
|
-
queues << queue1
|
|
32
|
-
queues << queue2
|
|
33
|
-
|
|
34
|
-
allow(subject).to receive(:delay).and_return(10)
|
|
35
|
-
|
|
36
|
-
now = Time.now
|
|
37
|
-
allow(Time).to receive(:now).and_return(now)
|
|
38
|
-
|
|
39
|
-
# pause the first queue
|
|
40
|
-
subject.messages_found(queue1, 0)
|
|
41
|
-
expect(subject.next_queue).to eq(queue2)
|
|
42
|
-
|
|
43
|
-
now += 5
|
|
44
|
-
allow(Time).to receive(:now).and_return(now)
|
|
45
|
-
|
|
46
|
-
# pause the second queue
|
|
47
|
-
subject.messages_found(queue2, 0)
|
|
48
|
-
expect(subject.next_queue).to eq(nil)
|
|
49
|
-
|
|
50
|
-
# queue1 should be unpaused now
|
|
51
|
-
now += 6
|
|
52
|
-
allow(Time).to receive(:now).and_return(now)
|
|
53
|
-
expect(subject.next_queue).to eq(queue1)
|
|
54
|
-
|
|
55
|
-
# queue1 should be unpaused and added to the end of queues now
|
|
56
|
-
now += 6
|
|
57
|
-
allow(Time).to receive(:now).and_return(now)
|
|
58
|
-
expect(subject.next_queue).to eq(queue1)
|
|
59
|
-
expect(subject.next_queue).to eq(queue2)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
describe '#messages_found' do
|
|
64
|
-
it 'pauses a queue if there are no messages found' do
|
|
65
|
-
# [shoryuken, 2]
|
|
66
|
-
# [uppercut, 1]
|
|
67
|
-
queues << queue1
|
|
68
|
-
queues << queue1
|
|
69
|
-
queues << queue2
|
|
70
|
-
|
|
71
|
-
expect(subject).to receive(:pause).with(queue1).and_call_original
|
|
72
|
-
subject.messages_found(queue1, 0)
|
|
73
|
-
expect(subject.instance_variable_get(:@queues)).to eq([queue2])
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it 'increased the weight if message is found' do
|
|
77
|
-
# [shoryuken, 2]
|
|
78
|
-
# [uppercut, 1]
|
|
79
|
-
queues << queue1
|
|
80
|
-
queues << queue1
|
|
81
|
-
queues << queue2
|
|
82
|
-
|
|
83
|
-
expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2])
|
|
84
|
-
subject.messages_found(queue1, 1)
|
|
85
|
-
expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2, queue1])
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
it 'respects the maximum queue weight' do
|
|
89
|
-
# [shoryuken, 2]
|
|
90
|
-
# [uppercut, 1]
|
|
91
|
-
queues << queue1
|
|
92
|
-
queues << queue1
|
|
93
|
-
queues << queue2
|
|
94
|
-
|
|
95
|
-
subject.messages_found(queue1, 1)
|
|
96
|
-
subject.messages_found(queue1, 1)
|
|
97
|
-
expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2, queue1])
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
describe Shoryuken::Polling::StrictPriority do
|
|
3
|
+
RSpec.describe Shoryuken::Polling::StrictPriority do
|
|
103
4
|
let(:queue1) { 'shoryuken' }
|
|
104
5
|
let(:queue2) { 'uppercut' }
|
|
105
6
|
let(:queue3) { 'other' }
|
|
106
|
-
let(:queues) {
|
|
7
|
+
let(:queues) { [] }
|
|
107
8
|
subject { Shoryuken::Polling::StrictPriority.new(queues) }
|
|
108
9
|
|
|
109
10
|
describe '#next_queue' do
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Shoryuken::Polling::WeightedRoundRobin do
|
|
4
|
+
let(:queue1) { 'shoryuken' }
|
|
5
|
+
let(:queue2) { 'uppercut' }
|
|
6
|
+
let(:queues) { [] }
|
|
7
|
+
subject { Shoryuken::Polling::WeightedRoundRobin.new(queues) }
|
|
8
|
+
|
|
9
|
+
describe '#next_queue' do
|
|
10
|
+
it 'cycles' do
|
|
11
|
+
# [shoryuken, 2]
|
|
12
|
+
# [uppercut, 1]
|
|
13
|
+
queues << queue1
|
|
14
|
+
queues << queue1
|
|
15
|
+
queues << queue2
|
|
16
|
+
|
|
17
|
+
expect(subject.next_queue).to eq(queue1)
|
|
18
|
+
expect(subject.next_queue).to eq(queue2)
|
|
19
|
+
expect(subject.next_queue).to eq(queue1)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'returns nil if there are no active queues' do
|
|
23
|
+
expect(subject.next_queue).to eq(nil)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'unpauses queues whose pause is expired' do
|
|
27
|
+
# [shoryuken, 2]
|
|
28
|
+
# [uppercut, 1]
|
|
29
|
+
queues << queue1
|
|
30
|
+
queues << queue1
|
|
31
|
+
queues << queue2
|
|
32
|
+
|
|
33
|
+
allow(subject).to receive(:delay).and_return(10)
|
|
34
|
+
|
|
35
|
+
now = Time.now
|
|
36
|
+
allow(Time).to receive(:now).and_return(now)
|
|
37
|
+
|
|
38
|
+
# pause the first queue
|
|
39
|
+
subject.messages_found(queue1, 0)
|
|
40
|
+
expect(subject.next_queue).to eq(queue2)
|
|
41
|
+
|
|
42
|
+
now += 5
|
|
43
|
+
allow(Time).to receive(:now).and_return(now)
|
|
44
|
+
|
|
45
|
+
# pause the second queue
|
|
46
|
+
subject.messages_found(queue2, 0)
|
|
47
|
+
expect(subject.next_queue).to eq(nil)
|
|
48
|
+
|
|
49
|
+
# queue1 should be unpaused now
|
|
50
|
+
now += 6
|
|
51
|
+
allow(Time).to receive(:now).and_return(now)
|
|
52
|
+
expect(subject.next_queue).to eq(queue1)
|
|
53
|
+
|
|
54
|
+
# queue1 should be unpaused and added to the end of queues now
|
|
55
|
+
now += 6
|
|
56
|
+
allow(Time).to receive(:now).and_return(now)
|
|
57
|
+
expect(subject.next_queue).to eq(queue1)
|
|
58
|
+
expect(subject.next_queue).to eq(queue2)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe '#messages_found' do
|
|
63
|
+
it 'pauses a queue if there are no messages found' do
|
|
64
|
+
# [shoryuken, 2]
|
|
65
|
+
# [uppercut, 1]
|
|
66
|
+
queues << queue1
|
|
67
|
+
queues << queue1
|
|
68
|
+
queues << queue2
|
|
69
|
+
|
|
70
|
+
expect(subject).to receive(:pause).with(queue1).and_call_original
|
|
71
|
+
subject.messages_found(queue1, 0)
|
|
72
|
+
expect(subject.instance_variable_get(:@queues)).to eq([queue2])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'increased the weight if message is found' do
|
|
76
|
+
# [shoryuken, 2]
|
|
77
|
+
# [uppercut, 1]
|
|
78
|
+
queues << queue1
|
|
79
|
+
queues << queue1
|
|
80
|
+
queues << queue2
|
|
81
|
+
|
|
82
|
+
expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2])
|
|
83
|
+
subject.messages_found(queue1, 1)
|
|
84
|
+
expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2, queue1])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'respects the maximum queue weight' do
|
|
88
|
+
# [shoryuken, 2]
|
|
89
|
+
# [uppercut, 1]
|
|
90
|
+
queues << queue1
|
|
91
|
+
queues << queue1
|
|
92
|
+
queues << queue2
|
|
93
|
+
|
|
94
|
+
subject.messages_found(queue1, 1)
|
|
95
|
+
subject.messages_found(queue1, 1)
|
|
96
|
+
expect(subject.instance_variable_get(:@queues)).to eq([queue1, queue2, queue1])
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -1,139 +1,44 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'shoryuken/processor'
|
|
3
|
-
require 'shoryuken/manager'
|
|
4
2
|
|
|
5
3
|
RSpec.describe Shoryuken::Processor do
|
|
6
|
-
let(:manager) { double Shoryuken::Manager
|
|
4
|
+
let(:manager) { double Shoryuken::Manager }
|
|
7
5
|
let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 30 }
|
|
8
6
|
let(:queue) { 'default' }
|
|
9
7
|
|
|
10
8
|
let(:sqs_msg) do
|
|
11
|
-
double
|
|
9
|
+
double(
|
|
10
|
+
Shoryuken::Message,
|
|
12
11
|
queue_url: queue,
|
|
13
12
|
body: 'test',
|
|
14
13
|
message_attributes: {},
|
|
15
14
|
message_id: SecureRandom.uuid,
|
|
16
15
|
receipt_handle: SecureRandom.uuid
|
|
16
|
+
)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
subject { described_class.new(manager) }
|
|
20
|
-
|
|
21
19
|
before do
|
|
22
20
|
allow(manager).to receive(:async).and_return(manager)
|
|
23
21
|
allow(manager).to receive(:real_thread)
|
|
24
22
|
allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
it 'parses the body into JSON' do
|
|
29
|
-
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
30
|
-
|
|
31
|
-
body = { 'test' => 'hi' }
|
|
32
|
-
|
|
33
|
-
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
34
|
-
|
|
35
|
-
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
36
|
-
|
|
37
|
-
subject.process(queue, sqs_msg)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
it 'parses the body calling the proc' do
|
|
41
|
-
TestWorker.get_shoryuken_options['body_parser'] = proc { |sqs_msg| "*#{sqs_msg.body}*" }
|
|
42
|
-
|
|
43
|
-
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, '*test*')
|
|
44
|
-
|
|
45
|
-
allow(sqs_msg).to receive(:body).and_return('test')
|
|
46
|
-
|
|
47
|
-
subject.process(queue, sqs_msg)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it 'parses the body as text' do
|
|
51
|
-
TestWorker.get_shoryuken_options['body_parser'] = :text
|
|
52
|
-
|
|
53
|
-
body = 'test'
|
|
54
|
-
|
|
55
|
-
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
56
|
-
|
|
57
|
-
allow(sqs_msg).to receive(:body).and_return(body)
|
|
58
|
-
|
|
59
|
-
subject.process(queue, sqs_msg)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'parses calling `.load`' do
|
|
63
|
-
TestWorker.get_shoryuken_options['body_parser'] = Class.new do
|
|
64
|
-
def self.load(*args)
|
|
65
|
-
JSON.load(*args)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
body = { 'test' => 'hi' }
|
|
70
|
-
|
|
71
|
-
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
72
|
-
|
|
73
|
-
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
74
|
-
|
|
75
|
-
subject.process(queue, sqs_msg)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it 'parses calling `.parse`' do
|
|
79
|
-
TestWorker.get_shoryuken_options['body_parser'] = Class.new do
|
|
80
|
-
def self.parse(*args)
|
|
81
|
-
JSON.parse(*args)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
body = { 'test' => 'hi' }
|
|
86
|
-
|
|
87
|
-
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
88
|
-
|
|
89
|
-
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
90
|
-
|
|
91
|
-
subject.process(queue, sqs_msg)
|
|
92
|
-
end
|
|
25
|
+
subject { described_class.new(queue, sqs_msg) }
|
|
93
26
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
allow(sqs_msg).to receive(:body).and_return('invalid json')
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
it 'logs the error' do
|
|
102
|
-
expect(manager).to receive(:processor_failed)
|
|
103
|
-
expect(subject.logger).to receive(:error) do |&block|
|
|
104
|
-
expect(block.call).
|
|
105
|
-
to include("unexpected token at 'invalid json'\nbody_parser: json\nsqs_msg.body: invalid json")
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
subject.process(queue, sqs_msg) rescue nil
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it 're raises the error' do
|
|
112
|
-
expect(manager).to receive(:processor_failed)
|
|
113
|
-
expect { subject.process(queue, sqs_msg) }.
|
|
114
|
-
to raise_error(JSON::ParserError, /unexpected token at 'invalid json'/)
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
context 'when `object_type: nil`' do
|
|
119
|
-
it 'parses the body as text' do
|
|
120
|
-
TestWorker.get_shoryuken_options['body_parser'] = nil
|
|
121
|
-
|
|
122
|
-
body = 'test'
|
|
123
|
-
|
|
124
|
-
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
27
|
+
describe '#process' do
|
|
28
|
+
it 'sets log context' do
|
|
29
|
+
expect(Shoryuken::Logging).to receive(:with_context).with("TestWorker/#{queue}/#{sqs_msg.message_id}")
|
|
125
30
|
|
|
126
|
-
|
|
31
|
+
allow_any_instance_of(TestWorker).to receive(:perform)
|
|
32
|
+
allow(sqs_msg).to receive(:body)
|
|
127
33
|
|
|
128
|
-
|
|
129
|
-
end
|
|
34
|
+
subject.process
|
|
130
35
|
end
|
|
131
36
|
|
|
132
37
|
context 'when custom middleware' do
|
|
133
38
|
let(:queue) { 'worker_called_middleware' }
|
|
134
39
|
|
|
135
40
|
class WorkerCalledMiddleware
|
|
136
|
-
def call(worker, queue, sqs_msg,
|
|
41
|
+
def call(worker, queue, sqs_msg, _body)
|
|
137
42
|
# called is defined with `allow(...).to receive(...)`
|
|
138
43
|
worker.called(sqs_msg, queue)
|
|
139
44
|
yield
|
|
@@ -152,7 +57,7 @@ RSpec.describe Shoryuken::Processor do
|
|
|
152
57
|
|
|
153
58
|
context 'server' do
|
|
154
59
|
before do
|
|
155
|
-
allow(Shoryuken).to receive(:server?).and_return(true)
|
|
60
|
+
allow(Shoryuken::Options).to receive(:server?).and_return(true)
|
|
156
61
|
WorkerCalledMiddlewareWorker.instance_variable_set(:@server_chain, nil) # un-memoize middleware
|
|
157
62
|
|
|
158
63
|
Shoryuken.configure_server do |config|
|
|
@@ -171,12 +76,10 @@ RSpec.describe Shoryuken::Processor do
|
|
|
171
76
|
end
|
|
172
77
|
|
|
173
78
|
it 'invokes middleware' do
|
|
174
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
175
|
-
|
|
176
79
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
177
80
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:called).with(sqs_msg, queue)
|
|
178
81
|
|
|
179
|
-
subject.process
|
|
82
|
+
subject.process
|
|
180
83
|
end
|
|
181
84
|
end
|
|
182
85
|
|
|
@@ -201,12 +104,10 @@ RSpec.describe Shoryuken::Processor do
|
|
|
201
104
|
end
|
|
202
105
|
|
|
203
106
|
it "doesn't invoke middleware" do
|
|
204
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
205
|
-
|
|
206
107
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
207
108
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to_not receive(:called).with(sqs_msg, queue)
|
|
208
109
|
|
|
209
|
-
subject.process
|
|
110
|
+
subject.process
|
|
210
111
|
end
|
|
211
112
|
end
|
|
212
113
|
end
|
|
@@ -214,50 +115,48 @@ RSpec.describe Shoryuken::Processor do
|
|
|
214
115
|
it 'performs with delete' do
|
|
215
116
|
TestWorker.get_shoryuken_options['auto_delete'] = true
|
|
216
117
|
|
|
217
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
218
|
-
|
|
219
118
|
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
220
119
|
|
|
221
120
|
expect(sqs_queue).to receive(:delete_messages).with(entries: [{ id: '0', receipt_handle: sqs_msg.receipt_handle }])
|
|
222
121
|
|
|
223
|
-
subject.process
|
|
122
|
+
subject.process
|
|
224
123
|
end
|
|
225
124
|
|
|
226
125
|
it 'performs without delete' do
|
|
227
126
|
TestWorker.get_shoryuken_options['auto_delete'] = false
|
|
228
127
|
|
|
229
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
230
|
-
|
|
231
128
|
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
232
129
|
|
|
233
130
|
expect(sqs_queue).to_not receive(:delete_messages)
|
|
234
131
|
|
|
235
|
-
subject.process
|
|
132
|
+
subject.process
|
|
236
133
|
end
|
|
237
134
|
|
|
238
135
|
context 'when shoryuken_class header' do
|
|
239
136
|
let(:sqs_msg) do
|
|
240
|
-
double
|
|
137
|
+
double(
|
|
138
|
+
Shoryuken::Message,
|
|
241
139
|
queue_url: queue,
|
|
242
140
|
body: 'test',
|
|
243
141
|
message_attributes: {
|
|
244
142
|
'shoryuken_class' => {
|
|
245
143
|
string_value: TestWorker.to_s,
|
|
246
|
-
data_type: 'String'
|
|
247
|
-
|
|
248
|
-
|
|
144
|
+
data_type: 'String'
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
message_id: SecureRandom.uuid,
|
|
148
|
+
receipt_handle: SecureRandom.uuid
|
|
149
|
+
)
|
|
249
150
|
end
|
|
250
151
|
|
|
251
152
|
it 'performs without delete' do
|
|
252
153
|
Shoryuken.worker_registry.clear # unregister TestWorker
|
|
253
154
|
|
|
254
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
255
|
-
|
|
256
155
|
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
257
156
|
|
|
258
157
|
expect(sqs_queue).to_not receive(:delete_messages)
|
|
259
158
|
|
|
260
|
-
subject.process
|
|
159
|
+
subject.process
|
|
261
160
|
end
|
|
262
161
|
end
|
|
263
162
|
end
|