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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +20 -0
  3. data/.rubocop.yml +8 -2
  4. data/.travis.yml +7 -5
  5. data/CHANGELOG.md +92 -10
  6. data/Gemfile +1 -0
  7. data/README.md +20 -57
  8. data/Rakefile +0 -1
  9. data/bin/cli/base.rb +42 -0
  10. data/bin/cli/sqs.rb +188 -0
  11. data/bin/shoryuken +47 -9
  12. data/examples/default_worker.rb +1 -12
  13. data/lib/shoryuken/client.rb +3 -25
  14. data/lib/shoryuken/default_worker_registry.rb +9 -5
  15. data/lib/shoryuken/environment_loader.rb +29 -67
  16. data/lib/shoryuken/fetcher.rb +22 -53
  17. data/lib/shoryuken/launcher.rb +5 -29
  18. data/lib/shoryuken/manager.rb +72 -184
  19. data/lib/shoryuken/message.rb +4 -13
  20. data/lib/shoryuken/middleware/chain.rb +1 -18
  21. data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +21 -18
  22. data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +26 -19
  23. data/lib/shoryuken/polling.rb +204 -0
  24. data/lib/shoryuken/processor.rb +6 -14
  25. data/lib/shoryuken/queue.rb +36 -38
  26. data/lib/shoryuken/runner.rb +143 -0
  27. data/lib/shoryuken/util.rb +3 -9
  28. data/lib/shoryuken/version.rb +1 -1
  29. data/lib/shoryuken/worker.rb +1 -1
  30. data/lib/shoryuken.rb +78 -39
  31. data/shoryuken.gemspec +6 -6
  32. data/spec/integration/launcher_spec.rb +4 -3
  33. data/spec/shoryuken/client_spec.rb +2 -43
  34. data/spec/shoryuken/default_worker_registry_spec.rb +12 -10
  35. data/spec/shoryuken/environment_loader_spec.rb +34 -0
  36. data/spec/shoryuken/fetcher_spec.rb +18 -52
  37. data/spec/shoryuken/manager_spec.rb +56 -97
  38. data/spec/shoryuken/middleware/chain_spec.rb +0 -24
  39. data/spec/shoryuken/middleware/server/auto_delete_spec.rb +2 -2
  40. data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +7 -3
  41. data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +56 -33
  42. data/spec/shoryuken/polling_spec.rb +239 -0
  43. data/spec/shoryuken/processor_spec.rb +5 -5
  44. data/spec/shoryuken/queue_spec.rb +110 -63
  45. data/spec/shoryuken/{cli_spec.rb → runner_spec.rb} +10 -24
  46. data/spec/shoryuken_spec.rb +13 -1
  47. data/spec/spec_helper.rb +8 -20
  48. data/test_workers/endless_interruptive_worker.rb +41 -0
  49. data/test_workers/endless_uninterruptive_worker.rb +44 -0
  50. metadata +34 -35
  51. data/.hound.yml +0 -6
  52. data/lib/shoryuken/cli.rb +0 -210
  53. data/lib/shoryuken/sns_arn.rb +0 -27
  54. data/lib/shoryuken/topic.rb +0 -17
  55. data/spec/shoryuken/sns_arn_spec.rb +0 -42
  56. data/spec/shoryuken/topic_spec.rb +0 -32
  57. data/spec/shoryuken_endpoint.yml +0 -6
  58. /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/sns_arn'
22
- require 'shoryuken/topic'
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 = DefaultWorkerRegistry.new
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 logger
52
- Shoryuken::Logging.logger
57
+ def add_queue(queue, priority = 1)
58
+ priority.times { queues << queue }
53
59
  end
54
60
 
55
- def register_worker(*args)
56
- worker_registry.register_worker(*args)
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 worker_registry
64
- @@worker_registry
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=(prefixing)
72
- @@active_job_queue_name_prefixing = 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
- @server_chain ||= default_server_middleware
81
- yield @server_chain if block_given?
82
- @server_chain
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
- @client_chain ||= default_client_middleware
91
- yield @client_chain if block_given?
92
- @client_chain
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
- end
104
-
105
- def default_worker_options=(options)
106
- @@default_worker_options = options
148
+ 'batch' => false
149
+ }
107
150
  end
108
151
 
109
- def on_aws_initialization(&block)
110
- @aws_initialization_callback = block
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
- @start_callback = block
157
+ @@start_callback = block
115
158
  end
116
159
 
117
160
  def on_stop(&block)
118
- @stop_callback = block
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', 'Mario Kostelac']
10
- spec.email = ['pablo@pablocantero.com', 'mariokostelac@gmail.com']
11
- spec.description = spec.summary = %q(Shoryuken is a super efficient AWS SQS thread based message processor)
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[shoryuken]
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 'celluloid', '~> 0.16'
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(queue_name: StandardWorker.get_shoryuken_options['queue']).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
- describe Shoryuken::DefaultWorkerRegistry do
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 queue, explicit_worker = nil
46
+ def build_message(queue, explicit_worker = nil)
46
47
  attributes = {}
47
- attributes['shoryuken_class'] = {
48
- string_value: explicit_worker.to_s,
49
- data_type: 'String' } if explicit_worker
50
-
51
- double Shoryuken::Message,
52
- body: 'test',
53
- message_attributes: attributes,
54
- message_id: SecureRandom.uuid
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(:manager) { double Shoryuken::Manager }
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 Shoryuken::Message,
11
+ double(Shoryuken::Message,
12
12
  queue_url: queue_name,
13
13
  body: 'test',
14
- message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e'
15
- end
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 pause when no message' do
27
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: 1, attribute_names: ['All'], message_attribute_names: ['All']).and_return([])
28
-
29
- expect(manager).to receive(:pause_queue!).with(queue_name)
30
- expect(manager).to receive(:dispatch)
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 'assigns messages in batch' do
46
- TestWorker.get_shoryuken_options['batch'] = true
47
-
48
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: described_class::FETCH_LIMIT, attribute_names: ['All'], message_attribute_names: ['All']).and_return(sqs_msg)
49
-
50
- expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
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
- subject do
6
- condvar = double(:condvar)
7
- allow(condvar).to receive(:signal).and_return(nil)
8
- Shoryuken::Manager.new(condvar)
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 'Auto Scaling' do
21
- it 'decreases weight' do
22
- queue1 = 'shoryuken'
23
- queue2 = 'uppercut'
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
- it 'increases weight' do
40
- queue1 = 'shoryuken'
41
- queue2 = 'uppercut'
42
-
43
- Shoryuken.queues.clear
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
- it 'adds queue back' do
66
- queue1 = 'shoryuken'
67
- queue2 = 'uppercut'
68
-
69
- Shoryuken.queues.clear
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 '#next_queue' do
91
- it 'returns queues' do
92
- queue1 = 'shoryuken'
93
- queue2 = 'uppercut'
94
-
95
- Shoryuken.queues.clear
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
- it 'skips when no worker' do
108
- queue1 = 'shoryuken'
109
- queue2 = 'uppercut'
110
-
111
- Shoryuken.queues.clear
112
-
113
- Shoryuken.register_worker queue2, TestWorker
114
-
115
- Shoryuken.queues << queue1
116
- Shoryuken.queues << queue2
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