shoryuken 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9072ce45c134cd9563ee7dddac27bc7d19733cde
4
- data.tar.gz: 2b48cb3b0d594ef1111456d0f9101ce154056995
3
+ metadata.gz: 08068a4a26c1f914715b25a27e463fac07e31b57
4
+ data.tar.gz: ac4fc670aac9fb76dec2c8c369f021ace729f0cd
5
5
  SHA512:
6
- metadata.gz: 62bde57b564da861f90c2a68816a69743766f1120dd146817f884b2d4d2e5e5119dcef8858516b7d2581517a04177ec0edb151a97f254c6185ae9092c96efab2
7
- data.tar.gz: e011b7d261eaf6e4b87d7dc322d2f458da09fdcd5074eac90eedb8101c082b2987f303b557ba20d90c89b321a7d4df7d613852890b6888263ccbc8adb1bf77db
6
+ metadata.gz: fdf1a335919582abb26bd34583de36da01b117b2e952b5d4874d40f4ee28b5d86cdca65fe9d4d52a807347569d2c9cb50bb20e82f8f6801ebf49aa2638219856
7
+ data.tar.gz: c9d462d292894b91ffe066af240ee5e7c6b8232542f19ba9d849a05bb9d7a0b6b732d6f96dbe6096610bbd9e4857f4540eed53bd5fd60b19c888dc8b88692e6b
data/.gitignore CHANGED
@@ -24,3 +24,4 @@ shoryuken.yml
24
24
  *.pid
25
25
  *.log
26
26
  .env
27
+ rubocop.html
data/.hound.yml CHANGED
@@ -1,2 +1,6 @@
1
- StringLiterals:
2
- EnforcedStyle: single_quotes
1
+ ruby:
2
+ enabled: true
3
+ config_file: .rubocop.yml
4
+
5
+ java_script:
6
+ enabled: true
data/.rubocop.yml ADDED
@@ -0,0 +1,50 @@
1
+ LineLength:
2
+ Max: 120
3
+
4
+ Style/SignalException:
5
+ Enabled: false
6
+
7
+ Style/SpaceAroundEqualsInParameterDefault:
8
+ Enabled: false
9
+
10
+ Style/Documentation:
11
+ Enabled: false
12
+
13
+ Style/ClassAndModuleChildren:
14
+ Enabled: false
15
+
16
+ Metrics/PerceivedComplexity:
17
+ Enabled: false
18
+
19
+ Metrics/CyclomaticComplexity:
20
+ Enabled: false
21
+
22
+ Style/CommentAnnotation:
23
+ Enabled: false
24
+
25
+ Metrics/ClassLength:
26
+ Enabled: false
27
+
28
+ Metrics/ParameterLists:
29
+ Enabled: false
30
+
31
+ Metrics/MethodLength :
32
+ Enabled: false
33
+
34
+ Style/PerlBackrefs:
35
+ Enabled: false
36
+
37
+ Style/StringLiterals:
38
+ EnforcedStyle: single_quotes
39
+ SupportedStyles:
40
+ - single_quotes
41
+ - double_quotes
42
+
43
+ Style/StringLiteralsInInterpolation:
44
+ EnforcedStyle: single_quotes
45
+ SupportedStyles:
46
+ - single_quotes
47
+ - double_quotes
48
+
49
+ Lint/UnusedMethodArgument:
50
+ Enabled: false
@@ -22,7 +22,7 @@ if sqs.config['endpoint'] =~ /amazonaws.com/
22
22
  ).attributes['QueueArn']
23
23
 
24
24
  attributes = {}
25
- attributes['RedrivePolicy'] = %Q{{"maxReceiveCount":"7", "deadLetterTargetArn":"#{dead_letter_queue_arn}"}}
25
+ attributes['RedrivePolicy'] = %Q({"maxReceiveCount":"7", "deadLetterTargetArn":"#{dead_letter_queue_arn}"})
26
26
 
27
27
  sqs.set_queue_attributes queue_url: default_queue_url, attributes: attributes
28
28
  end
data/lib/shoryuken.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'yaml'
2
2
  require 'aws-sdk-core'
3
- require 'aws-sdk-resources'
4
3
  require 'time'
5
4
 
6
5
  require 'shoryuken/version'
@@ -8,12 +7,15 @@ require 'shoryuken/core_ext'
8
7
  require 'shoryuken/util'
9
8
  require 'shoryuken/logging'
10
9
  require 'shoryuken/environment_loader'
10
+ require 'shoryuken/queue'
11
+ require 'shoryuken/message'
11
12
  require 'shoryuken/client'
12
13
  require 'shoryuken/worker'
13
14
  require 'shoryuken/worker_registry'
14
15
  require 'shoryuken/default_worker_registry'
15
16
  require 'shoryuken/middleware/chain'
16
17
  require 'shoryuken/middleware/server/auto_delete'
18
+ require 'shoryuken/middleware/server/exponential_backoff_retry'
17
19
  require 'shoryuken/middleware/server/timing'
18
20
  require 'shoryuken/sns_arn'
19
21
  require 'shoryuken/topic'
@@ -76,6 +78,7 @@ module Shoryuken
76
78
  'delete' => false,
77
79
  'auto_delete' => false,
78
80
  'auto_visibility_timeout' => false,
81
+ 'retry_intervals' => nil,
79
82
  'batch' => false }
80
83
  end
81
84
 
@@ -104,6 +107,7 @@ module Shoryuken
104
107
  def default_server_middleware
105
108
  Middleware::Chain.new do |m|
106
109
  m.add Middleware::Server::Timing
110
+ m.add Middleware::Server::ExponentialBackoffRetry
107
111
  m.add Middleware::Server::AutoDelete
108
112
  if defined?(::ActiveRecord::Base)
109
113
  require 'shoryuken/middleware/server/active_record'
data/lib/shoryuken/cli.rb CHANGED
@@ -19,7 +19,7 @@ module Shoryuken
19
19
  def run(args)
20
20
  self_read, self_write = IO.pipe
21
21
 
22
- %w[INT TERM USR1 USR2 TTIN].each do |sig|
22
+ %w(INT TERM USR1 USR2 TTIN).each do |sig|
23
23
  begin
24
24
  trap sig do
25
25
  self_write.puts(sig)
@@ -48,7 +48,7 @@ module Shoryuken
48
48
  begin
49
49
  launcher.run
50
50
 
51
- while readable_io = IO.select([self_read])
51
+ while (readable_io = IO.select([self_read]))
52
52
  signal = readable_io.first[0].gets.strip
53
53
  handle_signal(signal)
54
54
  end
@@ -86,7 +86,7 @@ module Shoryuken
86
86
 
87
87
  files_to_reopen.each do |file|
88
88
  begin
89
- file.reopen file.path, "a+"
89
+ file.reopen file.path, 'a+'
90
90
  file.sync = true
91
91
  rescue ::Exception
92
92
  end
@@ -102,7 +102,7 @@ module Shoryuken
102
102
  end
103
103
 
104
104
  def write_pid
105
- if path = Shoryuken.options[:pidfile]
105
+ if (path = Shoryuken.options[:pidfile])
106
106
  File.open(path, 'w') do |f|
107
107
  f.puts Process.pid
108
108
  end
@@ -150,7 +150,7 @@ module Shoryuken
150
150
  opts[:verbose] = arg
151
151
  end
152
152
 
153
- o.on '-V', '--version', 'Print version and exit' do |arg|
153
+ o.on '-V', '--version', 'Print version and exit' do
154
154
  puts "Shoryuken #{Shoryuken::VERSION}"
155
155
  exit 0
156
156
  end
@@ -170,7 +170,7 @@ module Shoryuken
170
170
 
171
171
  case sig
172
172
  when 'USR1'
173
- logger.info "Received USR1, will soft shutdown down"
173
+ logger.info 'Received USR1, will soft shutdown down'
174
174
 
175
175
  launcher.stop
176
176
 
@@ -181,7 +181,7 @@ module Shoryuken
181
181
  if thread.backtrace
182
182
  logger.info thread.backtrace.join("\n")
183
183
  else
184
- logger.info "<no backtrace available>"
184
+ logger.info '<no backtrace available>'
185
185
  end
186
186
  end
187
187
 
@@ -5,7 +5,7 @@ module Shoryuken
5
5
 
6
6
  class << self
7
7
  def queues(name)
8
- @@queues[name.to_s] ||= sqs_resource.get_queue_by_name(queue_name: name)
8
+ @@queues[name.to_s] ||= Shoryuken::Queue.new(sqs, name)
9
9
  end
10
10
 
11
11
  def sns
@@ -20,10 +20,6 @@ module Shoryuken
20
20
  @sqs ||= Aws::SQS::Client.new(aws_client_options(:sqs_endpoint))
21
21
  end
22
22
 
23
- def sqs_resource
24
- @sqs_resource ||= Aws::SQS::Resource.new(client: sqs)
25
- end
26
-
27
23
  def topics(name)
28
24
  @@topics[name.to_s] ||= Topic.new(name, sns)
29
25
  end
@@ -33,7 +29,7 @@ module Shoryuken
33
29
 
34
30
  private
35
31
 
36
- def aws_client_options service_endpoint_key
32
+ def aws_client_options(service_endpoint_key)
37
33
  explicit_endpoint = Shoryuken.options[:aws][service_endpoint_key]
38
34
  options = {}
39
35
  options[:endpoint] = explicit_endpoint unless explicit_endpoint.to_s.empty?
@@ -28,7 +28,7 @@ module Shoryuken
28
28
  end
29
29
 
30
30
  def register_worker(queue, clazz)
31
- if worker_class = @workers[queue]
31
+ if (worker_class = @workers[queue])
32
32
  if worker_class.get_shoryuken_options['batch'] == true || clazz.get_shoryuken_options['batch'] == true
33
33
  raise ArgumentError, "Could not register #{clazz} for '#{queue}', "\
34
34
  "because #{worker_class} is already registered for this queue, "\
@@ -10,7 +10,7 @@ module Shoryuken
10
10
  load(config_file: (Rails.root + 'config' + 'shoryuken.yml'))
11
11
  end
12
12
 
13
- def initialize options
13
+ def initialize(options)
14
14
  @options = options
15
15
  end
16
16
 
@@ -64,8 +64,8 @@ module Shoryuken
64
64
 
65
65
  aws_options = aws_options.merge(credentials: credentials)
66
66
 
67
- if callback = Shoryuken.aws_initialization_callback
68
- Shoryuken.logger.info "Calling Shoryuken.on_aws_initialization block"
67
+ if (callback = Shoryuken.aws_initialization_callback)
68
+ Shoryuken.logger.info 'Calling Shoryuken.on_aws_initialization block'
69
69
  callback.call(aws_options)
70
70
  end
71
71
 
@@ -82,19 +82,19 @@ module Shoryuken
82
82
 
83
83
  require 'rails'
84
84
  if ::Rails::VERSION::MAJOR < 4
85
- require File.expand_path("config/environment.rb")
85
+ require File.expand_path('config/environment.rb')
86
86
  ::Rails.application.eager_load!
87
87
  else
88
88
  # Painful contortions, see 1791 for discussion
89
- require File.expand_path("config/application.rb")
90
- ::Rails::Application.initializer "shoryuken.eager_load" do
89
+ require File.expand_path('config/application.rb')
90
+ ::Rails::Application.initializer 'shoryuken.eager_load' do
91
91
  ::Rails.application.config.eager_load = true
92
92
  end
93
93
  require 'shoryuken/extensions/active_job_adapter' if defined?(::ActiveJob)
94
- require File.expand_path("config/environment.rb")
94
+ require File.expand_path('config/environment.rb')
95
95
  end
96
96
 
97
- Shoryuken.logger.info "Rails environment loaded"
97
+ Shoryuken.logger.info 'Rails environment loaded'
98
98
  end
99
99
 
100
100
  def merge_cli_defined_queues
@@ -37,7 +37,6 @@ module ActiveJob
37
37
 
38
38
  def message(job, options = {})
39
39
  body = job.serialize
40
- body = JSON.dump(body) if body.is_a?(Hash)
41
40
 
42
41
  { message_body: body,
43
42
  message_attributes: message_attributes }.merge(options)
@@ -16,6 +16,7 @@ module Shoryuken
16
16
  options = Shoryuken.options[:aws][:receive_message].to_h.dup
17
17
  options[:max_number_of_messages] = limit
18
18
  options[:message_attribute_names] = %w(All)
19
+ options[:attribute_names] = %w(All)
19
20
 
20
21
  Shoryuken::Client.queues(queue).receive_messages options
21
22
  end
@@ -58,6 +59,7 @@ module Shoryuken
58
59
  end
59
60
 
60
61
  end
62
+
61
63
  private
62
64
 
63
65
  def patch_sqs_msgs!(sqs_msgs)
@@ -17,12 +17,10 @@ module Shoryuken
17
17
  end
18
18
 
19
19
  def self.with_context(msg)
20
- begin
21
- Thread.current[:shoryuken_context] = msg
22
- yield
23
- ensure
24
- Thread.current[:shoryuken_context] = nil
25
- end
20
+ Thread.current[:shoryuken_context] = msg
21
+ yield
22
+ ensure
23
+ Thread.current[:shoryuken_context] = nil
26
24
  end
27
25
 
28
26
  def self.initialize_logger(log_target = STDOUT)
@@ -32,8 +32,8 @@ module Shoryuken
32
32
  watchdog('Manager#stop died') do
33
33
  @done = true
34
34
 
35
- if callback = Shoryuken.stop_callback
36
- logger.info "Calling Shoryuken.on_stop block"
35
+ if (callback = Shoryuken.stop_callback)
36
+ logger.info 'Calling Shoryuken.on_stop block'
37
37
  callback.call
38
38
  end
39
39
 
@@ -89,7 +89,7 @@ module Shoryuken
89
89
  end
90
90
 
91
91
  def assign(queue, sqs_msg)
92
- watchdog("Manager#assign died") do
92
+ watchdog('Manager#assign died') do
93
93
  logger.info "Assigning #{sqs_msg.message_id}"
94
94
 
95
95
  processor = @ready.pop
@@ -133,7 +133,7 @@ module Shoryuken
133
133
  return
134
134
  end
135
135
 
136
- if queue = next_queue
136
+ if (queue = next_queue)
137
137
  @fetcher.async.fetch(queue, @ready.size)
138
138
  else
139
139
  logger.debug { 'Pausing fetcher, because all queues are paused' }
@@ -220,7 +220,7 @@ module Shoryuken
220
220
  logger.info { "Pausing up to #{delay} seconds to allow workers to finish..." }
221
221
 
222
222
  after(delay) do
223
- watchdog("Manager#hard_shutdown_in died") do
223
+ watchdog('Manager#hard_shutdown_in died') do
224
224
  if @busy.size > 0
225
225
  logger.info { "Hard shutting down #{@busy.size} busy workers" }
226
226
 
@@ -0,0 +1,60 @@
1
+ module Shoryuken
2
+ class Message
3
+ attr_accessor :client, :queue_url, :data
4
+
5
+ def initialize(client, queue_url, data)
6
+ self.client = client
7
+ self.queue_url = queue_url
8
+ self.data = data
9
+ end
10
+
11
+ def delete
12
+ client.delete_message(
13
+ queue_url: queue_url,
14
+ receipt_handle: data.receipt_handle
15
+ )
16
+ end
17
+
18
+ def change_visibility(options)
19
+ client.change_message_visibility(
20
+ options.merge(queue_url: queue_url, receipt_handle: data.receipt_handle)
21
+ )
22
+ end
23
+
24
+ def visibility_timeout=(timeout)
25
+ client.change_message_visibility(
26
+ queue_url: queue_url,
27
+ receipt_handle: data.receipt_handle,
28
+ visibility_timeout: timeout
29
+ )
30
+ end
31
+
32
+ def message_id
33
+ data.message_id
34
+ end
35
+
36
+ def receipt_handle
37
+ data.receipt_handle
38
+ end
39
+
40
+ def md5_of_body
41
+ data.md5_of_body
42
+ end
43
+
44
+ def body
45
+ data.body
46
+ end
47
+
48
+ def attributes
49
+ data.attributes
50
+ end
51
+
52
+ def md5_of_message_attributes
53
+ data.md5_of_message_attributes
54
+ end
55
+
56
+ def message_attributes
57
+ data.message_attributes
58
+ end
59
+ end
60
+ end
@@ -72,7 +72,7 @@ module Shoryuken
72
72
  i = entries.index { |entry| entry.klass == newklass }
73
73
  new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
74
74
  i = entries.find_index { |entry| entry.klass == oldklass } || entries.count - 1
75
- entries.insert(i+1, new_entry)
75
+ entries.insert(i + 1, new_entry)
76
76
  end
77
77
 
78
78
  def exists?(klass)
@@ -0,0 +1,50 @@
1
+ module Shoryuken
2
+ module Middleware
3
+ module Server
4
+ class ExponentialBackoffRetry
5
+ include Util
6
+
7
+ def call(worker, queue, sqs_msg, body)
8
+ started_at = Time.now
9
+ yield
10
+ rescue
11
+ retry_intervals = Array(worker.class.get_shoryuken_options['retry_intervals'])
12
+
13
+ if retry_intervals.empty? || !handle_failure(sqs_msg, started_at, retry_intervals)
14
+ # Re-raise the exception if the job is not going to be exponential backoff retried.
15
+ # This allows custom middleware (like exception notifiers) to be aware of the unhandled failure.
16
+ raise
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def handle_failure(sqs_msg, started_at, retry_intervals)
23
+ attempts = sqs_msg.attributes['ApproximateReceiveCount']
24
+
25
+ return unless attempts
26
+
27
+ attempts = attempts.to_i - 1
28
+
29
+ interval = if attempts < retry_intervals.size
30
+ retry_intervals[attempts]
31
+ else
32
+ retry_intervals.last
33
+ end
34
+
35
+ # Visibility timeouts are limited to a total 12 hours, starting from the receipt of the message.
36
+ # We calculate the maximum timeout by subtracting the amount of time since the receipt of the message.
37
+ #
38
+ # From the docs: "Amazon SQS restarts the timeout period using the new value."
39
+ # http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html#AboutVT-extending-message-visibility-timeout
40
+ max_timeout = 43200 - (Time.now - started_at).ceil - 1
41
+ interval = max_timeout if interval > max_timeout
42
+
43
+ sqs_msg.change_visibility(visibility_timeout: interval.to_i)
44
+
45
+ logger.info "Message #{sqs_msg.message_id} failed, will be retried in #{interval} seconds."
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ module Shoryuken
2
+ class Queue
3
+ attr_accessor :name, :client, :url
4
+
5
+ def initialize(client, name)
6
+ self.name = name
7
+ self.client = client
8
+ self.url = client.get_queue_url(queue_name: name).queue_url
9
+ end
10
+
11
+ def visibility_timeout
12
+ client.get_queue_attributes(
13
+ queue_url: url,
14
+ attribute_names: ['VisibilityTimeout']
15
+ ).attributes['VisibilityTimeout'].to_i
16
+ end
17
+
18
+ def delete_messages(options)
19
+ client.delete_message_batch(options.merge(queue_url: url))
20
+ end
21
+
22
+ def send_message(options)
23
+ client.send_message(sanitize_message_body(options.merge(queue_url: url)))
24
+ end
25
+
26
+ def send_messages(options)
27
+ client.send_message_batch(sanitize_message_body(options.merge(queue_url: url)))
28
+ end
29
+
30
+ def receive_messages(options)
31
+ client.receive_message(options.merge(queue_url: url)).
32
+ messages.
33
+ map { |m| Message.new(client, url, m) }
34
+ end
35
+
36
+ private
37
+
38
+ def sanitize_message_body(options)
39
+ messages = options[:entries] || [options]
40
+
41
+ messages.each do |m|
42
+ body = m[:message_body]
43
+ if body.is_a?(Hash)
44
+ m[:message_body] = JSON.dump(body)
45
+ elsif !body.is_a? String
46
+ fail ArgumentError, "The message body must be a String and you passed a #{body.class}"
47
+ end
48
+ end
49
+
50
+ options
51
+ end
52
+ end
53
+ end
@@ -1,6 +1,6 @@
1
1
  module Shoryuken
2
2
  class SnsArn
3
- def initialize topic
3
+ def initialize(topic)
4
4
  @topic = topic
5
5
  end
6
6
 
@@ -13,14 +13,14 @@ module Shoryuken
13
13
  def account_id
14
14
  Shoryuken::Client.account_id.tap do |account_id|
15
15
  if account_id.to_s.empty?
16
- fail "To generate SNS ARNs, you must assign an :account_id in your Shoryuken::Client."
16
+ fail 'To generate SNS ARNs, you must assign an :account_id in your Shoryuken::Client.'
17
17
  end
18
18
  end
19
19
  end
20
20
 
21
21
  def region
22
22
  Aws.config.fetch(:region) do
23
- fail "To generate SNS ARNs, you must include a :region in your AWS config."
23
+ fail 'To generate SNS ARNs, you must include a :region in your AWS config.'
24
24
  end
25
25
  end
26
26
  end
@@ -18,9 +18,8 @@ module Shoryuken
18
18
  end
19
19
 
20
20
  def unparse_queues(queues)
21
- queues.inject({}) do |queue_and_weights, name|
21
+ queues.each_with_object({}) do |name, queue_and_weights|
22
22
  queue_and_weights[name] = queue_and_weights[name].to_i + 1
23
- queue_and_weights
24
23
  end.to_a
25
24
  end
26
25
 
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '1.0.0'
2
+ VERSION = '1.0.1'
3
3
  end
@@ -13,7 +13,6 @@ module Shoryuken
13
13
  data_type: 'String'
14
14
  }
15
15
 
16
- body = JSON.dump(body) if body.is_a?(Hash)
17
16
  options[:message_body] = body
18
17
 
19
18
  Shoryuken::Client.queues(get_shoryuken_options['queue']).send_message(options)
data/shoryuken.gemspec CHANGED
@@ -4,27 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'shoryuken/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "shoryuken"
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 = %q{Shoryuken is a super efficient AWS SQS thread based message processor}
12
- spec.homepage = "https://github.com/phstc/shoryuken"
13
- spec.license = "LGPL-3.0"
9
+ spec.authors = ['Pablo Cantero']
10
+ spec.email = ['pablo@pablocantero.com']
11
+ spec.description = spec.summary = %q(Shoryuken is a super efficient AWS SQS thread based message processor)
12
+ spec.homepage = 'https://github.com/phstc/shoryuken'
13
+ spec.license = 'LGPL-3.0'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = %w[shoryuken]
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ['lib']
19
19
 
20
- spec.add_development_dependency "bundler", "~> 1.6"
21
- spec.add_development_dependency "rake"
22
- spec.add_development_dependency "rspec"
23
- spec.add_development_dependency "pry-byebug"
24
- spec.add_development_dependency "nokogiri"
25
- spec.add_development_dependency "dotenv"
20
+ spec.add_development_dependency 'bundler', '~> 1.6'
21
+ spec.add_development_dependency 'rake'
22
+ spec.add_development_dependency 'rspec'
23
+ spec.add_development_dependency 'pry-byebug'
24
+ spec.add_development_dependency 'nokogiri'
25
+ spec.add_development_dependency 'dotenv'
26
26
 
27
- spec.add_dependency "aws-sdk-core", "2.0.21"
28
- spec.add_dependency "aws-sdk-resources", "2.0.21.pre"
29
- spec.add_dependency "celluloid", "~> 0.16.0"
27
+ spec.add_dependency 'aws-sdk-core', '~> 2.0.21'
28
+ spec.add_dependency 'celluloid', '~> 0.16.0'
30
29
  end
@@ -48,7 +48,7 @@ describe Shoryuken::DefaultWorkerRegistry do
48
48
  string_value: explicit_worker.to_s,
49
49
  data_type: 'String' } if explicit_worker
50
50
 
51
- double Aws::SQS::Message,
51
+ double Shoryuken::Message,
52
52
  body: 'test',
53
53
  message_attributes: attributes,
54
54
  message_id: SecureRandom.uuid
@@ -4,11 +4,11 @@ require 'shoryuken/fetcher'
4
4
 
5
5
  describe Shoryuken::Fetcher do
6
6
  let(:manager) { double Shoryuken::Manager }
7
- let(:queue) { double Aws::SQS::Queue }
7
+ let(:queue) { double Shoryuken::Queue }
8
8
  let(:queue_name) { 'default' }
9
9
 
10
10
  let(:sqs_msg) do
11
- double Aws::SQS::Message,
11
+ double Shoryuken::Message,
12
12
  queue_url: queue_name,
13
13
  body: 'test',
14
14
  message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e'
@@ -24,7 +24,7 @@ describe Shoryuken::Fetcher do
24
24
 
25
25
  describe '#fetch' do
26
26
  it 'calls pause when no message' do
27
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: 1, message_attribute_names: ['All']).and_return([])
27
+ allow(queue).to receive(:receive_messages).with(max_number_of_messages: 1, attribute_names: ['All'], message_attribute_names: ['All']).and_return([])
28
28
 
29
29
  expect(manager).to receive(:pause_queue!).with(queue_name)
30
30
  expect(manager).to receive(:dispatch)
@@ -33,7 +33,7 @@ describe Shoryuken::Fetcher do
33
33
  end
34
34
 
35
35
  it 'assigns messages' do
36
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, message_attribute_names: ['All']).and_return(sqs_msg)
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
37
 
38
38
  expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
39
39
  expect(manager).to receive(:assign).with(queue_name, sqs_msg)
@@ -45,7 +45,7 @@ describe Shoryuken::Fetcher do
45
45
  it 'assigns messages in batch' do
46
46
  TestWorker.get_shoryuken_options['batch'] = true
47
47
 
48
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: described_class::FETCH_LIMIT, message_attribute_names: ['All']).and_return(sqs_msg)
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
49
 
50
50
  expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
51
51
  expect(manager).to receive(:assign).with(queue_name, [sqs_msg])
@@ -58,7 +58,7 @@ describe Shoryuken::Fetcher do
58
58
  let(:queue_name) { 'notfound' }
59
59
 
60
60
  it 'ignores batch' do
61
- allow(queue).to receive(:receive_messages).with(max_number_of_messages: 5, message_attribute_names: ['All']).and_return(sqs_msg)
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
62
 
63
63
  expect(manager).to receive(:rebalance_queue_weight!).with(queue_name)
64
64
  expect(manager).to receive(:assign).with(queue_name, sqs_msg)
@@ -2,10 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  describe Shoryuken::Middleware::Server::AutoDelete do
4
4
  let(:queue) { 'default' }
5
- let(:sqs_queue) { double Aws::SQS::Queue }
5
+ let(:sqs_queue) { double Shoryuken::Queue }
6
6
 
7
7
  def build_message
8
- double Aws::SQS::Message,
8
+ double Shoryuken::Message,
9
9
  queue_url: queue,
10
10
  body: 'test',
11
11
  receipt_handle: SecureRandom.uuid
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoryuken::Middleware::Server::ExponentialBackoffRetry do
4
+ let(:queue) { 'default' }
5
+ let(:sqs_queue) { double Shoryuken::Queue }
6
+ let(:sqs_msg) { double Shoryuken::Message, queue_url: queue, body: 'test', receipt_handle: SecureRandom.uuid,
7
+ attributes: {'ApproximateReceiveCount' => 1}, message_id: SecureRandom.uuid }
8
+
9
+ before do
10
+ allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
11
+ end
12
+
13
+ context 'when a job succeeds' do
14
+ it 'does not retry the job' do
15
+ TestWorker.get_shoryuken_options['retry_intervals'] = [300, 1800]
16
+
17
+ expect(sqs_msg).not_to receive(:change_visibility)
18
+
19
+ subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) {}
20
+ end
21
+ end
22
+
23
+ context 'when a job throws an exception' do
24
+
25
+ it 'does not retry the job by default' do
26
+ expect(sqs_msg).not_to receive(:change_visibility)
27
+
28
+ expect { subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise } }.to raise_error
29
+ end
30
+
31
+ it 'does not retry the job if :retry_intervals is empty' do
32
+ TestWorker.get_shoryuken_options['retry_intervals'] = []
33
+
34
+ expect(sqs_msg).not_to receive(:change_visibility)
35
+
36
+ expect { subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise } }.to raise_error
37
+ end
38
+
39
+ it 'retries the job if :retry_intervals is non-empty' do
40
+ TestWorker.get_shoryuken_options['retry_intervals'] = [300, 1800]
41
+
42
+ allow(sqs_msg).to receive(:queue){ sqs_queue }
43
+ expect(sqs_msg).to receive(:change_visibility).with(visibility_timeout: 300)
44
+
45
+ expect { subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise } }.not_to raise_error
46
+ end
47
+
48
+ it 'retries the job with exponential backoff' do
49
+ TestWorker.get_shoryuken_options['retry_intervals'] = [300, 1800]
50
+
51
+ allow(sqs_msg).to receive(:attributes){ {'ApproximateReceiveCount' => 2 } }
52
+ allow(sqs_msg).to receive(:queue){ sqs_queue }
53
+ expect(sqs_msg).to receive(:change_visibility).with(visibility_timeout: 1800)
54
+
55
+ expect { subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise } }.not_to raise_error
56
+ end
57
+
58
+ it 'uses the last retry interval when :receive_count exceeds the size of :retry_intervals' do
59
+ TestWorker.get_shoryuken_options['retry_intervals'] = [300, 1800]
60
+
61
+ allow(sqs_msg).to receive(:attributes){ {'ApproximateReceiveCount' => 3 } }
62
+ allow(sqs_msg).to receive(:queue){ sqs_queue }
63
+ expect(sqs_msg).to receive(:change_visibility).with(visibility_timeout: 1800)
64
+
65
+ expect { subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise } }.not_to raise_error
66
+ end
67
+
68
+ it 'limits the visibility timeout to 12 hours from receipt of message' do
69
+ TestWorker.get_shoryuken_options['retry_intervals'] = [86400]
70
+
71
+ allow(sqs_msg).to receive(:queue){ sqs_queue }
72
+ expect(sqs_msg).to receive(:change_visibility).with(visibility_timeout: 43198)
73
+
74
+ expect { subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise } }.not_to raise_error
75
+ end
76
+ end
77
+ end
@@ -2,10 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  describe Shoryuken::Middleware::Server::Timing do
4
4
  let(:queue) { 'default' }
5
- let(:sqs_queue) { double Aws::SQS::Queue, visibility_timeout: 60 }
5
+ let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 60 }
6
6
 
7
7
  let(:sqs_msg) do
8
- double Aws::SQS::Message,
8
+ double Shoryuken::Message,
9
9
  queue_url: queue,
10
10
  body: 'test',
11
11
  message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e'
@@ -4,11 +4,11 @@ require 'shoryuken/manager'
4
4
 
5
5
  describe Shoryuken::Processor do
6
6
  let(:manager) { double Shoryuken::Manager, processor_done: nil }
7
- let(:sqs_queue) { double Aws::SQS::Queue, visibility_timeout: 30 }
7
+ let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 30 }
8
8
  let(:queue) { 'default' }
9
9
 
10
10
  let(:sqs_msg) do
11
- double Aws::SQS::Message,
11
+ double Shoryuken::Message,
12
12
  queue_url: queue,
13
13
  body: 'test',
14
14
  message_attributes: {},
@@ -38,7 +38,7 @@ describe Shoryuken::Processor do
38
38
  end
39
39
 
40
40
  it 'parses the body calling the proc' do
41
- TestWorker.get_shoryuken_options['body_parser'] = Proc.new { |sqs_msg| "*#{sqs_msg.body}*" }
41
+ TestWorker.get_shoryuken_options['body_parser'] = proc { |sqs_msg| "*#{sqs_msg.body}*" }
42
42
 
43
43
  expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, '*test*')
44
44
 
@@ -207,15 +207,15 @@ describe Shoryuken::Processor do
207
207
 
208
208
  context 'when shoryuken_class header' do
209
209
  let(:sqs_msg) do
210
- double Aws::SQS::Message,
210
+ double Shoryuken::Message,
211
211
  queue_url: queue,
212
212
  body: 'test',
213
213
  message_attributes: {
214
214
  'shoryuken_class' => {
215
215
  string_value: TestWorker.to_s,
216
216
  data_type: 'String' }},
217
- message_id: SecureRandom.uuid,
218
- receipt_handle: SecureRandom.uuid
217
+ message_id: SecureRandom.uuid,
218
+ receipt_handle: SecureRandom.uuid
219
219
  end
220
220
 
221
221
  it 'performs without delete' do
@@ -244,7 +244,7 @@ describe Shoryuken::Processor do
244
244
 
245
245
  context 'when the worker takes a long time', slow: true do
246
246
  it 'extends the message invisibility to prevent it from being dequeued concurrently' do
247
- TestWorker.get_shoryuken_options['body_parser'] = Proc.new do |sqs_msg|
247
+ TestWorker.get_shoryuken_options['body_parser'] = proc do
248
248
  sleep visibility_timeout
249
249
  'test'
250
250
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoryuken::Queue do
4
+ let(:credentials) { Aws::Credentials.new('access_key_id', 'secret_access_key') }
5
+ let(:sqs) { Aws::SQS::Client.new(stub_responses: true, credentials: credentials) }
6
+ let(:queue_name) { 'shoryuken' }
7
+ let(:queue_url) { 'https://eu-west-1.amazonaws.com:6059/123456789012/shoryuken' }
8
+
9
+ subject { described_class.new(sqs, queue_name) }
10
+
11
+ describe '#send_message' do
12
+ context 'when body is invalid' do
13
+ it 'raises ArgumentError for nil' do
14
+ expect {
15
+ subject.send_message(message_body: nil)
16
+ }.to raise_error(ArgumentError, 'The message body must be a String and you passed a NilClass')
17
+ end
18
+
19
+ it 'raises ArgumentError for Fixnum' do
20
+ expect {
21
+ subject.send_message(message_body: 1)
22
+ }.to raise_error(ArgumentError, 'The message body must be a String and you passed a Fixnum')
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '#send_messages' do
28
+ context 'when body is invalid' do
29
+ it 'raises ArgumentError for nil' do
30
+ expect {
31
+ subject.send_messages(entries: [message_body: nil])
32
+ }.to raise_error(ArgumentError, 'The message body must be a String and you passed a NilClass')
33
+ end
34
+
35
+ it 'raises ArgumentError for Fixnum' do
36
+ expect {
37
+ subject.send_messages(entries: [message_body: 1])
38
+ }.to raise_error(ArgumentError, 'The message body must be a String and you passed a Fixnum')
39
+ end
40
+ end
41
+ end
42
+ end
@@ -17,7 +17,7 @@ describe 'Shoryuken::Util' do
17
17
 
18
18
  describe '#worker_name' do
19
19
  let(:sqs_msg) do
20
- double Aws::SQS::Message, message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', message_attributes: {}
20
+ double Shoryuken::Message, message_id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', message_attributes: {}
21
21
  end
22
22
 
23
23
  it 'returns Shoryuken worker name' do
@@ -66,19 +66,6 @@ describe 'Shoryuken::Worker' do
66
66
  TestWorker.perform_async('message')
67
67
  end
68
68
 
69
- it 'enqueues a message given as hash' do
70
- expect(sqs_queue).to receive(:send_message).with(
71
- message_attributes: {
72
- 'shoryuken_class' => {
73
- string_value: TestWorker.to_s,
74
- data_type: 'String'
75
- }
76
- },
77
- message_body: '{"field":"part1","other_field":"part2"}')
78
-
79
- TestWorker.perform_async(field: 'part1', other_field: 'part2')
80
- end
81
-
82
69
  it 'enqueues a message with options' do
83
70
  expect(sqs_queue).to receive(:send_message).with(
84
71
  delay_seconds: 60,
data/spec/spec_helper.rb CHANGED
@@ -42,9 +42,6 @@ RSpec.configure do |config|
42
42
  config.filter_run_excluding slow: true unless ENV['SPEC_ALL']
43
43
 
44
44
  config.before do
45
- # remove doubles, preventing:
46
- # Double "Queue" was originally created in one example but has leaked into another example and can no longer be used.
47
- # rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.
48
45
  Shoryuken::Client.class_variable_set :@@queues, {}
49
46
  Shoryuken::Client.class_variable_set :@@visibility_timeouts, {}
50
47
 
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: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pablo Cantero
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-14 00:00:00.000000000 Z
11
+ date: 2015-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -98,30 +98,16 @@ dependencies:
98
98
  name: aws-sdk-core
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '='
101
+ - - ~>
102
102
  - !ruby/object:Gem::Version
103
103
  version: 2.0.21
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '='
108
+ - - ~>
109
109
  - !ruby/object:Gem::Version
110
110
  version: 2.0.21
111
- - !ruby/object:Gem::Dependency
112
- name: aws-sdk-resources
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - '='
116
- - !ruby/object:Gem::Version
117
- version: 2.0.21.pre
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - '='
123
- - !ruby/object:Gem::Version
124
- version: 2.0.21.pre
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: celluloid
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -147,6 +133,7 @@ files:
147
133
  - .gitignore
148
134
  - .hound.yml
149
135
  - .rspec
136
+ - .rubocop.yml
150
137
  - .travis.yml
151
138
  - Gemfile
152
139
  - LICENSE.txt
@@ -166,11 +153,14 @@ files:
166
153
  - lib/shoryuken/launcher.rb
167
154
  - lib/shoryuken/logging.rb
168
155
  - lib/shoryuken/manager.rb
156
+ - lib/shoryuken/message.rb
169
157
  - lib/shoryuken/middleware/chain.rb
170
158
  - lib/shoryuken/middleware/server/active_record.rb
171
159
  - lib/shoryuken/middleware/server/auto_delete.rb
160
+ - lib/shoryuken/middleware/server/exponential_backoff_retry.rb
172
161
  - lib/shoryuken/middleware/server/timing.rb
173
162
  - lib/shoryuken/processor.rb
163
+ - lib/shoryuken/queue.rb
174
164
  - lib/shoryuken/sns_arn.rb
175
165
  - lib/shoryuken/topic.rb
176
166
  - lib/shoryuken/util.rb
@@ -188,8 +178,10 @@ files:
188
178
  - spec/shoryuken/manager_spec.rb
189
179
  - spec/shoryuken/middleware/chain_spec.rb
190
180
  - spec/shoryuken/middleware/server/auto_delete_spec.rb
181
+ - spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb
191
182
  - spec/shoryuken/middleware/server/timing_spec.rb
192
183
  - spec/shoryuken/processor_spec.rb
184
+ - spec/shoryuken/queue_spec.rb
193
185
  - spec/shoryuken/sns_arn_spec.rb
194
186
  - spec/shoryuken/topic_spec.rb
195
187
  - spec/shoryuken/util_spec.rb
@@ -230,8 +222,10 @@ test_files:
230
222
  - spec/shoryuken/manager_spec.rb
231
223
  - spec/shoryuken/middleware/chain_spec.rb
232
224
  - spec/shoryuken/middleware/server/auto_delete_spec.rb
225
+ - spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb
233
226
  - spec/shoryuken/middleware/server/timing_spec.rb
234
227
  - spec/shoryuken/processor_spec.rb
228
+ - spec/shoryuken/queue_spec.rb
235
229
  - spec/shoryuken/sns_arn_spec.rb
236
230
  - spec/shoryuken/topic_spec.rb
237
231
  - spec/shoryuken/util_spec.rb