shoryuken 2.0.11 → 3.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/.codeclimate.yml +20 -0
- data/.rubocop.yml +8 -2
- data/.travis.yml +7 -5
- data/CHANGELOG.md +92 -10
- data/Gemfile +1 -0
- data/README.md +20 -57
- data/Rakefile +0 -1
- data/bin/cli/base.rb +42 -0
- data/bin/cli/sqs.rb +188 -0
- data/bin/shoryuken +47 -9
- data/examples/default_worker.rb +1 -12
- data/lib/shoryuken/client.rb +3 -25
- data/lib/shoryuken/default_worker_registry.rb +9 -5
- data/lib/shoryuken/environment_loader.rb +29 -67
- data/lib/shoryuken/fetcher.rb +22 -53
- data/lib/shoryuken/launcher.rb +5 -29
- data/lib/shoryuken/manager.rb +72 -184
- data/lib/shoryuken/message.rb +4 -13
- data/lib/shoryuken/middleware/chain.rb +1 -18
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +21 -18
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +26 -19
- data/lib/shoryuken/polling.rb +204 -0
- data/lib/shoryuken/processor.rb +6 -14
- data/lib/shoryuken/queue.rb +36 -38
- data/lib/shoryuken/runner.rb +143 -0
- data/lib/shoryuken/util.rb +3 -9
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker.rb +1 -1
- data/lib/shoryuken.rb +78 -39
- data/shoryuken.gemspec +6 -6
- data/spec/integration/launcher_spec.rb +4 -3
- data/spec/shoryuken/client_spec.rb +2 -43
- data/spec/shoryuken/default_worker_registry_spec.rb +12 -10
- data/spec/shoryuken/environment_loader_spec.rb +34 -0
- data/spec/shoryuken/fetcher_spec.rb +18 -52
- data/spec/shoryuken/manager_spec.rb +56 -97
- data/spec/shoryuken/middleware/chain_spec.rb +0 -24
- data/spec/shoryuken/middleware/server/auto_delete_spec.rb +2 -2
- data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +7 -3
- data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +56 -33
- data/spec/shoryuken/polling_spec.rb +239 -0
- data/spec/shoryuken/processor_spec.rb +5 -5
- data/spec/shoryuken/queue_spec.rb +110 -63
- data/spec/shoryuken/{cli_spec.rb → runner_spec.rb} +10 -24
- data/spec/shoryuken_spec.rb +13 -1
- data/spec/spec_helper.rb +8 -20
- data/test_workers/endless_interruptive_worker.rb +41 -0
- data/test_workers/endless_uninterruptive_worker.rb +44 -0
- metadata +34 -35
- data/.hound.yml +0 -6
- data/lib/shoryuken/cli.rb +0 -210
- data/lib/shoryuken/sns_arn.rb +0 -27
- data/lib/shoryuken/topic.rb +0 -17
- data/spec/shoryuken/sns_arn_spec.rb +0 -42
- data/spec/shoryuken/topic_spec.rb +0 -32
- data/spec/shoryuken_endpoint.yml +0 -6
- /data/{LICENSE.txt → LICENSE} +0 -0
data/lib/shoryuken.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require 'yaml'
|
|
2
|
+
require 'json'
|
|
2
3
|
require 'aws-sdk-core'
|
|
3
4
|
require 'time'
|
|
5
|
+
require 'concurrent'
|
|
4
6
|
|
|
5
7
|
require 'shoryuken/version'
|
|
6
8
|
require 'shoryuken/core_ext'
|
|
@@ -18,8 +20,11 @@ require 'shoryuken/middleware/server/auto_delete'
|
|
|
18
20
|
Shoryuken::Middleware::Server.autoload :AutoExtendVisibility, 'shoryuken/middleware/server/auto_extend_visibility'
|
|
19
21
|
require 'shoryuken/middleware/server/exponential_backoff_retry'
|
|
20
22
|
require 'shoryuken/middleware/server/timing'
|
|
21
|
-
require 'shoryuken/
|
|
22
|
-
require 'shoryuken/
|
|
23
|
+
require 'shoryuken/polling'
|
|
24
|
+
require 'shoryuken/manager'
|
|
25
|
+
require 'shoryuken/launcher'
|
|
26
|
+
require 'shoryuken/processor'
|
|
27
|
+
require 'shoryuken/fetcher'
|
|
23
28
|
|
|
24
29
|
module Shoryuken
|
|
25
30
|
DEFAULTS = {
|
|
@@ -31,45 +36,86 @@ module Shoryuken
|
|
|
31
36
|
lifecycle_events: {
|
|
32
37
|
startup: [],
|
|
33
38
|
quiet: [],
|
|
34
|
-
shutdown: []
|
|
35
|
-
}
|
|
36
|
-
|
|
39
|
+
shutdown: []
|
|
40
|
+
},
|
|
41
|
+
polling_strategy: Polling::WeightedRoundRobin
|
|
42
|
+
}.freeze
|
|
37
43
|
|
|
38
|
-
@@queues
|
|
39
|
-
@@worker_registry
|
|
44
|
+
@@queues = []
|
|
45
|
+
@@worker_registry = DefaultWorkerRegistry.new
|
|
40
46
|
@@active_job_queue_name_prefixing = false
|
|
47
|
+
@@sqs_client = nil
|
|
48
|
+
@@sqs_client_receive_message_opts = {}
|
|
49
|
+
@@start_callback = nil
|
|
50
|
+
@@stop_callback = nil
|
|
41
51
|
|
|
42
52
|
class << self
|
|
43
|
-
def options
|
|
44
|
-
@options ||= DEFAULTS.dup
|
|
45
|
-
end
|
|
46
|
-
|
|
47
53
|
def queues
|
|
48
54
|
@@queues
|
|
49
55
|
end
|
|
50
56
|
|
|
51
|
-
def
|
|
52
|
-
|
|
57
|
+
def add_queue(queue, priority = 1)
|
|
58
|
+
priority.times { queues << queue }
|
|
53
59
|
end
|
|
54
60
|
|
|
55
|
-
def
|
|
56
|
-
worker_registry
|
|
61
|
+
def worker_registry
|
|
62
|
+
@@worker_registry
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
def worker_registry=(worker_registry)
|
|
60
66
|
@@worker_registry = worker_registry
|
|
61
67
|
end
|
|
62
68
|
|
|
63
|
-
def
|
|
64
|
-
@@
|
|
69
|
+
def start_callback
|
|
70
|
+
@@start_callback
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def start_callback=(start_callback)
|
|
74
|
+
@@start_callback = start_callback
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def stop_callback
|
|
78
|
+
@@stop_callback
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def stop_callback=(stop_callback)
|
|
82
|
+
@@stop_callback = stop_callback
|
|
65
83
|
end
|
|
66
84
|
|
|
67
85
|
def active_job_queue_name_prefixing
|
|
68
86
|
@@active_job_queue_name_prefixing
|
|
69
87
|
end
|
|
70
88
|
|
|
71
|
-
def active_job_queue_name_prefixing=(
|
|
72
|
-
@@active_job_queue_name_prefixing =
|
|
89
|
+
def active_job_queue_name_prefixing=(active_job_queue_name_prefixing)
|
|
90
|
+
@@active_job_queue_name_prefixing = active_job_queue_name_prefixing
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def sqs_client
|
|
94
|
+
@@sqs_client ||= Aws::SQS::Client.new
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def sqs_client=(sqs_client)
|
|
98
|
+
@@sqs_client
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def sqs_client_receive_message_opts
|
|
102
|
+
@@sqs_client_receive_message_opts
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def sqs_client_receive_message_opts=(sqs_client_receive_message_opts)
|
|
106
|
+
@@sqs_client_receive_message_opts
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def options
|
|
110
|
+
@@options ||= DEFAULTS.dup
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def logger
|
|
114
|
+
Shoryuken::Logging.logger
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def register_worker(*args)
|
|
118
|
+
@@worker_registry.register_worker(*args)
|
|
73
119
|
end
|
|
74
120
|
|
|
75
121
|
def configure_server
|
|
@@ -77,19 +123,19 @@ module Shoryuken
|
|
|
77
123
|
end
|
|
78
124
|
|
|
79
125
|
def server_middleware
|
|
80
|
-
|
|
81
|
-
yield
|
|
82
|
-
|
|
126
|
+
@@server_chain ||= default_server_middleware
|
|
127
|
+
yield @@server_chain if block_given?
|
|
128
|
+
@@server_chain
|
|
83
129
|
end
|
|
84
130
|
|
|
85
131
|
def configure_client
|
|
86
|
-
yield self
|
|
132
|
+
yield self unless server?
|
|
87
133
|
end
|
|
88
134
|
|
|
89
135
|
def client_middleware
|
|
90
|
-
|
|
91
|
-
yield
|
|
92
|
-
|
|
136
|
+
@@client_chain ||= default_client_middleware
|
|
137
|
+
yield @@client_chain if block_given?
|
|
138
|
+
@@client_chain
|
|
93
139
|
end
|
|
94
140
|
|
|
95
141
|
def default_worker_options
|
|
@@ -99,23 +145,20 @@ module Shoryuken
|
|
|
99
145
|
'auto_delete' => false,
|
|
100
146
|
'auto_visibility_timeout' => false,
|
|
101
147
|
'retry_intervals' => nil,
|
|
102
|
-
'batch' => false
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def default_worker_options=(options)
|
|
106
|
-
@@default_worker_options = options
|
|
148
|
+
'batch' => false
|
|
149
|
+
}
|
|
107
150
|
end
|
|
108
151
|
|
|
109
|
-
def
|
|
110
|
-
|
|
152
|
+
def default_worker_options=(default_worker_options)
|
|
153
|
+
@@default_worker_options = default_worker_options
|
|
111
154
|
end
|
|
112
155
|
|
|
113
156
|
def on_start(&block)
|
|
114
|
-
|
|
157
|
+
@@start_callback = block
|
|
115
158
|
end
|
|
116
159
|
|
|
117
160
|
def on_stop(&block)
|
|
118
|
-
|
|
161
|
+
@@stop_callback = block
|
|
119
162
|
end
|
|
120
163
|
|
|
121
164
|
# Register a block to run at a point in the Shoryuken lifecycle.
|
|
@@ -132,10 +175,6 @@ module Shoryuken
|
|
|
132
175
|
options[:lifecycle_events][event] << block
|
|
133
176
|
end
|
|
134
177
|
|
|
135
|
-
attr_reader :aws_initialization_callback,
|
|
136
|
-
:start_callback,
|
|
137
|
-
:stop_callback
|
|
138
|
-
|
|
139
178
|
private
|
|
140
179
|
|
|
141
180
|
def default_server_middleware
|
data/shoryuken.gemspec
CHANGED
|
@@ -6,14 +6,14 @@ require 'shoryuken/version'
|
|
|
6
6
|
Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = 'shoryuken'
|
|
8
8
|
spec.version = Shoryuken::VERSION
|
|
9
|
-
spec.authors = ['Pablo Cantero'
|
|
10
|
-
spec.email = ['pablo@pablocantero.com'
|
|
11
|
-
spec.description = spec.summary =
|
|
9
|
+
spec.authors = ['Pablo Cantero']
|
|
10
|
+
spec.email = ['pablo@pablocantero.com']
|
|
11
|
+
spec.description = spec.summary = 'Shoryuken is a super efficient AWS SQS thread based message processor'
|
|
12
12
|
spec.homepage = 'https://github.com/phstc/shoryuken'
|
|
13
13
|
spec.license = 'LGPL-3.0'
|
|
14
14
|
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
|
16
|
-
spec.executables = %w
|
|
16
|
+
spec.executables = %w(shoryuken)
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
18
|
spec.require_paths = ['lib']
|
|
19
19
|
|
|
@@ -21,9 +21,9 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.add_development_dependency 'rake'
|
|
22
22
|
spec.add_development_dependency 'rspec'
|
|
23
23
|
spec.add_development_dependency 'pry-byebug'
|
|
24
|
-
spec.add_development_dependency 'nokogiri'
|
|
25
24
|
spec.add_development_dependency 'dotenv'
|
|
26
25
|
|
|
27
26
|
spec.add_dependency 'aws-sdk-core', '~> 2'
|
|
28
|
-
spec.add_dependency '
|
|
27
|
+
spec.add_dependency 'concurrent-ruby'
|
|
28
|
+
spec.add_dependency 'thor'
|
|
29
29
|
end
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
require 'shoryuken/manager'
|
|
3
3
|
require 'shoryuken/launcher'
|
|
4
|
+
require 'securerandom'
|
|
4
5
|
|
|
5
6
|
RSpec.describe Shoryuken::Launcher do
|
|
6
7
|
describe 'Consuming messages', slow: :true do
|
|
7
8
|
before do
|
|
8
|
-
Shoryuken.options[:aws][:receive_message] = { wait_time_seconds: 5 }
|
|
9
|
-
|
|
10
9
|
StandardWorker.received_messages = 0
|
|
11
10
|
|
|
12
11
|
queue = "test_shoryuken#{StandardWorker}_#{SecureRandom.uuid}"
|
|
@@ -21,7 +20,9 @@ RSpec.describe Shoryuken::Launcher do
|
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
after do
|
|
24
|
-
queue_url = Shoryuken::Client.sqs.get_queue_url(
|
|
23
|
+
queue_url = Shoryuken::Client.sqs.get_queue_url(
|
|
24
|
+
queue_name: StandardWorker.get_shoryuken_options['queue']
|
|
25
|
+
).queue_url
|
|
25
26
|
|
|
26
27
|
Shoryuken::Client.sqs.delete_queue queue_url: queue_url
|
|
27
28
|
end
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
describe Shoryuken::Client do
|
|
3
|
+
RSpec.describe Shoryuken::Client 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
7
|
let(:queue_url) { 'https://eu-west-1.amazonaws.com:6059/123456789012/shoryuken' }
|
|
8
|
-
let(:sqs_endpoint) { 'http://localhost:4568' }
|
|
9
|
-
let(:sns_endpoint) { 'http://0.0.0.0:4568' }
|
|
10
8
|
|
|
11
9
|
describe '.queue' do
|
|
12
10
|
before do
|
|
13
11
|
described_class.sqs = sqs
|
|
14
12
|
end
|
|
13
|
+
|
|
15
14
|
it 'memoizes queues' do
|
|
16
15
|
sqs.stub_responses(:get_queue_url, { queue_url: queue_url }, { queue_url: 'xyz' })
|
|
17
16
|
|
|
@@ -19,44 +18,4 @@ describe Shoryuken::Client do
|
|
|
19
18
|
expect(Shoryuken::Client.queues(queue_name).url).to eq queue_url
|
|
20
19
|
end
|
|
21
20
|
end
|
|
22
|
-
|
|
23
|
-
describe 'environment variable endpoints' do
|
|
24
|
-
before do
|
|
25
|
-
ENV['AWS_SQS_ENDPOINT'] = sqs_endpoint
|
|
26
|
-
ENV['AWS_SNS_ENDPOINT'] = sns_endpoint
|
|
27
|
-
ENV['AWS_REGION'] = 'us-east-1'
|
|
28
|
-
Shoryuken.options[:aws] = {}
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'will use config file settings if set' do
|
|
32
|
-
load_config_file_by_file_name('shoryuken_endpoint.yml')
|
|
33
|
-
expect(described_class.sqs.config.endpoint.to_s).to eql('https://github.com/phstc/shoryuken:4568')
|
|
34
|
-
expect(described_class.sns.config.endpoint.to_s).to eq('http://127.0.0.1:4568')
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it 'should fallback to environment variable if config file not found or set' do
|
|
38
|
-
load_config_file_by_file_name(nil)
|
|
39
|
-
expect(described_class.sqs.config.endpoint.to_s).to eql(sqs_endpoint)
|
|
40
|
-
expect(described_class.sns.config.endpoint.to_s).to eq(sns_endpoint)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'should fallback to environment variable if config file found but settings not set' do
|
|
44
|
-
load_config_file_by_file_name('shoryuken.yml')
|
|
45
|
-
expect(described_class.sqs.config.endpoint.to_s).to eql(sqs_endpoint)
|
|
46
|
-
expect(described_class.sns.config.endpoint.to_s).to eq(sns_endpoint)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it 'will fallback to default settings if no config file settings or environment variables found' do
|
|
50
|
-
ENV['AWS_SQS_ENDPOINT'] = nil
|
|
51
|
-
ENV['AWS_SNS_ENDPOINT'] = nil
|
|
52
|
-
load_config_file_by_file_name('shoryuken.yml')
|
|
53
|
-
expect(described_class.sqs.config.endpoint.to_s).to eql('https://sqs.us-east-1.amazonaws.com')
|
|
54
|
-
expect(described_class.sns.config.endpoint.to_s).to eq('https://sns.us-east-1.amazonaws.com')
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def load_config_file_by_file_name(file_name)
|
|
59
|
-
path_name = file_name ? File.join(File.expand_path('../../..', __FILE__), 'spec', file_name) : nil
|
|
60
|
-
Shoryuken::EnvironmentLoader.load(config_file: path_name)
|
|
61
|
-
end
|
|
62
21
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# rubocop:disable Metrics/BlockLength
|
|
4
|
+
RSpec.describe Shoryuken::DefaultWorkerRegistry do
|
|
4
5
|
class RegistryTestWorker
|
|
5
6
|
include Shoryuken::Worker
|
|
6
7
|
|
|
@@ -42,16 +43,17 @@ describe Shoryuken::DefaultWorkerRegistry do
|
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
describe 'a registry with workers is handling messages' do
|
|
45
|
-
def build_message
|
|
46
|
+
def build_message(queue, explicit_worker = nil)
|
|
46
47
|
attributes = {}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
data_type: 'String' }
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
|
|
49
|
+
if explicit_worker
|
|
50
|
+
attributes['shoryuken_class'] = { string_value: explicit_worker.to_s, data_type: 'String' }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
double(Shoryuken::Message,
|
|
54
|
+
body: 'test',
|
|
55
|
+
message_attributes: attributes,
|
|
56
|
+
message_id: SecureRandom.uuid)
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
context 'a batch of messages is being processed' do
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# rubocop:disable Metrics/BlockLength
|
|
4
|
+
RSpec.describe Shoryuken::EnvironmentLoader do
|
|
5
|
+
subject { described_class.new({}) }
|
|
6
|
+
|
|
7
|
+
describe '#parse_queues' do
|
|
8
|
+
before do
|
|
9
|
+
# TODO proper test other methods
|
|
10
|
+
allow(subject).to receive(:load_rails).with(anything)
|
|
11
|
+
allow(subject).to receive(:prefix_active_job_queue_names)
|
|
12
|
+
allow(subject).to receive(:require_workers)
|
|
13
|
+
allow(subject).to receive(:validate_queues)
|
|
14
|
+
allow(subject).to receive(:validate_workers)
|
|
15
|
+
allow(subject).to receive(:patch_deprecated_workers)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'parses' do
|
|
19
|
+
Shoryuken.options[:queues] = ['queue_1']
|
|
20
|
+
subject.load
|
|
21
|
+
|
|
22
|
+
expect(Shoryuken.queues).to eq(%w(queue_1))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'with priority' do
|
|
26
|
+
it 'parses' do
|
|
27
|
+
Shoryuken.options[:queues] = ['queue_1', ['queue_2', 2]]
|
|
28
|
+
subject.load
|
|
29
|
+
|
|
30
|
+
expect(Shoryuken.queues).to eq(%w(queue_1 queue_2 queue_2))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -3,69 +3,35 @@ require 'shoryuken/manager'
|
|
|
3
3
|
require 'shoryuken/fetcher'
|
|
4
4
|
|
|
5
5
|
describe Shoryuken::Fetcher do
|
|
6
|
-
let(:
|
|
7
|
-
let(:queue) { double Shoryuken::Queue }
|
|
6
|
+
let(:queue) { instance_double('Shoryuken::Queue') }
|
|
8
7
|
let(:queue_name) { 'default' }
|
|
8
|
+
let(:queue_config) { Shoryuken::Polling::QueueConfiguration.new(queue_name, {}) }
|
|
9
9
|
|
|
10
10
|
let(:sqs_msg) do
|
|
11
|
-
double
|
|
11
|
+
double(Shoryuken::Message,
|
|
12
12
|
queue_url: queue_name,
|
|
13
13
|
body: 'test',
|
|
14
|
-
message_id: '
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
subject { described_class.new(manager) }
|
|
18
|
-
|
|
19
|
-
before do
|
|
20
|
-
allow(manager).to receive(:async).and_return(manager)
|
|
21
|
-
allow(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
|
|
14
|
+
message_id: 'fc754df79cc24c4196ca5996a44b771e',
|
|
15
|
+
)
|
|
22
16
|
end
|
|
23
17
|
|
|
18
|
+
subject { described_class.new }
|
|
24
19
|
|
|
25
20
|
describe '#fetch' do
|
|
26
|
-
it 'calls
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
subject.fetch(queue_name, 1)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'assigns messages' do
|
|
36
|
-
allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, attribute_names: ['All'], message_attribute_names: ['All']).and_return(sqs_msg)
|
|
37
|
-
|
|
38
|
-
expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
|
|
39
|
-
expect(manager).to receive(:assign).with(queue_name, sqs_msg)
|
|
40
|
-
expect(manager).to receive(:dispatch)
|
|
41
|
-
|
|
42
|
-
subject.fetch(queue_name, 5)
|
|
21
|
+
it 'calls Shoryuken::Client to receive messages' do
|
|
22
|
+
expect(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
|
|
23
|
+
expect(queue).to receive(:receive_messages).
|
|
24
|
+
with(max_number_of_messages: 1, attribute_names: ['All'], message_attribute_names: ['All']).
|
|
25
|
+
and_return([])
|
|
26
|
+
subject.fetch(queue_config, 1)
|
|
43
27
|
end
|
|
44
28
|
|
|
45
|
-
it '
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
expect(manager).to receive(:assign).with(queue_name, [sqs_msg])
|
|
52
|
-
expect(manager).to receive(:dispatch)
|
|
53
|
-
|
|
54
|
-
subject.fetch(queue_name, 5)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
context 'when worker not found' do
|
|
58
|
-
let(:queue_name) { 'notfound' }
|
|
59
|
-
|
|
60
|
-
it 'ignores batch' do
|
|
61
|
-
allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, attribute_names: ['All'], message_attribute_names: ['All']).and_return(sqs_msg)
|
|
62
|
-
|
|
63
|
-
expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
|
|
64
|
-
expect(manager).to receive(:assign).with(queue_name, sqs_msg)
|
|
65
|
-
expect(manager).to receive(:dispatch)
|
|
66
|
-
|
|
67
|
-
subject.fetch(queue_name, 5)
|
|
68
|
-
end
|
|
29
|
+
it 'maxes messages to receive to 10 (SQS limit)' do
|
|
30
|
+
allow(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
|
|
31
|
+
expect(queue).to receive(:receive_messages).
|
|
32
|
+
with(max_number_of_messages: 10, attribute_names: ['All'], message_attribute_names: ['All']).
|
|
33
|
+
and_return([])
|
|
34
|
+
subject.fetch(queue_config, 20)
|
|
69
35
|
end
|
|
70
36
|
end
|
|
71
37
|
end
|
|
@@ -1,122 +1,81 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
require 'shoryuken/manager'
|
|
3
3
|
|
|
4
|
+
RSpec::Matchers.define :queue_config_of do |expected|
|
|
5
|
+
match do |actual|
|
|
6
|
+
actual.name == expected
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
4
10
|
RSpec.describe Shoryuken::Manager do
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
let(:queue) { 'default' }
|
|
12
|
+
let(:queues) { [queue] }
|
|
13
|
+
let(:polling_strategy) { Shoryuken::Polling::WeightedRoundRobin.new(queues) }
|
|
14
|
+
let(:fetcher) { Shoryuken::Fetcher.new }
|
|
15
|
+
let(:concurrency) { 1 }
|
|
16
|
+
|
|
17
|
+
subject { Shoryuken::Manager.new(fetcher, polling_strategy) }
|
|
18
|
+
|
|
19
|
+
before(:each) do
|
|
20
|
+
Shoryuken.options[:concurrency] = concurrency
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
after(:each) do
|
|
24
|
+
Shoryuken.options[:concurrency] = 1
|
|
25
|
+
TestWorker.get_shoryuken_options['batch'] = false
|
|
9
26
|
end
|
|
10
27
|
|
|
11
28
|
describe 'Invalid concurrency setting' do
|
|
12
29
|
it 'raises ArgumentError if concurrency is not positive number' do
|
|
13
30
|
Shoryuken.options[:concurrency] = -1
|
|
14
|
-
expect { Shoryuken::Manager.new(nil) }
|
|
31
|
+
expect { Shoryuken::Manager.new(nil, nil) }
|
|
15
32
|
.to raise_error(ArgumentError, 'Concurrency value -1 is invalid, it needs to be a positive number')
|
|
16
33
|
end
|
|
17
|
-
|
|
18
34
|
end
|
|
19
35
|
|
|
20
|
-
describe '
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Shoryuken.queues.clear
|
|
26
|
-
# [shoryuken, 2]
|
|
27
|
-
# [uppercut, 1]
|
|
28
|
-
Shoryuken.queues << queue1
|
|
29
|
-
Shoryuken.queues << queue1
|
|
30
|
-
Shoryuken.queues << queue2
|
|
31
|
-
|
|
32
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue1, queue2]
|
|
33
|
-
|
|
34
|
-
subject.pause_queue!(queue1)
|
|
35
|
-
|
|
36
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue2]
|
|
36
|
+
describe '#start' do
|
|
37
|
+
xit 'pauses when there are no active queues' do
|
|
38
|
+
expect(polling_strategy).to receive(:next_queue).and_return(nil)
|
|
39
|
+
expect_any_instance_of(described_class).to receive(:after)
|
|
40
|
+
subject.start
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# [shoryuken, 3]
|
|
45
|
-
# [uppercut, 1]
|
|
46
|
-
Shoryuken.queues << queue1
|
|
47
|
-
Shoryuken.queues << queue1
|
|
48
|
-
Shoryuken.queues << queue1
|
|
49
|
-
Shoryuken.queues << queue2
|
|
50
|
-
|
|
51
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue1, queue2]
|
|
52
|
-
subject.pause_queue!(queue1)
|
|
53
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue2]
|
|
54
|
-
|
|
55
|
-
subject.rebalance_queue_weight!(queue1)
|
|
56
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue2, queue1]
|
|
57
|
-
|
|
58
|
-
subject.rebalance_queue_weight!(queue1)
|
|
59
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue2, queue1, queue1]
|
|
60
|
-
|
|
61
|
-
subject.rebalance_queue_weight!(queue1)
|
|
62
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue2, queue1, queue1, queue1]
|
|
43
|
+
xit 'calls dispatch_batch if worker wants batches' do
|
|
44
|
+
TestWorker.get_shoryuken_options['batch'] = true
|
|
45
|
+
expect_any_instance_of(described_class).to receive(:dispatch_batch).with(queue_config_of(queue))
|
|
46
|
+
expect(subject).to receive(:dispatch_later)
|
|
47
|
+
subject.start
|
|
63
48
|
end
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# [shoryuken, 2]
|
|
71
|
-
# [uppercut, 1]
|
|
72
|
-
Shoryuken.queues << queue1
|
|
73
|
-
Shoryuken.queues << queue1
|
|
74
|
-
Shoryuken.queues << queue2
|
|
75
|
-
|
|
76
|
-
Shoryuken.options[:delay] = 0.1
|
|
77
|
-
|
|
78
|
-
fetcher = double('Fetcher').as_null_object
|
|
79
|
-
subject.fetcher = fetcher
|
|
80
|
-
|
|
81
|
-
subject.pause_queue!(queue1)
|
|
82
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue2]
|
|
83
|
-
|
|
84
|
-
sleep 0.5
|
|
85
|
-
|
|
86
|
-
expect(subject.instance_variable_get('@queues')).to eq [queue2, queue1]
|
|
50
|
+
xit 'calls dispatch_single_messages if worker wants single messages' do
|
|
51
|
+
expect_any_instance_of(described_class).to receive(:dispatch_single_messages).
|
|
52
|
+
with(queue_config_of(queue))
|
|
53
|
+
expect(subject).to receive(:dispatch_later)
|
|
54
|
+
subject.start
|
|
87
55
|
end
|
|
88
56
|
end
|
|
89
57
|
|
|
90
|
-
describe '#
|
|
91
|
-
it '
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Shoryuken.register_worker queue1, TestWorker
|
|
98
|
-
Shoryuken.register_worker queue2, TestWorker
|
|
99
|
-
|
|
100
|
-
Shoryuken.queues << queue1
|
|
101
|
-
Shoryuken.queues << queue2
|
|
102
|
-
|
|
103
|
-
expect(subject.send :next_queue).to eq queue1
|
|
104
|
-
expect(subject.send :next_queue).to eq queue2
|
|
58
|
+
describe '#dispatch_batch' do
|
|
59
|
+
it 'assings batch as a single message' do
|
|
60
|
+
q = polling_strategy.next_queue
|
|
61
|
+
messages = [1, 2, 3]
|
|
62
|
+
expect(fetcher).to receive(:fetch).with(q, described_class::BATCH_LIMIT).and_return(messages)
|
|
63
|
+
expect_any_instance_of(described_class).to receive(:assign).with(q.name, messages)
|
|
64
|
+
subject.send(:dispatch_batch, q)
|
|
105
65
|
end
|
|
66
|
+
end
|
|
106
67
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
expect(subject.send :next_queue).to eq queue2
|
|
119
|
-
expect(subject.send :next_queue).to eq queue2
|
|
68
|
+
describe '#dispatch_single_messages' do
|
|
69
|
+
let(:concurrency) { 3 }
|
|
70
|
+
|
|
71
|
+
it 'assings messages from batch one by one' do
|
|
72
|
+
q = polling_strategy.next_queue
|
|
73
|
+
messages = [1, 2, 3]
|
|
74
|
+
expect(fetcher).to receive(:fetch).with(q, concurrency).and_return(messages)
|
|
75
|
+
expect_any_instance_of(described_class).to receive(:assign).with(q.name, 1)
|
|
76
|
+
expect_any_instance_of(described_class).to receive(:assign).with(q.name, 2)
|
|
77
|
+
expect_any_instance_of(described_class).to receive(:assign).with(q.name, 3)
|
|
78
|
+
subject.send(:dispatch_single_messages, q)
|
|
120
79
|
end
|
|
121
80
|
end
|
|
122
81
|
end
|