shoryuken 3.1.1 → 3.2.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +13 -3
  4. data/CHANGELOG.md +82 -1
  5. data/Gemfile +5 -2
  6. data/Gemfile.aws-sdk-core-v2 +13 -0
  7. data/bin/cli/sqs.rb +8 -4
  8. data/bin/shoryuken +2 -1
  9. data/lib/shoryuken/body_parser.rb +27 -0
  10. data/lib/shoryuken/environment_loader.rb +25 -11
  11. data/lib/shoryuken/fetcher.rb +40 -13
  12. data/lib/shoryuken/launcher.rb +4 -20
  13. data/lib/shoryuken/logging.rb +0 -5
  14. data/lib/shoryuken/manager.rb +31 -22
  15. data/lib/shoryuken/middleware/chain.rb +4 -0
  16. data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +2 -5
  17. data/lib/shoryuken/middleware/server/timing.rb +12 -14
  18. data/lib/shoryuken/options.rb +25 -1
  19. data/lib/shoryuken/processor.rb +9 -21
  20. data/lib/shoryuken/queue.rb +12 -5
  21. data/lib/shoryuken/runner.rb +3 -1
  22. data/lib/shoryuken/util.rb +3 -3
  23. data/lib/shoryuken/version.rb +1 -1
  24. data/lib/shoryuken/worker/default_executor.rb +33 -0
  25. data/lib/shoryuken/worker/inline_executor.rb +28 -0
  26. data/lib/shoryuken/worker.rb +68 -31
  27. data/lib/shoryuken.rb +14 -1
  28. data/shoryuken.gemspec +1 -1
  29. data/spec/shoryuken/body_parser_spec.rb +89 -0
  30. data/spec/shoryuken/environment_loader_spec.rb +32 -3
  31. data/spec/shoryuken/fetcher_spec.rb +61 -9
  32. data/spec/shoryuken/manager_spec.rb +31 -18
  33. data/spec/shoryuken/middleware/chain_spec.rb +16 -4
  34. data/spec/shoryuken/middleware/server/timing_spec.rb +5 -3
  35. data/spec/shoryuken/options_spec.rb +80 -0
  36. data/spec/shoryuken/processor_spec.rb +15 -97
  37. data/spec/shoryuken/queue_spec.rb +43 -32
  38. data/spec/shoryuken/util_spec.rb +25 -1
  39. data/spec/shoryuken/worker/default_executor_spec.rb +100 -0
  40. data/spec/shoryuken/worker/inline_executor_spec.rb +23 -0
  41. data/spec/shoryuken/worker_spec.rb +32 -91
  42. data/spec/spec_helper.rb +5 -0
  43. metadata +15 -5
@@ -21,8 +21,13 @@ module Shoryuken
21
21
  @@sqs_client_receive_message_opts = {}
22
22
  @@start_callback = nil
23
23
  @@stop_callback = nil
24
+ @@worker_executor = Worker::DefaultExecutor
24
25
 
25
26
  class << self
27
+ def active_job?
28
+ defined?(::ActiveJob)
29
+ end
30
+
26
31
  def add_group(group, concurrency)
27
32
  groups[group] ||= {
28
33
  concurrency: concurrency,
@@ -52,8 +57,27 @@ module Shoryuken
52
57
  @@worker_registry = worker_registry
53
58
  end
54
59
 
60
+ def worker_executor
61
+ @@worker_executor
62
+ end
63
+
64
+ def worker_executor=(worker_executor)
65
+ @@worker_executor = worker_executor
66
+ end
67
+
55
68
  def polling_strategy(group)
56
- options[group].to_h.fetch(:polling_strategy, Polling::WeightedRoundRobin)
69
+ strategy = (group == 'default' ? options : options[:groups].to_h[group]).to_h[:polling_strategy]
70
+
71
+ case strategy
72
+ when 'WeightedRoundRobin', nil # Default case
73
+ Polling::WeightedRoundRobin
74
+ when 'StrictPriority'
75
+ Polling::StrictPriority
76
+ when Class
77
+ strategy
78
+ else
79
+ raise ArgumentError, "#{strategy} is not a valid polling_strategy"
80
+ end
57
81
  end
58
82
 
59
83
  def start_callback
@@ -4,6 +4,10 @@ module Shoryuken
4
4
 
5
5
  attr_reader :queue, :sqs_msg
6
6
 
7
+ def self.process(queue, sqs_msg)
8
+ new(queue, sqs_msg).process
9
+ end
10
+
7
11
  def initialize(queue, sqs_msg)
8
12
  @queue = queue
9
13
  @sqs_msg = sqs_msg
@@ -12,8 +16,10 @@ module Shoryuken
12
16
  def process
13
17
  return logger.error { "No worker found for #{queue}" } unless worker
14
18
 
15
- worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
16
- worker.perform(sqs_msg, body)
19
+ Shoryuken::Logging.with_context("#{worker_name(worker.class, sqs_msg, body)}/#{queue}/#{sqs_msg.message_id}") do
20
+ worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
21
+ worker.perform(sqs_msg, body)
22
+ end
17
23
  end
18
24
  rescue Exception => ex
19
25
  logger.error { "Processor failed: #{ex.message}" }
@@ -37,25 +43,7 @@ module Shoryuken
37
43
  end
38
44
 
39
45
  def parse_body(sqs_msg)
40
- body_parser = worker_class.get_shoryuken_options['body_parser']
41
-
42
- case body_parser
43
- when :json
44
- JSON.parse(sqs_msg.body)
45
- when Proc
46
- body_parser.call(sqs_msg)
47
- when :text, nil
48
- sqs_msg.body
49
- else
50
- if body_parser.respond_to?(:parse)
51
- # JSON.parse
52
- body_parser.parse(sqs_msg.body)
53
- elsif body_parser.respond_to?(:load)
54
- # see https://github.com/phstc/shoryuken/pull/91
55
- # JSON.load
56
- body_parser.load(sqs_msg.body)
57
- end
58
- end
46
+ BodyParser.parse(worker_class, sqs_msg)
59
47
  end
60
48
  end
61
49
  end
@@ -49,20 +49,27 @@ module Shoryuken
49
49
 
50
50
  private
51
51
 
52
- def set_name(name)
52
+ def set_by_name(name)
53
53
  self.name = name
54
54
  self.url = client.get_queue_url(queue_name: name).queue_url
55
- rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => ex
56
- raise ex, "The specified queue #{name} does not exist."
57
55
  end
58
56
 
59
- def set_url(url)
57
+ def set_by_url(url)
60
58
  self.name = url.split('/').last
61
59
  self.url = url
62
60
  end
63
61
 
64
62
  def set_name_and_url(name_or_url)
65
- name_or_url.start_with?('https://sqs.') ? set_url(name_or_url) : set_name(name_or_url)
63
+ if name_or_url.start_with?('https://sqs.')
64
+ set_by_url(name_or_url)
65
+
66
+ # anticipate the fifo? checker for validating the queue URL
67
+ return fifo?
68
+ end
69
+
70
+ set_by_name(name_or_url)
71
+ rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue => ex
72
+ raise ex, "The specified queue #{name_or_url} does not exist."
66
73
  end
67
74
 
68
75
  def queue_attributes
@@ -118,10 +118,12 @@ module Shoryuken
118
118
  end
119
119
 
120
120
  def handle_signal(sig)
121
+ logger.debug "Got #{sig} signal"
122
+
121
123
  case sig
122
124
  when 'USR1' then execute_soft_shutdown
123
125
  when 'TTIN' then print_threads_backtrace
124
- else
126
+ when 'TERM', 'INT'
125
127
  logger.info { "Received #{sig}, will shutdown down" }
126
128
 
127
129
  raise Interrupt
@@ -4,13 +4,13 @@ module Shoryuken
4
4
  Shoryuken.logger
5
5
  end
6
6
 
7
- def fire_event(event, reverse = false)
7
+ def fire_event(event, reverse = false, event_options = {})
8
8
  logger.debug { "Firing '#{event}' lifecycle event" }
9
9
  arr = Shoryuken.options[:lifecycle_events][event]
10
10
  arr.reverse! if reverse
11
11
  arr.each do |block|
12
12
  begin
13
- block.call
13
+ block.call(event_options)
14
14
  rescue => ex
15
15
  logger.warn(event: event)
16
16
  logger.warn "#{ex.class.name}: #{ex.message}"
@@ -30,7 +30,7 @@ module Shoryuken
30
30
  end
31
31
 
32
32
  def worker_name(worker_class, sqs_msg, body = nil)
33
- if defined?(::ActiveJob) \
33
+ if Shoryuken.active_job? \
34
34
  && !sqs_msg.is_a?(Array) \
35
35
  && sqs_msg.message_attributes \
36
36
  && sqs_msg.message_attributes['shoryuken_class'] \
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '3.1.1'.freeze
2
+ VERSION = '3.2.0'.freeze
3
3
  end
@@ -0,0 +1,33 @@
1
+ module Shoryuken
2
+ module Worker
3
+ class DefaultExecutor
4
+ class << self
5
+ def perform_async(worker_class, body, options = {})
6
+ options[:message_attributes] ||= {}
7
+ options[:message_attributes]['shoryuken_class'] = {
8
+ string_value: worker_class.to_s,
9
+ data_type: 'String'
10
+ }
11
+
12
+ options[:message_body] = body
13
+
14
+ queue = options.delete(:queue) || worker_class.get_shoryuken_options['queue']
15
+
16
+ Shoryuken::Client.queues(queue).send_message(options)
17
+ end
18
+
19
+ def perform_in(worker_class, interval, body, options = {})
20
+ interval = interval.to_f
21
+ now = Time.now.to_f
22
+ ts = (interval < 1_000_000_000 ? (now + interval).to_f : interval)
23
+
24
+ delay = (ts - now).ceil
25
+
26
+ raise 'The maximum allowed delay is 15 minutes' if delay > 15 * 60
27
+
28
+ worker_class.perform_async(body, options.merge(delay_seconds: delay))
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ module Shoryuken
2
+ module Worker
3
+ class InlineExecutor
4
+ class << self
5
+ def perform_async(worker_class, body, options = {})
6
+ body = JSON.dump(body) if body.is_a?(Hash)
7
+
8
+ sqs_msg = OpenStruct.new(
9
+ body: body,
10
+ attributes: nil,
11
+ md5_of_body: nil,
12
+ md5_of_message_attributes: nil,
13
+ message_attributes: nil,
14
+ message_id: nil,
15
+ receipt_handle: nil,
16
+ delete: nil
17
+ )
18
+
19
+ worker_class.new.perform(sqs_msg, BodyParser.parse(worker_class, sqs_msg))
20
+ end
21
+
22
+ def perform_in(worker_class, _interval, body, options = {})
23
+ perform_async(worker_class, body, options)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -2,33 +2,16 @@ module Shoryuken
2
2
  module Worker
3
3
  def self.included(base)
4
4
  base.extend(ClassMethods)
5
+ base.shoryuken_class_attribute :shoryuken_options_hash
5
6
  end
6
7
 
7
8
  module ClassMethods
8
9
  def perform_async(body, options = {})
9
- options[:message_attributes] ||= {}
10
- options[:message_attributes]['shoryuken_class'] = {
11
- string_value: self.to_s,
12
- data_type: 'String'
13
- }
14
-
15
- options[:message_body] = body
16
-
17
- queue = options.delete(:queue) || get_shoryuken_options['queue']
18
-
19
- Shoryuken::Client.queues(queue).send_message(options)
10
+ Shoryuken.worker_executor.perform_async(self, body, options)
20
11
  end
21
12
 
22
13
  def perform_in(interval, body, options = {})
23
- interval = interval.to_f
24
- now = Time.now.to_f
25
- ts = (interval < 1_000_000_000 ? (now + interval).to_f : interval)
26
-
27
- delay = (ts - now).ceil
28
-
29
- raise 'The maximum allowed delay is 15 minutes' if delay > 15 * 60
30
-
31
- perform_async(body, options.merge(delay_seconds: delay))
14
+ Shoryuken.worker_executor.perform_in(self, interval, body, options)
32
15
  end
33
16
 
34
17
  alias_method :perform_at, :perform_in
@@ -40,7 +23,7 @@ module Shoryuken
40
23
  end
41
24
 
42
25
  def shoryuken_options(opts = {})
43
- @shoryuken_options = get_shoryuken_options.merge(stringify_keys(opts || {}))
26
+ self.shoryuken_options_hash = get_shoryuken_options.merge(stringify_keys(opts || {}))
44
27
  normalize_worker_queue!
45
28
  end
46
29
 
@@ -57,33 +40,87 @@ module Shoryuken
57
40
  end
58
41
 
59
42
  def get_shoryuken_options # :nodoc:
60
- @shoryuken_options || Shoryuken.default_worker_options
43
+ shoryuken_options_hash || Shoryuken.default_worker_options
61
44
  end
62
45
 
63
46
  def stringify_keys(hash) # :nodoc:
64
- hash.keys.each do |key|
65
- hash[key.to_s] = hash.delete(key)
47
+ new_hash = {}
48
+ hash.each { |key, value| new_hash[key.to_s] = value }
49
+ new_hash
50
+ end
51
+
52
+ def shoryuken_class_attribute(*attrs) # :nodoc:
53
+ attrs.each do |name|
54
+ singleton_class.instance_eval do
55
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
56
+ end
57
+ define_singleton_method(name) { nil }
58
+
59
+ ivar = "@#{name}"
60
+
61
+ singleton_class.instance_eval do
62
+ m = "#{name}="
63
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
64
+ end
65
+
66
+ define_singleton_method("#{name}=") do |val|
67
+ singleton_class.class_eval do
68
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
69
+ define_method(name) { val }
70
+ end
71
+
72
+ # singleton? backwards compatibility for ruby < 2.1
73
+ singleton_klass = respond_to?(:singleton?) ? singleton? : self != ancestors.first
74
+
75
+ if singleton_klass
76
+ class_eval do
77
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
78
+ define_method(name) do
79
+ if instance_variable_defined? ivar
80
+ instance_variable_get ivar
81
+ else
82
+ singleton_class.send name
83
+ end
84
+ end
85
+ end
86
+ end
87
+ val
88
+ end
89
+
90
+ # instance reader
91
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
92
+ define_method(name) do
93
+ if instance_variable_defined?(ivar)
94
+ instance_variable_get ivar
95
+ else
96
+ self.class.public_send name
97
+ end
98
+ end
99
+
100
+ # instance writer
101
+ m = "#{name}="
102
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
103
+ attr_writer name
66
104
  end
67
- hash
68
105
  end
69
106
 
70
107
  private
71
108
 
72
109
  def normalize_worker_queue!
73
- queue = @shoryuken_options['queue']
110
+ queue = shoryuken_options_hash['queue']
74
111
  if queue.respond_to?(:call)
75
112
  queue = queue.call
76
- @shoryuken_options['queue'] = queue
113
+ shoryuken_options_hash['queue'] = queue
77
114
  end
78
115
 
79
- case @shoryuken_options['queue']
116
+ case shoryuken_options_hash['queue']
80
117
  when Array
81
- @shoryuken_options['queue'].map!(&:to_s)
118
+ shoryuken_options_hash['queue'].map!(&:to_s)
82
119
  when Symbol
83
- @shoryuken_options['queue'] = @shoryuken_options['queue'].to_s
120
+ shoryuken_options_hash['queue'] = shoryuken_options_hash['queue'].to_s
84
121
  end
85
122
 
86
- [@shoryuken_options['queue']].flatten.compact.each(&method(:register_worker))
123
+ [shoryuken_options_hash['queue']].flatten.compact.each(&method(:register_worker))
87
124
  end
88
125
 
89
126
  def register_worker(queue)
data/lib/shoryuken.rb CHANGED
@@ -1,8 +1,14 @@
1
1
  require 'yaml'
2
2
  require 'json'
3
3
  require 'aws-sdk-core'
4
+ begin
5
+ require 'aws-sdk-sqs' unless defined?(Aws::SQS)
6
+ rescue LoadError
7
+ fail "AWS SDK 3 requires aws-sdk-sqs to be installed separately. Please add gem 'aws-sdk-sqs' to your Gemfile"
8
+ end
4
9
  require 'time'
5
10
  require 'concurrent'
11
+ require 'forwardable'
6
12
 
7
13
  require 'shoryuken/version'
8
14
  require 'shoryuken/core_ext'
@@ -13,6 +19,8 @@ require 'shoryuken/queue'
13
19
  require 'shoryuken/message'
14
20
  require 'shoryuken/client'
15
21
  require 'shoryuken/worker'
22
+ require 'shoryuken/worker/default_executor'
23
+ require 'shoryuken/worker/inline_executor'
16
24
  require 'shoryuken/worker_registry'
17
25
  require 'shoryuken/default_worker_registry'
18
26
  require 'shoryuken/middleware/chain'
@@ -26,20 +34,25 @@ require 'shoryuken/polling/strict_priority'
26
34
  require 'shoryuken/manager'
27
35
  require 'shoryuken/launcher'
28
36
  require 'shoryuken/processor'
37
+ require 'shoryuken/body_parser'
29
38
  require 'shoryuken/fetcher'
30
39
  require 'shoryuken/options'
31
40
 
41
+
32
42
  module Shoryuken
33
43
  extend SingleForwardable
34
44
 
35
45
  def_delegators(
36
46
  :'Shoryuken::Options',
47
+ :active_job?,
37
48
  :add_group,
38
49
  :groups,
39
50
  :add_queue,
40
51
  :ungrouped_queues,
41
52
  :worker_registry,
42
53
  :worker_registry=,
54
+ :worker_executor,
55
+ :worker_executor=,
43
56
  :polling_strategy,
44
57
  :start_callback,
45
58
  :start_callback=,
@@ -67,4 +80,4 @@ module Shoryuken
67
80
  )
68
81
  end
69
82
 
70
- require 'shoryuken/extensions/active_job_adapter' if defined?(::ActiveJob)
83
+ require 'shoryuken/extensions/active_job_adapter' if Shoryuken.active_job?
data/shoryuken.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency 'pry-byebug'
24
24
  spec.add_development_dependency 'dotenv'
25
25
 
26
- spec.add_dependency 'aws-sdk-core', '> 2'
26
+ spec.add_dependency 'aws-sdk-core', '>= 2'
27
27
  spec.add_dependency 'concurrent-ruby'
28
28
  spec.add_dependency 'thor'
29
29
  end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+ require 'shoryuken/body_parser'
3
+
4
+ RSpec.describe Shoryuken::BodyParser do
5
+ let(:sqs_msg) { double }
6
+
7
+ describe '#parser' do
8
+ it 'parses the body into JSON' do
9
+ TestWorker.get_shoryuken_options['body_parser'] = :json
10
+
11
+ body = { 'test' => 'hi' }
12
+
13
+ allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
14
+
15
+ expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
16
+ end
17
+
18
+ it 'parses the body calling the proc' do
19
+ TestWorker.get_shoryuken_options['body_parser'] = proc { |sqs_msg| "*#{sqs_msg.body}*" }
20
+
21
+ allow(sqs_msg).to receive(:body).and_return('test')
22
+
23
+ expect(described_class.parse(TestWorker, sqs_msg)).to eq('*test*')
24
+ end
25
+
26
+ it 'parses the body as text' do
27
+ TestWorker.get_shoryuken_options['body_parser'] = :text
28
+
29
+ body = 'test'
30
+
31
+ allow(sqs_msg).to receive(:body).and_return(body)
32
+
33
+ expect(described_class.parse(TestWorker, sqs_msg)).to eq('test')
34
+ end
35
+
36
+ it 'parses calling `.load`' do
37
+ TestWorker.get_shoryuken_options['body_parser'] = Class.new do
38
+ def self.load(*args)
39
+ JSON.load(*args)
40
+ end
41
+ end
42
+
43
+ body = { 'test' => 'hi' }
44
+
45
+ allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
46
+
47
+ expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
48
+ end
49
+
50
+ it 'parses calling `.parse`' do
51
+ TestWorker.get_shoryuken_options['body_parser'] = Class.new do
52
+ def self.parse(*args)
53
+ JSON.parse(*args)
54
+ end
55
+ end
56
+
57
+ body = { 'test' => 'hi' }
58
+
59
+ allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
60
+
61
+ expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
62
+ end
63
+
64
+ context 'when parse errors' do
65
+ before do
66
+ TestWorker.get_shoryuken_options['body_parser'] = :json
67
+
68
+ allow(sqs_msg).to receive(:body).and_return('invalid JSON')
69
+ end
70
+
71
+ specify do
72
+ expect { described_class.parse(TestWorker, sqs_msg) }.
73
+ to raise_error(JSON::ParserError, /unexpected token at 'invalid JSON'/)
74
+ end
75
+ end
76
+
77
+ context 'when `object_type: nil`' do
78
+ it 'parses the body as text' do
79
+ TestWorker.get_shoryuken_options['body_parser'] = nil
80
+
81
+ body = 'test'
82
+
83
+ allow(sqs_msg).to receive(:body).and_return(body)
84
+
85
+ expect(described_class.parse(TestWorker, sqs_msg)).to eq(body)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -1,13 +1,12 @@
1
1
  require 'spec_helper'
2
+ require 'active_job'
2
3
 
3
- # rubocop:disable Metrics/BlockLength
4
4
  RSpec.describe Shoryuken::EnvironmentLoader do
5
5
  subject { described_class.new({}) }
6
6
 
7
7
  describe '#parse_queues' do
8
8
  before do
9
- # TODO proper test other methods
10
- allow(subject).to receive(:load_rails).with(anything)
9
+ allow(subject).to receive(:load_rails)
11
10
  allow(subject).to receive(:prefix_active_job_queue_names)
12
11
  allow(subject).to receive(:require_workers)
13
12
  allow(subject).to receive(:validate_queues)
@@ -22,4 +21,34 @@ RSpec.describe Shoryuken::EnvironmentLoader do
22
21
  expect(Shoryuken.groups['default'][:queues]).to eq(%w(queue1 queue2 queue2))
23
22
  end
24
23
  end
24
+
25
+ describe '#prefix_active_job_queue_names' do
26
+ before do
27
+ allow(subject).to receive(:load_rails)
28
+ allow(subject).to receive(:require_workers)
29
+ allow(subject).to receive(:validate_queues)
30
+ allow(subject).to receive(:validate_workers)
31
+ allow(subject).to receive(:patch_deprecated_workers)
32
+
33
+ ActiveJob::Base.queue_name_prefix = 'test'
34
+ ActiveJob::Base.queue_name_delimiter = '_'
35
+
36
+ allow(Shoryuken).to receive(:active_job?).and_return(true)
37
+ end
38
+
39
+ specify do
40
+ Shoryuken.active_job_queue_name_prefixing = true
41
+
42
+ Shoryuken.options[:queues] = ['queue1', ['queue2', 2]]
43
+
44
+ Shoryuken.options[:groups] = {
45
+ 'group1' => { queues: %w(group1_queue1 group1_queue2) }
46
+ }
47
+
48
+ subject.load
49
+
50
+ expect(Shoryuken.groups['default'][:queues]).to eq(%w(test_queue1 test_queue2 test_queue2))
51
+ expect(Shoryuken.groups['group1'][:queues]).to eq(%w(test_group1_queue1 test_group1_queue2))
52
+ end
53
+ end
25
54
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
  require 'shoryuken/manager'
3
3
  require 'shoryuken/fetcher'
4
4
 
5
+ # rubocop:disable Metrics/BlockLength
5
6
  RSpec.describe Shoryuken::Fetcher do
6
7
  let(:queue) { instance_double('Shoryuken::Queue') }
7
8
  let(:queue_name) { 'default' }
@@ -13,7 +14,7 @@ RSpec.describe Shoryuken::Fetcher do
13
14
  Shoryuken::Message,
14
15
  queue_url: queue_name,
15
16
  body: 'test',
16
- message_id: 'fc754df79cc24c4196ca5996a44b771e',
17
+ message_id: 'fc754df79cc24c4196ca5996a44b771e'
17
18
  )
18
19
  end
19
20
 
@@ -27,23 +28,74 @@ RSpec.describe Shoryuken::Fetcher do
27
28
 
28
29
  Shoryuken.sqs_client_receive_message_opts[group] = { wait_time_seconds: 10 }
29
30
 
30
- expect(queue).to receive(:receive_messages).
31
- with(wait_time_seconds: 10, max_number_of_messages: limit, message_attribute_names: ['All'], attribute_names: ['All']).
32
- and_return([])
31
+ expect(queue).to receive(:receive_messages).with(
32
+ wait_time_seconds: 10,
33
+ max_number_of_messages: limit,
34
+ message_attribute_names: ['All'],
35
+ attribute_names: ['All']
36
+ ).and_return([])
33
37
 
34
38
  subject.fetch(queue_config, limit)
35
39
  end
36
40
 
41
+ it 'logs debug only' do
42
+ # See https://github.com/phstc/shoryuken/issues/435
43
+ logger = double 'logger'
44
+
45
+ allow(subject).to receive(:logger).and_return(logger)
46
+
47
+ expect(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
48
+
49
+ expect(queue).to receive(:receive_messages).and_return([double('SQS Msg')])
50
+
51
+ expect(logger).to receive(:debug).exactly(3).times
52
+ expect(logger).to_not receive(:info)
53
+
54
+ subject.fetch(queue_config, limit)
55
+ end
56
+
57
+ context 'when receive options per queue' do
58
+ let(:limit) { 5 }
59
+
60
+ specify do
61
+ expect(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
62
+
63
+ Shoryuken.sqs_client_receive_message_opts[queue_name] = { max_number_of_messages: 1 }
64
+
65
+ expect(queue).to receive(:receive_messages).with(
66
+ max_number_of_messages: 1,
67
+ message_attribute_names: ['All'],
68
+ attribute_names: ['All']
69
+ ).and_return([])
70
+
71
+ subject.fetch(queue_config, limit)
72
+ end
73
+ end
74
+
75
+ context 'when max_number_of_messages opt is great than limit' do
76
+ it 'uses limit' do
77
+ expect(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
78
+
79
+ Shoryuken.sqs_client_receive_message_opts[queue_name] = { max_number_of_messages: 20 }
80
+
81
+ expect(queue).to receive(:receive_messages).with(
82
+ max_number_of_messages: limit,
83
+ message_attribute_names: ['All'],
84
+ attribute_names: ['All']
85
+ ).and_return([])
86
+
87
+ subject.fetch(queue_config, limit)
88
+ end
89
+ end
90
+
37
91
  context 'when limit is greater than FETCH_LIMIT' do
38
92
  let(:limit) { 20 }
39
93
 
40
94
  specify do
41
- Shoryuken.sqs_client_receive_message_opts[group] = {}
42
-
43
95
  allow(Shoryuken::Client).to receive(:queues).with(queue_name).and_return(queue)
44
- expect(queue).to receive(:receive_messages).
45
- with(max_number_of_messages: described_class::FETCH_LIMIT, attribute_names: ['All'], message_attribute_names: ['All']).
46
- and_return([])
96
+ expect(queue).to receive(:receive_messages).with(
97
+ max_number_of_messages: described_class::FETCH_LIMIT, attribute_names: ['All'], message_attribute_names: ['All']
98
+ ).and_return([])
47
99
 
48
100
  subject.fetch(queue_config, limit)
49
101
  end