shoryuken 3.0.4 → 3.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/.rubocop.yml +1 -1
- data/.travis.yml +1 -0
- data/CHANGELOG.md +79 -0
- data/Gemfile +1 -0
- data/README.md +15 -117
- data/bin/cli/base.rb +0 -2
- data/bin/cli/sqs.rb +18 -1
- data/bin/shoryuken +9 -1
- data/examples/default_worker.rb +1 -1
- data/lib/shoryuken/default_worker_registry.rb +2 -2
- data/lib/shoryuken/environment_loader.rb +33 -13
- data/lib/shoryuken/fetcher.rb +17 -16
- data/lib/shoryuken/launcher.rb +86 -7
- data/lib/shoryuken/manager.rb +42 -72
- data/lib/shoryuken/middleware/server/auto_delete.rb +3 -8
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +4 -4
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +8 -2
- data/lib/shoryuken/middleware/server/timing.rb +2 -2
- data/lib/shoryuken/options.rb +192 -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 +23 -17
- data/lib/shoryuken/queue.rb +27 -6
- data/lib/shoryuken/runner.rb +3 -15
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker.rb +8 -0
- data/lib/shoryuken.rb +39 -172
- data/shoryuken.gemspec +1 -1
- data/spec/integration/launcher_spec.rb +12 -6
- data/spec/shoryuken/environment_loader_spec.rb +3 -12
- data/spec/shoryuken/fetcher_spec.rb +30 -15
- data/spec/shoryuken/manager_spec.rb +12 -13
- data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +1 -1
- data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +1 -1
- data/spec/shoryuken/options_spec.rb +100 -0
- data/spec/shoryuken/{polling_spec.rb → polling/strict_priority_spec.rb} +1 -100
- data/spec/shoryuken/polling/weighted_round_robin_spec.rb +99 -0
- data/spec/shoryuken/processor_spec.rb +20 -37
- data/spec/shoryuken/queue_spec.rb +72 -26
- data/spec/shoryuken/runner_spec.rb +3 -4
- data/spec/shoryuken_spec.rb +0 -59
- data/spec/spec_helper.rb +8 -2
- data/test_workers/endless_uninterruptive_worker.rb +1 -1
- metadata +14 -7
- data/lib/shoryuken/polling.rb +0 -204
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
end
|
|
@@ -1,105 +1,6 @@
|
|
|
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' }
|
|
@@ -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) { Array.new }
|
|
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
|
|
@@ -3,7 +3,7 @@ require 'shoryuken/processor'
|
|
|
3
3
|
require 'shoryuken/manager'
|
|
4
4
|
|
|
5
5
|
RSpec.describe Shoryuken::Processor do
|
|
6
|
-
let(:manager) { double Shoryuken::Manager
|
|
6
|
+
let(:manager) { double Shoryuken::Manager }
|
|
7
7
|
let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 30 }
|
|
8
8
|
let(:queue) { 'default' }
|
|
9
9
|
|
|
@@ -16,14 +16,14 @@ RSpec.describe Shoryuken::Processor do
|
|
|
16
16
|
receipt_handle: SecureRandom.uuid
|
|
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
|
|
|
25
|
+
subject { described_class.new(queue, sqs_msg) }
|
|
26
|
+
|
|
27
27
|
describe '#process' do
|
|
28
28
|
it 'parses the body into JSON' do
|
|
29
29
|
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
@@ -34,7 +34,7 @@ RSpec.describe Shoryuken::Processor do
|
|
|
34
34
|
|
|
35
35
|
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
36
36
|
|
|
37
|
-
subject.process
|
|
37
|
+
subject.process
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
it 'parses the body calling the proc' do
|
|
@@ -44,7 +44,7 @@ RSpec.describe Shoryuken::Processor do
|
|
|
44
44
|
|
|
45
45
|
allow(sqs_msg).to receive(:body).and_return('test')
|
|
46
46
|
|
|
47
|
-
subject.process
|
|
47
|
+
subject.process
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
it 'parses the body as text' do
|
|
@@ -56,7 +56,7 @@ RSpec.describe Shoryuken::Processor do
|
|
|
56
56
|
|
|
57
57
|
allow(sqs_msg).to receive(:body).and_return(body)
|
|
58
58
|
|
|
59
|
-
subject.process
|
|
59
|
+
subject.process
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
it 'parses calling `.load`' do
|
|
@@ -72,7 +72,7 @@ RSpec.describe Shoryuken::Processor do
|
|
|
72
72
|
|
|
73
73
|
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
74
74
|
|
|
75
|
-
subject.process
|
|
75
|
+
subject.process
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
it 'parses calling `.parse`' do
|
|
@@ -88,28 +88,21 @@ RSpec.describe Shoryuken::Processor do
|
|
|
88
88
|
|
|
89
89
|
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
90
90
|
|
|
91
|
-
subject.process
|
|
91
|
+
subject.process
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
context 'when parse errors' do
|
|
95
95
|
before do
|
|
96
96
|
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
97
97
|
|
|
98
|
-
allow(sqs_msg).to receive(:body).and_return('invalid
|
|
98
|
+
allow(sqs_msg).to receive(:body).and_return('invalid JSON')
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
expect(subject.logger).to receive(:error)
|
|
103
|
-
expect(block.call).
|
|
104
|
-
to include("unexpected token at 'invalid json'\nbody_parser: json\nsqs_msg.body: invalid json")
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
subject.process(queue, sqs_msg) rescue nil
|
|
108
|
-
end
|
|
101
|
+
specify do
|
|
102
|
+
expect(subject.logger).to receive(:error).twice
|
|
109
103
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
to raise_error(JSON::ParserError, /unexpected token at 'invalid json'/)
|
|
104
|
+
expect { subject.process }.
|
|
105
|
+
to raise_error(JSON::ParserError, /unexpected token at 'invalid JSON'/)
|
|
113
106
|
end
|
|
114
107
|
end
|
|
115
108
|
|
|
@@ -123,7 +116,7 @@ RSpec.describe Shoryuken::Processor do
|
|
|
123
116
|
|
|
124
117
|
allow(sqs_msg).to receive(:body).and_return(body)
|
|
125
118
|
|
|
126
|
-
subject.process
|
|
119
|
+
subject.process
|
|
127
120
|
end
|
|
128
121
|
end
|
|
129
122
|
|
|
@@ -150,7 +143,7 @@ RSpec.describe Shoryuken::Processor do
|
|
|
150
143
|
|
|
151
144
|
context 'server' do
|
|
152
145
|
before do
|
|
153
|
-
allow(Shoryuken).to receive(:server?).and_return(true)
|
|
146
|
+
allow(Shoryuken::Options).to receive(:server?).and_return(true)
|
|
154
147
|
WorkerCalledMiddlewareWorker.instance_variable_set(:@server_chain, nil) # un-memoize middleware
|
|
155
148
|
|
|
156
149
|
Shoryuken.configure_server do |config|
|
|
@@ -169,12 +162,10 @@ RSpec.describe Shoryuken::Processor do
|
|
|
169
162
|
end
|
|
170
163
|
|
|
171
164
|
it 'invokes middleware' do
|
|
172
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
173
|
-
|
|
174
165
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
175
166
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:called).with(sqs_msg, queue)
|
|
176
167
|
|
|
177
|
-
subject.process
|
|
168
|
+
subject.process
|
|
178
169
|
end
|
|
179
170
|
end
|
|
180
171
|
|
|
@@ -199,12 +190,10 @@ RSpec.describe Shoryuken::Processor do
|
|
|
199
190
|
end
|
|
200
191
|
|
|
201
192
|
it "doesn't invoke middleware" do
|
|
202
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
203
|
-
|
|
204
193
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
205
194
|
expect_any_instance_of(WorkerCalledMiddlewareWorker).to_not receive(:called).with(sqs_msg, queue)
|
|
206
195
|
|
|
207
|
-
subject.process
|
|
196
|
+
subject.process
|
|
208
197
|
end
|
|
209
198
|
end
|
|
210
199
|
end
|
|
@@ -212,25 +201,21 @@ RSpec.describe Shoryuken::Processor do
|
|
|
212
201
|
it 'performs with delete' do
|
|
213
202
|
TestWorker.get_shoryuken_options['auto_delete'] = true
|
|
214
203
|
|
|
215
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
216
|
-
|
|
217
204
|
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
218
205
|
|
|
219
206
|
expect(sqs_queue).to receive(:delete_messages).with(entries: [{ id: '0', receipt_handle: sqs_msg.receipt_handle }])
|
|
220
207
|
|
|
221
|
-
subject.process
|
|
208
|
+
subject.process
|
|
222
209
|
end
|
|
223
210
|
|
|
224
211
|
it 'performs without delete' do
|
|
225
212
|
TestWorker.get_shoryuken_options['auto_delete'] = false
|
|
226
213
|
|
|
227
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
228
|
-
|
|
229
214
|
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
230
215
|
|
|
231
216
|
expect(sqs_queue).to_not receive(:delete_messages)
|
|
232
217
|
|
|
233
|
-
subject.process
|
|
218
|
+
subject.process
|
|
234
219
|
end
|
|
235
220
|
|
|
236
221
|
context 'when shoryuken_class header' do
|
|
@@ -249,13 +234,11 @@ RSpec.describe Shoryuken::Processor do
|
|
|
249
234
|
it 'performs without delete' do
|
|
250
235
|
Shoryuken.worker_registry.clear # unregister TestWorker
|
|
251
236
|
|
|
252
|
-
expect(manager).to receive(:processor_done).with(queue)
|
|
253
|
-
|
|
254
237
|
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
255
238
|
|
|
256
239
|
expect(sqs_queue).to_not receive(:delete_messages)
|
|
257
240
|
|
|
258
|
-
subject.process
|
|
241
|
+
subject.process
|
|
259
242
|
end
|
|
260
243
|
end
|
|
261
244
|
end
|
|
@@ -1,22 +1,68 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
describe Shoryuken::Queue do
|
|
3
|
+
RSpec.describe Shoryuken::Queue do
|
|
4
4
|
let(:credentials) { Aws::Credentials.new('access_key_id', 'secret_access_key') }
|
|
5
5
|
let(:sqs) { Aws::SQS::Client.new(stub_responses: true, credentials: credentials) }
|
|
6
6
|
let(:queue_name) { 'shoryuken' }
|
|
7
|
-
let(:queue_url) {
|
|
7
|
+
let(:queue_url) { "https://sqs.eu-west-1.amazonaws.com:6059/0123456789/#{queue_name}" }
|
|
8
8
|
|
|
9
9
|
subject { described_class.new(sqs, queue_name) }
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
before do
|
|
11
12
|
# Required as Aws::SQS::Client.get_queue_url returns 'String' when responses are stubbed,
|
|
12
13
|
# which is not accepted by Aws::SQS::Client.get_queue_attributes for :queue_name parameter.
|
|
13
14
|
allow(subject).to receive(:url).and_return(queue_url)
|
|
14
|
-
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#new' do
|
|
18
|
+
context 'when queue url supplied' do
|
|
19
|
+
subject { described_class.new(sqs, queue_url) }
|
|
20
|
+
|
|
21
|
+
specify do
|
|
22
|
+
expect(subject.name).to eq(queue_name)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context 'when queue name supplied' do
|
|
27
|
+
subject { described_class.new(sqs, queue_name) }
|
|
28
|
+
|
|
29
|
+
specify do
|
|
30
|
+
expect(subject.name).to eq(queue_name)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#delete_messages' do
|
|
36
|
+
let(:entries) do
|
|
37
|
+
[
|
|
38
|
+
{ id: '1', receipt_handle: '1' },
|
|
39
|
+
{ id: '2', receipt_handle: '2' }
|
|
40
|
+
]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'deletes' do
|
|
44
|
+
expect(sqs).to receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: []))
|
|
45
|
+
|
|
46
|
+
subject.delete_messages(entries: entries)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context 'when it fails' do
|
|
50
|
+
it 'logs the reason' do
|
|
51
|
+
failure = double(id: 'id', code: 'code', message: '...', sender_fault: false)
|
|
52
|
+
logger = double 'Logger'
|
|
53
|
+
|
|
54
|
+
expect(sqs).to receive(:delete_message_batch).with(entries: entries, queue_url: queue_url).and_return(double(failed: [failure]))
|
|
55
|
+
expect(subject).to receive(:logger).and_return(logger)
|
|
56
|
+
expect(logger).to receive(:error)
|
|
57
|
+
|
|
58
|
+
subject.delete_messages(entries: entries)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
15
62
|
|
|
16
63
|
describe '#send_message' do
|
|
17
|
-
before {
|
|
18
|
-
|
|
19
|
-
}
|
|
64
|
+
before { allow(subject).to receive(:fifo?).and_return(false) }
|
|
65
|
+
|
|
20
66
|
it 'accepts SQS request parameters' do
|
|
21
67
|
# https://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#send_message-instance_method
|
|
22
68
|
expect(sqs).to receive(:send_message).with(hash_including(message_body: 'msg1'))
|
|
@@ -77,24 +123,24 @@ describe Shoryuken::Queue do
|
|
|
77
123
|
|
|
78
124
|
it 'accepts an array of messages' do
|
|
79
125
|
options = { entries: [{ id: '0',
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
126
|
+
message_body: 'msg1',
|
|
127
|
+
delay_seconds: 1,
|
|
128
|
+
message_attributes: { attr: 'attr1' } },
|
|
129
|
+
{ id: '1',
|
|
130
|
+
message_body: 'msg2',
|
|
131
|
+
delay_seconds: 1,
|
|
132
|
+
message_attributes: { attr: 'attr2' } }] }
|
|
87
133
|
|
|
88
134
|
expect(sqs).to receive(:send_message_batch).with(hash_including(options))
|
|
89
135
|
|
|
90
136
|
subject.send_messages([{ message_body: 'msg1',
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
137
|
+
delay_seconds: 1,
|
|
138
|
+
message_attributes: { attr: 'attr1' }
|
|
139
|
+
}, {
|
|
140
|
+
message_body: 'msg2',
|
|
141
|
+
delay_seconds: 1,
|
|
142
|
+
message_attributes: { attr: 'attr2' }
|
|
143
|
+
}])
|
|
98
144
|
end
|
|
99
145
|
|
|
100
146
|
context 'when FIFO' do
|
|
@@ -125,9 +171,9 @@ describe Shoryuken::Queue do
|
|
|
125
171
|
end
|
|
126
172
|
|
|
127
173
|
subject.send_messages([{ message_body: 'msg1',
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
174
|
+
message_attributes: { attr: 'attr1' },
|
|
175
|
+
message_group_id: 'my group',
|
|
176
|
+
message_deduplication_id: 'my id' }])
|
|
131
177
|
end
|
|
132
178
|
end
|
|
133
179
|
end
|
|
@@ -151,13 +197,13 @@ describe Shoryuken::Queue do
|
|
|
151
197
|
context 'when queue is FIFO' do
|
|
152
198
|
let(:fifo) { true }
|
|
153
199
|
|
|
154
|
-
|
|
200
|
+
specify { expect(subject.fifo?).to be }
|
|
155
201
|
end
|
|
156
202
|
|
|
157
203
|
context 'when queue is not FIFO' do
|
|
158
204
|
let(:fifo) { false }
|
|
159
205
|
|
|
160
|
-
|
|
206
|
+
specify { expect(subject.fifo?).to_not be }
|
|
161
207
|
end
|
|
162
208
|
end
|
|
163
209
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
require 'shoryuken/runner'
|
|
3
|
-
require 'shoryuken/launcher'
|
|
4
3
|
|
|
5
4
|
# rubocop:disable Metrics/BlockLength
|
|
6
5
|
RSpec.describe Shoryuken::Runner do
|
|
@@ -17,10 +16,10 @@ RSpec.describe Shoryuken::Runner do
|
|
|
17
16
|
describe '#run' do
|
|
18
17
|
let(:launcher) { instance_double('Shoryuken::Launcher') }
|
|
19
18
|
|
|
20
|
-
before
|
|
19
|
+
before do
|
|
21
20
|
allow(Shoryuken::Launcher).to receive(:new).and_return(launcher)
|
|
22
|
-
allow(launcher).to receive(:
|
|
23
|
-
allow(launcher).to receive(:stop)
|
|
21
|
+
allow(launcher).to receive(:start).and_raise(Interrupt)
|
|
22
|
+
allow(launcher).to receive(:stop!)
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
it 'does not raise' do
|