shoryuken 3.1.9 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 17dbef4b25b83a6c3529947b5fabf45ff89e868d
4
- data.tar.gz: aab8f0e8521732396865abbcb5394ad6e21ed317
3
+ metadata.gz: f74e5fbb599fe5bbe41bed5fd542c0c1fdcc2d35
4
+ data.tar.gz: 6099d42682b5e2a1797d7cdbdc7bf27b344b692c
5
5
  SHA512:
6
- metadata.gz: 54c17164557879edcc48b053dad99009989a7be515a0d8e38bd7eb47e95d5f7629389a54d92dfa137494966168d0e34a2a95f1c6e3ee29ff6950bdd8f970bf96
7
- data.tar.gz: eca828404986cc2aa5dcf22f01ccb3552591f676c299efac6139ce46dfc9f078d8fcdc1d4dbc5c0269361929f1b0673084733c09fbd23ded26c11142605cb7d4
6
+ metadata.gz: f076e8f427f39ca92be4f27be819350d90270ce9e695d1173a6944b82d16680d11afd78a0075dd549e0a2c6031e6d5708c306cb245c4d7dfcc69d8a5e7151bc7
7
+ data.tar.gz: 2fd34c84e6260df3c35427ffb27d3d067fc1ea11469609d61e8eedd465e4516d75e052079b5195ede7c7917ad3c97bb39c5440bc5436ae3ab2581f3fb72bad31
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.0
2
+ TargetRubyVersion: 2.1
3
3
 
4
4
  Style/SignalException:
5
5
  Enabled: false
data/.travis.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  language: ruby
2
+
2
3
  rvm:
3
4
  - 2.0.0
4
5
  - 2.1.10
@@ -11,9 +12,18 @@ notifications:
11
12
  on_success: change
12
13
  on_failure: always
13
14
 
14
- script: SPEC_ALL=true bundle exec rspec spec
15
+ gemfile:
16
+ - Gemfile
17
+ - Gemfile.aws-sdk-core-v2
18
+
19
+ env:
20
+ - SPEC_ALL=true
21
+
22
+ script: bundle exec rspec spec
23
+
15
24
  before_install:
16
- - gem update bundler
25
+ - gem update bundler
26
+
17
27
  after_success:
18
28
  - bundle exec codeclimate-test-reporter
19
29
 
data/CHANGELOG.md CHANGED
@@ -1,11 +1,37 @@
1
- ## [v3.1.9] - 2017-08-24
1
+ ## [v3.2.0] - 2018-01-03
2
2
 
3
- - Add support for dispatch fire event
4
- - [#426](https://github.com/phstc/shoryuken/pull/426)
3
+ - Preserve parent worker class options
4
+ - [#451](https://github.com/phstc/shoryuken/pull/451)
5
+
6
+ - Add -t (shutdown timeout) option to CL
7
+ - [#449](https://github.com/phstc/shoryuken/pull/449)
8
+
9
+ - Support inline (Active Job like) for standard workers
10
+ - [#448](https://github.com/phstc/shoryuken/pull/448)
11
+
12
+ ## [v3.1.12] - 2017-09-25
13
+
14
+ - Reduce fetch log verbosity
15
+ - [#436](https://github.com/phstc/shoryuken/pull/436)
16
+
17
+ ## [v3.1.11] - 2017-09-02
18
+
19
+ - Auto retry (up to 3 times) fetch errors
20
+ - [#429](https://github.com/phstc/shoryuken/pull/429)
21
+
22
+ ## [v3.1.10] - 2017-09-02
23
+
24
+ - Make Shoryuken compatible with AWS SDK 3 and 2
25
+ - [#433](https://github.com/phstc/shoryuken/pull/433)
26
+
27
+ ## [v3.1.9] - 2017-08-24
5
28
 
6
29
  - Add support for adding a middleware to the front of chain
7
30
  - [#427](https://github.com/phstc/shoryuken/pull/427)
8
31
 
32
+ - Add support for dispatch fire event
33
+ - [#426](https://github.com/phstc/shoryuken/pull/426)
34
+
9
35
  ## [v3.1.8] - 2017-08-17
10
36
 
11
37
  - Make Polling strategy backward compatibility
data/Gemfile CHANGED
@@ -4,9 +4,11 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :test do
7
- gem 'activejob'
7
+ gem 'activejob', '~> 4'
8
+ gem 'aws-sdk-core', '~> 3'
9
+ gem 'aws-sdk-sqs'
8
10
  gem 'codeclimate-test-reporter', require: nil
9
- gem 'simplecov'
10
- gem 'multi_xml'
11
11
  gem 'httparty'
12
+ gem 'multi_xml'
13
+ gem 'simplecov'
12
14
  end
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in shoryuken.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'activejob', '~> 4'
8
+ gem 'aws-sdk-core', '~> 2'
9
+ gem 'codeclimate-test-reporter', require: nil
10
+ gem 'httparty'
11
+ gem 'multi_xml'
12
+ gem 'simplecov'
13
+ end
data/bin/shoryuken CHANGED
@@ -21,6 +21,7 @@ module Shoryuken
21
21
  method_option :daemon, aliases: '-d', type: :boolean, desc: 'Daemonize process'
22
22
  method_option :queues, aliases: '-q', type: :array, desc: 'Queues to process with optional weights'
23
23
  method_option :require, aliases: '-r', type: :string, desc: 'Dir or path of the workers'
24
+ method_option :timeout, aliases: '-t', type: :numeric, desc: 'Hard shutdown timeout'
24
25
  method_option :config, aliases: '-C', type: :string, desc: 'Path to config file'
25
26
  method_option :config_file, type: :string, desc: 'Path to config file (backwards compatibility)'
26
27
  method_option :rails, aliases: '-R', type: :boolean, desc: 'Load Rails'
@@ -0,0 +1,27 @@
1
+ module Shoryuken
2
+ class BodyParser
3
+ class << self
4
+ def parse(worker_class, sqs_msg)
5
+ body_parser = worker_class.get_shoryuken_options['body_parser']
6
+
7
+ case body_parser
8
+ when :json
9
+ JSON.parse(sqs_msg.body)
10
+ when Proc
11
+ body_parser.call(sqs_msg)
12
+ when :text, nil
13
+ sqs_msg.body
14
+ else
15
+ if body_parser.respond_to?(:parse)
16
+ # JSON.parse
17
+ body_parser.parse(sqs_msg.body)
18
+ elsif body_parser.respond_to?(:load)
19
+ # see https://github.com/phstc/shoryuken/pull/91
20
+ # JSON.load
21
+ body_parser.load(sqs_msg.body)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -9,20 +9,41 @@ module Shoryuken
9
9
  end
10
10
 
11
11
  def fetch(queue, limit)
12
- started_at = Time.now
12
+ fetch_with_auto_retry(3) do
13
+ started_at = Time.now
13
14
 
14
- logger.debug { "Looking for new messages in #{queue}" }
15
+ logger.debug { "Looking for new messages in #{queue}" }
15
16
 
16
- sqs_msgs = Array(receive_messages(queue, [FETCH_LIMIT, limit].min))
17
+ sqs_msgs = Array(receive_messages(queue, [FETCH_LIMIT, limit].min))
17
18
 
18
- logger.info { "Found #{sqs_msgs.size} messages for #{queue.name}" } unless sqs_msgs.empty?
19
- logger.debug { "Fetcher for #{queue} completed in #{elapsed(started_at)} ms" }
19
+ logger.debug { "Found #{sqs_msgs.size} messages for #{queue.name}" } unless sqs_msgs.empty?
20
+ logger.debug { "Fetcher for #{queue} completed in #{elapsed(started_at)} ms" }
20
21
 
21
- sqs_msgs
22
+ sqs_msgs
23
+ end
22
24
  end
23
25
 
24
26
  private
25
27
 
28
+ def fetch_with_auto_retry(max_attempts, &block)
29
+ attempts = 0
30
+
31
+ begin
32
+ yield
33
+ rescue => ex
34
+ # Tries to auto retry connectivity errors
35
+ raise if attempts >= max_attempts
36
+
37
+ attempts += 1
38
+
39
+ logger.debug { "Retrying fetch attempt #{attempts} for #{ex.message}" }
40
+
41
+ sleep((1..5).to_a.sample)
42
+
43
+ retry
44
+ end
45
+ end
46
+
26
47
  def receive_messages(queue, limit)
27
48
  options = receive_options(queue)
28
49
 
@@ -12,6 +12,7 @@ module Shoryuken
12
12
  @max_processors = concurrency
13
13
  @busy_processors = Concurrent::AtomicFixnum.new(0)
14
14
  @executor = executor
15
+ @running = Concurrent::AtomicBoolean.new(true)
15
16
  end
16
17
 
17
18
  def start
@@ -21,7 +22,7 @@ module Shoryuken
21
22
  private
22
23
 
23
24
  def running?
24
- @executor.running?
25
+ @running.true? && @executor.running?
25
26
  end
26
27
 
27
28
  def dispatch_loop
@@ -104,6 +105,8 @@ module Shoryuken
104
105
  logger.error { ex.backtrace.join("\n") } unless ex.backtrace.nil?
105
106
 
106
107
  Process.kill('USR1', Process.pid)
108
+
109
+ @running.make_false
107
110
  end
108
111
  end
109
112
  end
@@ -31,16 +31,13 @@ module Shoryuken
31
31
  Concurrent::TimerTask.new(execution_interval: queue_visibility_timeout - EXTEND_UPFRONT_SECONDS) do
32
32
  begin
33
33
  logger.debug do
34
- "Extending message #{worker_name(worker.class, sqs_msg, body)}/#{queue}/#{sqs_msg.message_id} " \
35
- "visibility timeout by #{queue_visibility_timeout}s."
34
+ "Extending message #{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s"
36
35
  end
37
36
 
38
37
  sqs_msg.change_visibility(visibility_timeout: queue_visibility_timeout)
39
38
  rescue => ex
40
39
  logger.error do
41
- 'Could not auto extend the message ' \
42
- "#{worker_name(worker.class, sqs_msg, body)}/#{queue}/#{sqs_msg.message_id} " \
43
- "visibility timeout. Error: #{ex.message}"
40
+ "Could not auto extend the message #{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{ex.message}"
44
41
  end
45
42
  end
46
43
  end
@@ -5,25 +5,23 @@ module Shoryuken
5
5
  include Util
6
6
 
7
7
  def call(worker, queue, sqs_msg, body)
8
- Shoryuken::Logging.with_context("#{worker_name(worker.class, sqs_msg, body)}/#{queue}/#{sqs_msg.message_id}") do
9
- begin
10
- started_at = Time.now
8
+ begin
9
+ started_at = Time.now
11
10
 
12
- logger.info { "started at #{started_at}" }
11
+ logger.info { "started at #{started_at}" }
13
12
 
14
- yield
13
+ yield
15
14
 
16
- total_time = elapsed(started_at)
15
+ total_time = elapsed(started_at)
17
16
 
18
- if (total_time / 1000.0) > (timeout = Shoryuken::Client.queues(queue).visibility_timeout)
19
- logger.warn { "exceeded the queue visibility timeout by #{total_time - (timeout * 1000)} ms" }
20
- end
21
-
22
- logger.info { "completed in: #{total_time} ms" }
23
- rescue
24
- logger.info { "failed in: #{elapsed(started_at)} ms" }
25
- raise
17
+ if (total_time / 1000.0) > (timeout = Shoryuken::Client.queues(queue).visibility_timeout)
18
+ logger.warn { "exceeded the queue visibility timeout by #{total_time - (timeout * 1000)} ms" }
26
19
  end
20
+
21
+ logger.info { "completed in: #{total_time} ms" }
22
+ rescue
23
+ logger.info { "failed in: #{elapsed(started_at)} ms" }
24
+ raise
27
25
  end
28
26
  end
29
27
  end
@@ -21,6 +21,7 @@ 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
26
27
  def active_job?
@@ -56,6 +57,14 @@ module Shoryuken
56
57
  @@worker_registry = worker_registry
57
58
  end
58
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
+
59
68
  def polling_strategy(group)
60
69
  strategy = (group == 'default' ? options : options[:groups].to_h[group]).to_h[:polling_strategy]
61
70
 
@@ -16,8 +16,10 @@ module Shoryuken
16
16
  def process
17
17
  return logger.error { "No worker found for #{queue}" } unless worker
18
18
 
19
- worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
20
- 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
21
23
  end
22
24
  rescue Exception => ex
23
25
  logger.error { "Processor failed: #{ex.message}" }
@@ -41,25 +43,7 @@ module Shoryuken
41
43
  end
42
44
 
43
45
  def parse_body(sqs_msg)
44
- body_parser = worker_class.get_shoryuken_options['body_parser']
45
-
46
- case body_parser
47
- when :json
48
- JSON.parse(sqs_msg.body)
49
- when Proc
50
- body_parser.call(sqs_msg)
51
- when :text, nil
52
- sqs_msg.body
53
- else
54
- if body_parser.respond_to?(:parse)
55
- # JSON.parse
56
- body_parser.parse(sqs_msg.body)
57
- elsif body_parser.respond_to?(:load)
58
- # see https://github.com/phstc/shoryuken/pull/91
59
- # JSON.load
60
- body_parser.load(sqs_msg.body)
61
- end
62
- end
46
+ BodyParser.parse(worker_class, sqs_msg)
63
47
  end
64
48
  end
65
49
  end
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '3.1.9'.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,6 +1,11 @@
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'
6
11
  require 'forwardable'
@@ -14,6 +19,8 @@ require 'shoryuken/queue'
14
19
  require 'shoryuken/message'
15
20
  require 'shoryuken/client'
16
21
  require 'shoryuken/worker'
22
+ require 'shoryuken/worker/default_executor'
23
+ require 'shoryuken/worker/inline_executor'
17
24
  require 'shoryuken/worker_registry'
18
25
  require 'shoryuken/default_worker_registry'
19
26
  require 'shoryuken/middleware/chain'
@@ -27,9 +34,11 @@ require 'shoryuken/polling/strict_priority'
27
34
  require 'shoryuken/manager'
28
35
  require 'shoryuken/launcher'
29
36
  require 'shoryuken/processor'
37
+ require 'shoryuken/body_parser'
30
38
  require 'shoryuken/fetcher'
31
39
  require 'shoryuken/options'
32
40
 
41
+
33
42
  module Shoryuken
34
43
  extend SingleForwardable
35
44
 
@@ -42,6 +51,8 @@ module Shoryuken
42
51
  :ungrouped_queues,
43
52
  :worker_registry,
44
53
  :worker_registry=,
54
+ :worker_executor,
55
+ :worker_executor=,
45
56
  :polling_strategy,
46
57
  :start_callback,
47
58
  :start_callback=,
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
@@ -38,6 +38,22 @@ RSpec.describe Shoryuken::Fetcher do
38
38
  subject.fetch(queue_config, limit)
39
39
  end
40
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
+
41
57
  context 'when receive options per queue' do
42
58
  let(:limit) { 5 }
43
59
 
@@ -1,6 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'shoryuken/processor'
3
- require 'shoryuken/manager'
4
2
 
5
3
  RSpec.describe Shoryuken::Processor do
6
4
  let(:manager) { double Shoryuken::Manager }
@@ -8,12 +6,14 @@ RSpec.describe Shoryuken::Processor do
8
6
  let(:queue) { 'default' }
9
7
 
10
8
  let(:sqs_msg) do
11
- double Shoryuken::Message,
9
+ double(
10
+ Shoryuken::Message,
12
11
  queue_url: queue,
13
12
  body: 'test',
14
13
  message_attributes: {},
15
14
  message_id: SecureRandom.uuid,
16
15
  receipt_handle: SecureRandom.uuid
16
+ )
17
17
  end
18
18
 
19
19
  before do
@@ -25,101 +25,15 @@ RSpec.describe Shoryuken::Processor do
25
25
  subject { described_class.new(queue, sqs_msg) }
26
26
 
27
27
  describe '#process' do
28
- it 'parses the body into JSON' do
29
- TestWorker.get_shoryuken_options['body_parser'] = :json
28
+ it 'sets log context' do
29
+ expect(Shoryuken::Logging).to receive(:with_context).with("TestWorker/#{queue}/#{sqs_msg.message_id}")
30
30
 
31
- body = { 'test' => 'hi' }
32
-
33
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
34
-
35
- allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
31
+ allow_any_instance_of(TestWorker).to receive(:perform)
32
+ allow(sqs_msg).to receive(:body)
36
33
 
37
34
  subject.process
38
35
  end
39
36
 
40
- it 'parses the body calling the proc' do
41
- TestWorker.get_shoryuken_options['body_parser'] = proc { |sqs_msg| "*#{sqs_msg.body}*" }
42
-
43
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, '*test*')
44
-
45
- allow(sqs_msg).to receive(:body).and_return('test')
46
-
47
- subject.process
48
- end
49
-
50
- it 'parses the body as text' do
51
- TestWorker.get_shoryuken_options['body_parser'] = :text
52
-
53
- body = 'test'
54
-
55
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
56
-
57
- allow(sqs_msg).to receive(:body).and_return(body)
58
-
59
- subject.process
60
- end
61
-
62
- it 'parses calling `.load`' do
63
- TestWorker.get_shoryuken_options['body_parser'] = Class.new do
64
- def self.load(*args)
65
- JSON.load(*args)
66
- end
67
- end
68
-
69
- body = { 'test' => 'hi' }
70
-
71
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
72
-
73
- allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
74
-
75
- subject.process
76
- end
77
-
78
- it 'parses calling `.parse`' do
79
- TestWorker.get_shoryuken_options['body_parser'] = Class.new do
80
- def self.parse(*args)
81
- JSON.parse(*args)
82
- end
83
- end
84
-
85
- body = { 'test' => 'hi' }
86
-
87
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
88
-
89
- allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
90
-
91
- subject.process
92
- end
93
-
94
- context 'when parse errors' do
95
- before do
96
- TestWorker.get_shoryuken_options['body_parser'] = :json
97
-
98
- allow(sqs_msg).to receive(:body).and_return('invalid JSON')
99
- end
100
-
101
- specify do
102
- expect(subject.logger).to receive(:error).twice
103
-
104
- expect { subject.process }.
105
- to raise_error(JSON::ParserError, /unexpected token at 'invalid JSON'/)
106
- end
107
- end
108
-
109
- context 'when `object_type: nil`' do
110
- it 'parses the body as text' do
111
- TestWorker.get_shoryuken_options['body_parser'] = nil
112
-
113
- body = 'test'
114
-
115
- expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
116
-
117
- allow(sqs_msg).to receive(:body).and_return(body)
118
-
119
- subject.process
120
- end
121
- end
122
-
123
37
  context 'when custom middleware' do
124
38
  let(:queue) { 'worker_called_middleware' }
125
39
 
@@ -220,15 +134,19 @@ RSpec.describe Shoryuken::Processor do
220
134
 
221
135
  context 'when shoryuken_class header' do
222
136
  let(:sqs_msg) do
223
- double Shoryuken::Message,
137
+ double(
138
+ Shoryuken::Message,
224
139
  queue_url: queue,
225
140
  body: 'test',
226
141
  message_attributes: {
227
142
  'shoryuken_class' => {
228
143
  string_value: TestWorker.to_s,
229
- data_type: 'String' }},
230
- message_id: SecureRandom.uuid,
231
- receipt_handle: SecureRandom.uuid
144
+ data_type: 'String'
145
+ }
146
+ },
147
+ message_id: SecureRandom.uuid,
148
+ receipt_handle: SecureRandom.uuid
149
+ )
232
150
  end
233
151
 
234
152
  it 'performs without delete' do
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Shoryuken::Worker::DefaultExecutor do
4
+ let(:sqs_queue) { double 'SQS Queue' }
5
+ let(:queue) { 'default' }
6
+
7
+ before do
8
+ allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
9
+ end
10
+
11
+ describe '.perform_in' do
12
+ it 'delays a message' do
13
+ expect(sqs_queue).to receive(:send_message).with(
14
+ message_attributes: {
15
+ 'shoryuken_class' => {
16
+ string_value: TestWorker.to_s,
17
+ data_type: 'String'
18
+ }
19
+ },
20
+ message_body: 'message',
21
+ delay_seconds: 60)
22
+
23
+ TestWorker.perform_in(60, 'message')
24
+ end
25
+
26
+ it 'raises an exception' do
27
+ expect {
28
+ TestWorker.perform_in(901, 'message')
29
+ }.to raise_error 'The maximum allowed delay is 15 minutes'
30
+ end
31
+ end
32
+
33
+ describe '.perform_at' do
34
+ it 'delays a message' do
35
+ expect(sqs_queue).to receive(:send_message).with(
36
+ message_attributes: {
37
+ 'shoryuken_class' => {
38
+ string_value: TestWorker.to_s,
39
+ data_type: 'String'
40
+ }
41
+ },
42
+ message_body: 'message',
43
+ delay_seconds: 60)
44
+
45
+ TestWorker.perform_in(Time.now + 60, 'message')
46
+ end
47
+
48
+ it 'raises an exception' do
49
+ expect {
50
+ TestWorker.perform_in(Time.now + 901, 'message')
51
+ }.to raise_error 'The maximum allowed delay is 15 minutes'
52
+ end
53
+ end
54
+
55
+ describe '.perform_async' do
56
+ it 'enqueues a message' do
57
+ expect(sqs_queue).to receive(:send_message).with(
58
+ message_attributes: {
59
+ 'shoryuken_class' => {
60
+ string_value: TestWorker.to_s,
61
+ data_type: 'String'
62
+ }
63
+ },
64
+ message_body: 'message')
65
+
66
+ TestWorker.perform_async('message')
67
+ end
68
+
69
+ it 'enqueues a message with options' do
70
+ expect(sqs_queue).to receive(:send_message).with(
71
+ delay_seconds: 60,
72
+ message_attributes: {
73
+ 'shoryuken_class' => {
74
+ string_value: TestWorker.to_s,
75
+ data_type: 'String'
76
+ }
77
+ },
78
+ message_body: 'delayed message')
79
+
80
+ TestWorker.perform_async('delayed message', delay_seconds: 60)
81
+ end
82
+
83
+ it 'accepts an `queue` option' do
84
+ new_queue = 'some_different_queue'
85
+
86
+ expect(Shoryuken::Client).to receive(:queues).with(new_queue).and_return(sqs_queue)
87
+
88
+ expect(sqs_queue).to receive(:send_message).with(
89
+ message_attributes: {
90
+ 'shoryuken_class' => {
91
+ string_value: TestWorker.to_s,
92
+ data_type: 'String'
93
+ }
94
+ },
95
+ message_body: 'delayed message')
96
+
97
+ TestWorker.perform_async('delayed message', queue: new_queue)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Shoryuken::Worker::InlineExecutor do
4
+ before do
5
+ Shoryuken.worker_executor = described_class
6
+ end
7
+
8
+ describe '.perform_async' do
9
+ specify do
10
+ expect_any_instance_of(TestWorker).to receive(:perform)
11
+
12
+ TestWorker.perform_async('test')
13
+ end
14
+ end
15
+
16
+ describe '.perform_in' do
17
+ specify do
18
+ expect_any_instance_of(TestWorker).to receive(:perform)
19
+
20
+ TestWorker.perform_in(60, 'test')
21
+ end
22
+ end
23
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe 'Shoryuken::Worker' do
3
+ RSpec.describe Shoryuken::Worker do
4
4
  let(:sqs_queue) { double 'SQS Queue' }
5
5
  let(:queue) { 'default' }
6
6
 
@@ -8,96 +8,6 @@ RSpec.describe 'Shoryuken::Worker' do
8
8
  allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
9
9
  end
10
10
 
11
- describe '.perform_in' do
12
- it 'delays a message' do
13
- expect(sqs_queue).to receive(:send_message).with(
14
- message_attributes: {
15
- 'shoryuken_class' => {
16
- string_value: TestWorker.to_s,
17
- data_type: 'String'
18
- }
19
- },
20
- message_body: 'message',
21
- delay_seconds: 60)
22
-
23
- TestWorker.perform_in(60, 'message')
24
- end
25
-
26
- it 'raises an exception' do
27
- expect {
28
- TestWorker.perform_in(901, 'message')
29
- }.to raise_error 'The maximum allowed delay is 15 minutes'
30
- end
31
- end
32
-
33
- describe '.perform_at' do
34
- it 'delays a message' do
35
- expect(sqs_queue).to receive(:send_message).with(
36
- message_attributes: {
37
- 'shoryuken_class' => {
38
- string_value: TestWorker.to_s,
39
- data_type: 'String'
40
- }
41
- },
42
- message_body: 'message',
43
- delay_seconds: 60)
44
-
45
- TestWorker.perform_in(Time.now + 60, 'message')
46
- end
47
-
48
- it 'raises an exception' do
49
- expect {
50
- TestWorker.perform_in(Time.now + 901, 'message')
51
- }.to raise_error 'The maximum allowed delay is 15 minutes'
52
- end
53
- end
54
-
55
- describe '.perform_async' do
56
- it 'enqueues a message' do
57
- expect(sqs_queue).to receive(:send_message).with(
58
- message_attributes: {
59
- 'shoryuken_class' => {
60
- string_value: TestWorker.to_s,
61
- data_type: 'String'
62
- }
63
- },
64
- message_body: 'message')
65
-
66
- TestWorker.perform_async('message')
67
- end
68
-
69
- it 'enqueues a message with options' do
70
- expect(sqs_queue).to receive(:send_message).with(
71
- delay_seconds: 60,
72
- message_attributes: {
73
- 'shoryuken_class' => {
74
- string_value: TestWorker.to_s,
75
- data_type: 'String'
76
- }
77
- },
78
- message_body: 'delayed message')
79
-
80
- TestWorker.perform_async('delayed message', delay_seconds: 60)
81
- end
82
-
83
- it 'accepts an `queue` option' do
84
- new_queue = 'some_different_queue'
85
-
86
- expect(Shoryuken::Client).to receive(:queues).with(new_queue).and_return(sqs_queue)
87
-
88
- expect(sqs_queue).to receive(:send_message).with(
89
- message_attributes: {
90
- 'shoryuken_class' => {
91
- string_value: TestWorker.to_s,
92
- data_type: 'String'
93
- }
94
- },
95
- message_body: 'delayed message')
96
-
97
- TestWorker.perform_async('delayed message', queue: new_queue)
98
- end
99
- end
100
-
101
11
  describe '.shoryuken_options' do
102
12
  it 'registers a worker' do
103
13
  expect(Shoryuken.worker_registry.workers('default')).to eq([TestWorker])
@@ -116,6 +26,20 @@ RSpec.describe 'Shoryuken::Worker' do
116
26
  expect(NewTestWorker.get_shoryuken_options['queue']).to eq 'production_default'
117
27
  end
118
28
 
29
+ it 'does not change the original hash' do
30
+ class TestWorker
31
+ include Shoryuken::Worker
32
+
33
+ OPT = { queue: :default }
34
+
35
+ shoryuken_options OPT
36
+ end
37
+
38
+ expect(TestWorker::OPT['queue']).to eq(nil)
39
+ expect(TestWorker.get_shoryuken_options['queue']).to eq('default')
40
+ expect(TestWorker::OPT[:queue]).to eq(:default)
41
+ end
42
+
119
43
  it 'accepts an array as a queue' do
120
44
  class WorkerMultipleQueues
121
45
  include Shoryuken::Worker
@@ -164,6 +88,23 @@ RSpec.describe 'Shoryuken::Worker' do
164
88
  expect(Shoryuken.worker_registry.workers('symbol_queue2')).to eq([WorkerMultipleSymbolQueues])
165
89
  expect(Shoryuken.worker_registry.workers('symbol_queue3')).to eq([WorkerMultipleSymbolQueues])
166
90
  end
91
+
92
+ it 'preserves parent class options' do
93
+ class ParentWorker
94
+ include Shoryuken::Worker
95
+
96
+ shoryuken_options queue: "myqueue", auto_delete: false
97
+ end
98
+
99
+ class ChildWorker < ParentWorker
100
+ shoryuken_options auto_delete: true
101
+ end
102
+
103
+ expect(ParentWorker.get_shoryuken_options['queue']).to eq("myqueue")
104
+ expect(ChildWorker.get_shoryuken_options['queue']).to eq("myqueue")
105
+ expect(ParentWorker.get_shoryuken_options['auto_delete']).to eq(false)
106
+ expect(ChildWorker.get_shoryuken_options['auto_delete']).to eq(true)
107
+ end
167
108
  end
168
109
 
169
110
  describe '.server_middleware' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoryuken
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.9
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pablo Cantero
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-24 00:00:00.000000000 Z
11
+ date: 2018-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -84,14 +84,14 @@ dependencies:
84
84
  name: aws-sdk-core
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '2'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '2'
97
97
  - !ruby/object:Gem::Dependency
@@ -137,6 +137,7 @@ files:
137
137
  - ".travis.yml"
138
138
  - CHANGELOG.md
139
139
  - Gemfile
140
+ - Gemfile.aws-sdk-core-v2
140
141
  - LICENSE
141
142
  - README.md
142
143
  - Rakefile
@@ -146,6 +147,7 @@ files:
146
147
  - examples/bootstrap_queues.rb
147
148
  - examples/default_worker.rb
148
149
  - lib/shoryuken.rb
150
+ - lib/shoryuken/body_parser.rb
149
151
  - lib/shoryuken/client.rb
150
152
  - lib/shoryuken/core_ext.rb
151
153
  - lib/shoryuken/default_worker_registry.rb
@@ -172,11 +174,14 @@ files:
172
174
  - lib/shoryuken/util.rb
173
175
  - lib/shoryuken/version.rb
174
176
  - lib/shoryuken/worker.rb
177
+ - lib/shoryuken/worker/default_executor.rb
178
+ - lib/shoryuken/worker/inline_executor.rb
175
179
  - lib/shoryuken/worker_registry.rb
176
180
  - shoryuken.gemspec
177
181
  - shoryuken.jpg
178
182
  - spec/integration/launcher_spec.rb
179
183
  - spec/shoryuken.yml
184
+ - spec/shoryuken/body_parser_spec.rb
180
185
  - spec/shoryuken/client_spec.rb
181
186
  - spec/shoryuken/core_ext_spec.rb
182
187
  - spec/shoryuken/default_worker_registry_spec.rb
@@ -195,6 +200,8 @@ files:
195
200
  - spec/shoryuken/queue_spec.rb
196
201
  - spec/shoryuken/runner_spec.rb
197
202
  - spec/shoryuken/util_spec.rb
203
+ - spec/shoryuken/worker/default_executor_spec.rb
204
+ - spec/shoryuken/worker/inline_executor_spec.rb
198
205
  - spec/shoryuken/worker_spec.rb
199
206
  - spec/shoryuken_spec.rb
200
207
  - spec/spec_helper.rb
@@ -220,13 +227,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
220
227
  version: '0'
221
228
  requirements: []
222
229
  rubyforge_project:
223
- rubygems_version: 2.6.11
230
+ rubygems_version: 2.6.13
224
231
  signing_key:
225
232
  specification_version: 4
226
233
  summary: Shoryuken is a super efficient AWS SQS thread based message processor
227
234
  test_files:
228
235
  - spec/integration/launcher_spec.rb
229
236
  - spec/shoryuken.yml
237
+ - spec/shoryuken/body_parser_spec.rb
230
238
  - spec/shoryuken/client_spec.rb
231
239
  - spec/shoryuken/core_ext_spec.rb
232
240
  - spec/shoryuken/default_worker_registry_spec.rb
@@ -245,6 +253,8 @@ test_files:
245
253
  - spec/shoryuken/queue_spec.rb
246
254
  - spec/shoryuken/runner_spec.rb
247
255
  - spec/shoryuken/util_spec.rb
256
+ - spec/shoryuken/worker/default_executor_spec.rb
257
+ - spec/shoryuken/worker/inline_executor_spec.rb
248
258
  - spec/shoryuken/worker_spec.rb
249
259
  - spec/shoryuken_spec.rb
250
260
  - spec/spec_helper.rb