shoryuken 2.0.4 → 2.0.11

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: 25d7b8d6df3ab9ede1f0d7a6aba01d18e2a102f2
4
- data.tar.gz: bf26e631ee57665f7b18d0a314259b48e71951cd
3
+ metadata.gz: 9859d893c7542fbdac0224f68e55a8b49f0fd984
4
+ data.tar.gz: d3e5419d74c6766c95c4b8ad20333bdced3cfd57
5
5
  SHA512:
6
- metadata.gz: edfb890c3b31a38252bfecfd5f0f1c4d98b31c892f12106136fd91c973434f544e76c1ca48c860f2cd4f1065342231ca1dbd3a3f8752eb49613be45b57ce4c87
7
- data.tar.gz: 3c36a03cb7bc41ce4038c5942b87f3e5b087d08a4e26e9387f9dc3222bbd4e50840af7ddd68f3b073712de5d8a629d57d29ec9387e855c16499183983fea0e04
6
+ metadata.gz: cb5725e4def20f4c82a785db1c8a055ba59892979608ce95855888bfbd321c5310f491284a2d6ef319e7bc1b3c1f186d91e376cc6b35faef600c26d4fa453325
7
+ data.tar.gz: 15274a15cd3dc7f067d4ae54a40921b90b703e09e2002ed32049ce41f6dd1261629de3b3fa842656d574455f01b13e4558536997c93d4f5ebd05880f1c80b9e2
data/CHANGELOG.md CHANGED
@@ -1,4 +1,55 @@
1
- ## [v2.0.4] -
1
+ ## [v2.0.10] - 2016-06-09
2
+
3
+ - Fix manager #225
4
+ - [#226] https://github.com/phstc/shoryuken/pull/226
5
+
6
+ ## [v2.0.9] - 2016-06-08
7
+
8
+ - Fix daemonization broken in #219
9
+ - [#224] https://github.com/phstc/shoryuken/pull/224
10
+
11
+ ## [v2.0.8] - 2016-06-07
12
+
13
+ - Fix daemonization
14
+ - [#223] https://github.com/phstc/shoryuken/pull/223
15
+
16
+ ## [v2.0.7] - 2016-06-06
17
+
18
+ - Daemonize before loading environment
19
+ - [#219] https://github.com/phstc/shoryuken/pull/219
20
+
21
+ - Fix initialization when using rails
22
+ - [#197] https://github.com/phstc/shoryuken/pull/197
23
+
24
+ - Improve message fetching
25
+ - https://github.com/phstc/shoryuken/pull/214 and https://github.com/phstc/shoryuken/commit/f4640d97950c1783a061195855d93994725ed64a
26
+
27
+ - Fix hard shutdown if there are some busy workers when signal received
28
+ - [#215] https://github.com/phstc/shoryuken/pull/215
29
+
30
+ - Fix `rake console` task
31
+ - [#208] https://github.com/phstc/shoryuken/pull/208
32
+
33
+ - Isolate `MessageVisibilityExtender` as new middleware
34
+ - [#199] https://github.com/phstc/shoryuken/pull/190
35
+
36
+ - Fail on non-existent queues
37
+ - [#196] https://github.com/phstc/shoryuken/pull/196
38
+
39
+ ## [v2.0.6] - 2016-04-18
40
+
41
+ - Fix log initialization introduced by #191
42
+ - [#195](https://github.com/phstc/shoryuken/pull/195)
43
+
44
+ ## [v2.0.5] - 2016-04-17
45
+
46
+ - Fix log initialization when using `Shoryuken::EnvironmentLoader#load`
47
+ - [#191](https://github.com/phstc/shoryuken/pull/191)
48
+
49
+ - Fix `enqueue_at` in the ActiveJob Adapter
50
+ - [#182](https://github.com/phstc/shoryuken/pull/182)
51
+
52
+ ## [v2.0.4] - 2016-02-04
2
53
 
3
54
  - Add Rails 3 support
4
55
  - [#175](https://github.com/phstc/shoryuken/pull/175)
data/README.md CHANGED
@@ -105,7 +105,7 @@ end
105
105
 
106
106
  [Check the Middleware documentation](https://github.com/phstc/shoryuken/wiki/Middleware).
107
107
 
108
- ### Configuration
108
+ ### Configuration (worker side)
109
109
 
110
110
  Sample configuration file `shoryuken.yml`.
111
111
 
@@ -136,6 +136,19 @@ The ```aws``` section is used to configure both the Aws objects used by Shoryuke
136
136
 
137
137
  The ```sns_endpoint``` and ```sqs_endpoint``` Shoryuken-specific options will also fallback to the environment variables ```AWS_SNS_ENDPOINT``` and ```AWS_SQS_ENDPOINT``` respectively, if they are set.
138
138
 
139
+ ### Configuration (producer side)
140
+
141
+ 'Producer' processes need permissions to put messages into SQS. There are a few ways:
142
+
143
+ * Ensure the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` env vars are set.
144
+ * Create a `~/.aws/credentials` file.
145
+ * Set `Aws.config[:credentials]` from Ruby code (e.g. in a Rails initializer)
146
+ * Use the Instance Profiles feature. The IAM role of the targeted machine must have an adequate SQS Policy.
147
+
148
+ Note that storing your credentials into Amazon instances represents a security risk. Instance Profiles tends to be the best choice.
149
+
150
+ You can read about these in more detail [here](http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html).
151
+
139
152
  ### Rails Integration
140
153
 
141
154
  [Check the Rails Integration Active Job documention](https://github.com/phstc/shoryuken/wiki/Rails-Integration-Active-Job).
data/Rakefile CHANGED
@@ -1,9 +1,16 @@
1
1
  require 'bundler/gem_tasks'
2
2
  $stdout.sync = true
3
3
 
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ rescue LoadError
8
+ end
9
+
4
10
  desc 'Open Shoryuken pry console'
5
11
  task :console do
6
12
  require 'pry'
13
+ require 'celluloid'
7
14
  require 'shoryuken'
8
15
 
9
16
  config_file = File.join File.expand_path('..', __FILE__), 'shoryuken.yml'
data/lib/shoryuken.rb CHANGED
@@ -15,6 +15,7 @@ require 'shoryuken/worker_registry'
15
15
  require 'shoryuken/default_worker_registry'
16
16
  require 'shoryuken/middleware/chain'
17
17
  require 'shoryuken/middleware/server/auto_delete'
18
+ Shoryuken::Middleware::Server.autoload :AutoExtendVisibility, 'shoryuken/middleware/server/auto_extend_visibility'
18
19
  require 'shoryuken/middleware/server/exponential_backoff_retry'
19
20
  require 'shoryuken/middleware/server/timing'
20
21
  require 'shoryuken/sns_arn'
@@ -126,8 +127,8 @@ module Shoryuken
126
127
  # end
127
128
  # end
128
129
  def on(event, &block)
129
- raise ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
130
- raise ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
130
+ fail ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
131
+ fail ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].key?(event)
131
132
  options[:lifecycle_events][event] << block
132
133
  end
133
134
 
@@ -142,6 +143,7 @@ module Shoryuken
142
143
  m.add Middleware::Server::Timing
143
144
  m.add Middleware::Server::ExponentialBackoffRetry
144
145
  m.add Middleware::Server::AutoDelete
146
+ m.add Middleware::Server::AutoExtendVisibility
145
147
  if defined?(::ActiveRecord::Base)
146
148
  require 'shoryuken/middleware/server/active_record'
147
149
  m.add Middleware::Server::ActiveRecord
data/lib/shoryuken/cli.rb CHANGED
@@ -31,10 +31,11 @@ module Shoryuken
31
31
 
32
32
  options = parse_cli_args(args)
33
33
 
34
+ daemonize(options)
35
+ write_pid(options)
36
+
34
37
  EnvironmentLoader.load(options)
35
38
 
36
- daemonize
37
- write_pid
38
39
  load_celluloid
39
40
 
40
41
  require 'shoryuken/launcher'
@@ -63,21 +64,27 @@ module Shoryuken
63
64
  private
64
65
 
65
66
  def load_celluloid
66
- raise "Celluloid cannot be required until here, or it will break Shoryuken's daemonization" if defined?(::Celluloid) && Shoryuken.options[:daemon]
67
-
68
- # Celluloid can't be loaded until after we've daemonized
69
- # because it spins up threads and creates locks which get
70
- # into a very bad state if forked.
71
67
  require 'celluloid/autostart'
72
68
  Celluloid.logger = (Shoryuken.options[:verbose] ? Shoryuken.logger : nil)
73
69
 
74
70
  require 'shoryuken/manager'
75
71
  end
76
72
 
77
- def daemonize
78
- return unless Shoryuken.options[:daemon]
73
+ def celluloid_loaded?
74
+ defined?(::Celluloid)
75
+ end
76
+
77
+ def daemonize(options)
78
+ return unless options[:daemon]
79
+
80
+ fail ArgumentError, "You really should set a logfile if you're going to daemonize" unless options[:logfile]
79
81
 
80
- raise ArgumentError, "You really should set a logfile if you're going to daemonize" unless Shoryuken.options[:logfile]
82
+ if celluloid_loaded?
83
+ # Celluloid can't be loaded until after we've daemonized
84
+ # because it spins up threads and creates locks which get
85
+ # into a very bad state if forked.
86
+ raise "Celluloid cannot be required until here, or it will break Shoryuken's daemonization"
87
+ end
81
88
 
82
89
  files_to_reopen = []
83
90
  ObjectSpace.each_object(File) do |file|
@@ -95,7 +102,7 @@ module Shoryuken
95
102
  end
96
103
 
97
104
  [$stdout, $stderr].each do |io|
98
- File.open(Shoryuken.options[:logfile], 'ab') do |f|
105
+ File.open(options[:logfile], 'ab') do |f|
99
106
  io.reopen(f)
100
107
  end
101
108
  io.sync = true
@@ -103,8 +110,8 @@ module Shoryuken
103
110
  $stdin.reopen('/dev/null')
104
111
  end
105
112
 
106
- def write_pid
107
- if (path = Shoryuken.options[:pidfile])
113
+ def write_pid(options)
114
+ if (path = options[:pidfile])
108
115
  File.open(path, 'w') do |f|
109
116
  f.puts Process.pid
110
117
  end
@@ -112,7 +119,7 @@ module Shoryuken
112
119
  end
113
120
 
114
121
  def parse_cli_args(argv)
115
- opts = { queues: [] }
122
+ opts = {}
116
123
 
117
124
  @parser = OptionParser.new do |o|
118
125
  o.on '-c', '--concurrency INT', 'Processor threads to use' do |arg|
@@ -125,6 +132,7 @@ module Shoryuken
125
132
 
126
133
  o.on '-q', '--queue QUEUE[,WEIGHT]...', 'Queues to process with optional weights' do |arg|
127
134
  queue, weight = arg.split(',')
135
+ opts[:queues] = [] unless opts[:queues]
128
136
  opts[:queues] << [queue, weight]
129
137
  end
130
138
 
@@ -30,9 +30,9 @@ module Shoryuken
30
30
  def register_worker(queue, clazz)
31
31
  if (worker_class = @workers[queue])
32
32
  if worker_class.get_shoryuken_options['batch'] == true || clazz.get_shoryuken_options['batch'] == true
33
- raise ArgumentError, "Could not register #{clazz} for '#{queue}', "\
33
+ fail ArgumentError, "Could not register #{clazz} for '#{queue}', "\
34
34
  "because #{worker_class} is already registered for this queue, "\
35
- "and Shoryuken doesn't support a batchable worker for a queue with multiple workers."
35
+ "and Shoryuken doesn't support a batchable worker for a queue with multiple workers"
36
36
  end
37
37
  end
38
38
 
@@ -15,12 +15,11 @@ module Shoryuken
15
15
  end
16
16
 
17
17
  def load
18
- initialize_logger
19
18
  load_rails if options[:rails]
20
- Shoryuken.options.merge!(config_file_options)
19
+ initialize_options
20
+ initialize_logger
21
21
  merge_cli_defined_queues
22
22
  prefix_active_job_queue_names
23
- Shoryuken.options.merge!(options)
24
23
  parse_queues
25
24
  require_workers
26
25
  initialize_aws
@@ -31,15 +30,15 @@ module Shoryuken
31
30
 
32
31
  private
33
32
 
33
+ def initialize_options
34
+ Shoryuken.options.merge!(config_file_options)
35
+ Shoryuken.options.merge!(options)
36
+ end
37
+
34
38
  def config_file_options
35
- if (path = options[:config_file])
36
- unless File.exist?(path)
37
- Shoryuken.logger.warn { "Config file #{path} does not exist" }
38
- path = nil
39
- end
40
- end
39
+ return {} unless (path = options[:config_file])
41
40
 
42
- return {} unless path
41
+ fail ArgumentError, "The supplied config file '#{path}' does not exist" unless File.exist?(path)
43
42
 
44
43
  YAML.load(ERB.new(IO.read(path)).result).deep_symbolize_keys
45
44
  end
@@ -96,8 +95,6 @@ module Shoryuken
96
95
  require 'shoryuken/extensions/active_job_adapter' if defined?(::ActiveJob)
97
96
  require File.expand_path('config/environment.rb')
98
97
  end
99
-
100
- Shoryuken.logger.info { 'Rails environment loaded' }
101
98
  end
102
99
 
103
100
  def merge_cli_defined_queues
@@ -162,13 +159,17 @@ module Shoryuken
162
159
  def validate_queues
163
160
  Shoryuken.logger.warn { 'No queues supplied' } if Shoryuken.queues.empty?
164
161
 
162
+ non_existent_queues = []
163
+
165
164
  Shoryuken.queues.uniq.each do |queue|
166
165
  begin
167
166
  Shoryuken::Client.queues queue
168
167
  rescue Aws::SQS::Errors::NonExistentQueue
169
- Shoryuken.logger.warn { "The specified queue '#{queue}' does not exist" }
168
+ non_existent_queues << queue
170
169
  end
171
170
  end
171
+
172
+ fail ArgumentError, "The specified queue(s) #{non_existent_queues} do not exist" if non_existent_queues.any?
172
173
  end
173
174
 
174
175
  def validate_workers
@@ -26,7 +26,7 @@ module ActiveJob
26
26
  end
27
27
 
28
28
  def enqueue_at(job, timestamp)
29
- instance.enqueue(job, timestamp)
29
+ instance.enqueue_at(job, timestamp)
30
30
  end
31
31
  end
32
32
 
@@ -47,15 +47,13 @@ module Shoryuken
47
47
  @manager.async.pause_queue!(queue)
48
48
  end
49
49
 
50
- @manager.async.dispatch
51
-
52
50
  logger.debug { "Fetcher for '#{queue}' completed in #{elapsed(started_at)} ms" }
53
51
  rescue => ex
54
52
  logger.error { "Error fetching message: #{ex}" }
55
53
  logger.error { ex.backtrace.first }
56
-
57
- @manager.async.dispatch
58
54
  end
55
+
56
+ @manager.async.dispatch
59
57
  end
60
58
 
61
59
  end
@@ -11,7 +11,8 @@ module Shoryuken
11
11
  trap_exit :processor_died
12
12
 
13
13
  def initialize(condvar)
14
- @count = Shoryuken.options[:concurrency] || 25
14
+ @count = Shoryuken.options[:concurrency] || 25
15
+ raise(ArgumentError, "Concurrency value #{@count} is invalid, it needs to be a positive number") unless @count > 0
15
16
  @queues = Shoryuken.queues.dup.uniq
16
17
  @finished = condvar
17
18
 
@@ -67,6 +68,7 @@ module Shoryuken
67
68
 
68
69
  if stopped?
69
70
  processor.terminate if processor.alive?
71
+ return after(0) { @finished.signal } if @busy.empty?
70
72
  else
71
73
  @ready << processor
72
74
  end
@@ -80,7 +82,9 @@ module Shoryuken
80
82
  @threads.delete(processor.object_id)
81
83
  @busy.delete processor
82
84
 
83
- unless stopped?
85
+ if stopped?
86
+ return after(0) { @finished.signal } if @busy.empty?
87
+ else
84
88
  @ready << build_processor
85
89
  end
86
90
  end
@@ -0,0 +1,53 @@
1
+ require 'celluloid' unless defined?(Celluloid)
2
+
3
+ module Shoryuken
4
+ module Middleware
5
+ module Server
6
+ class AutoExtendVisibility
7
+ EXTEND_UPFRONT_SECONDS = 5
8
+
9
+ def call(worker, queue, sqs_msg, body)
10
+ timer = auto_visibility_timer(queue, sqs_msg, worker.class)
11
+ begin
12
+ yield
13
+ ensure
14
+ timer.cancel if timer
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ class MessageVisibilityExtender
21
+ include Celluloid
22
+ include Util
23
+
24
+ def auto_extend(queue, sqs_msg, worker_class)
25
+ queue_visibility_timeout = Shoryuken::Client.queues(queue).visibility_timeout
26
+
27
+ every(queue_visibility_timeout - EXTEND_UPFRONT_SECONDS) do
28
+ begin
29
+ logger.debug do
30
+ "Extending message #{worker_name(worker_class, sqs_msg)}/#{queue}/#{sqs_msg.message_id} " \
31
+ "visibility timeout by #{queue_visibility_timeout}s."
32
+ end
33
+
34
+ sqs_msg.change_visibility(visibility_timeout: queue_visibility_timeout)
35
+ rescue => e
36
+ logger.error do
37
+ "Could not auto extend the message #{worker_class}/#{queue}/#{sqs_msg.message_id} " \
38
+ "visibility timeout. Error: #{e.message}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def auto_visibility_timer(queue, sqs_msg, worker_class)
46
+ return unless worker_class.auto_visibility_timeout?
47
+ @visibility_extender ||= MessageVisibilityExtender.new_link
48
+ @visibility_extender.auto_extend(queue, sqs_msg, worker_class)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -16,9 +16,6 @@ module Shoryuken
16
16
 
17
17
  worker = Shoryuken.worker_registry.fetch_worker(queue, sqs_msg)
18
18
 
19
- timer = auto_visibility_timeout(queue, sqs_msg, worker.class)
20
-
21
- begin
22
19
  body = get_body(worker.class, sqs_msg)
23
20
 
24
21
  worker.class.server_middleware.invoke(worker, queue, sqs_msg, body) do
@@ -26,40 +23,10 @@ module Shoryuken
26
23
  end
27
24
 
28
25
  @manager.async.processor_done(queue, current_actor)
29
- ensure
30
- timer.cancel if timer
31
- end
32
26
  end
33
27
 
34
28
  private
35
29
 
36
- class MessageVisibilityExtender
37
- include Celluloid
38
- include Util
39
-
40
- def auto_extend(queue, sqs_msg, worker_class)
41
- queue_visibility_timeout = Shoryuken::Client.queues(queue).visibility_timeout
42
-
43
- every(queue_visibility_timeout - 5) do
44
- begin
45
- logger.debug { "Extending message #{worker_name(worker_class, sqs_msg)}/#{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s." }
46
-
47
- sqs_msg.visibility_timeout = queue_visibility_timeout
48
- rescue => e
49
- logger.error { "Could not auto extend the message #{worker_class}/#{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{e.message}" }
50
- end
51
- end
52
- end
53
- end
54
-
55
- def auto_visibility_timeout(queue, sqs_msg, worker_class)
56
- return unless worker_class.auto_visibility_timeout?
57
-
58
- @visibility_extender ||= MessageVisibilityExtender.new_link
59
-
60
- @visibility_extender.auto_extend(queue, sqs_msg, worker_class)
61
- end
62
-
63
30
  def get_body(worker_class, sqs_msg)
64
31
  if sqs_msg.is_a? Array
65
32
  sqs_msg.map { |m| parse_body(worker_class, m) }
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '2.0.4'
2
+ VERSION = '2.0.11'
3
3
  end
data/shoryuken.gemspec CHANGED
@@ -6,8 +6,8 @@ require 'shoryuken/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'shoryuken'
8
8
  spec.version = Shoryuken::VERSION
9
- spec.authors = ['Pablo Cantero']
10
- spec.email = ['pablo@pablocantero.com']
9
+ spec.authors = ['Pablo Cantero', 'Mario Kostelac']
10
+ spec.email = ['pablo@pablocantero.com', 'mariokostelac@gmail.com']
11
11
  spec.description = spec.summary = %q(Shoryuken is a super efficient AWS SQS thread based message processor)
12
12
  spec.homepage = 'https://github.com/phstc/shoryuken'
13
13
  spec.license = 'LGPL-3.0'
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+ require 'shoryuken/cli'
3
+ require 'shoryuken/launcher'
4
+
5
+ RSpec.describe Shoryuken::CLI do
6
+ let(:cli) { Shoryuken::CLI.instance }
7
+
8
+ before(:each) do
9
+ # make sure we do not bail
10
+ allow(cli).to receive(:exit)
11
+
12
+ # make sure we do not mess with standard streams
13
+ allow_any_instance_of(IO).to receive(:reopen)
14
+ end
15
+
16
+ describe '#run' do
17
+ let(:launcher) { instance_double('Shoryuken::Launcher') }
18
+
19
+ before(:each) do
20
+ allow(Shoryuken::Launcher).to receive(:new).and_return(launcher)
21
+ allow(launcher).to receive(:run).and_raise(Interrupt)
22
+ allow(launcher).to receive(:stop)
23
+ end
24
+
25
+ it 'does not raise' do
26
+ expect { cli.run([]) }.to_not raise_error
27
+ end
28
+
29
+ it 'daemonizes with --daemon --logfile' do
30
+ expect(cli).to receive(:celluloid_loaded?).and_return(false)
31
+ expect(Process).to receive(:daemon)
32
+ cli.run(['--daemon', '--logfile', '/dev/null'])
33
+ end
34
+
35
+ it 'does NOT daemonize with --daemon --logfile' do
36
+ expect(Process).to_not receive(:daemon)
37
+ cli.run(['--logfile', '/dev/null'])
38
+ end
39
+
40
+ it 'writes PID file with --pidfile' do
41
+ pidfile = instance_double('File')
42
+ expect(File).to receive(:open).with('/dev/null', 'w').and_yield(pidfile)
43
+ expect(pidfile).to receive(:puts).with(Process.pid)
44
+ cli.run(['--pidfile', '/dev/null'])
45
+ end
46
+ end
47
+
48
+ describe '#daemonize' do
49
+ before(:each) do
50
+ allow(cli).to receive(:celluloid_loaded?).and_return(false)
51
+ end
52
+
53
+ it 'raises if logfile is not set' do
54
+ expect { cli.send(:daemonize, daemon: true) }.to raise_error(ArgumentError)
55
+ end
56
+
57
+ it 'raises if Celluloid is already loaded' do
58
+ expect(cli).to receive(:celluloid_loaded?).and_return(true)
59
+ args = { daemon: true, logfile: '/dev/null' }
60
+ expect { cli.send(:daemonize, args) }.to raise_error(RuntimeError)
61
+ end
62
+
63
+ it 'calls Process.daemon' do
64
+ args = { daemon: true, logfile: '/dev/null' }
65
+ expect(Process).to receive(:daemon).with(true, true)
66
+ cli.send(:daemonize, args)
67
+ end
68
+ end
69
+ end
@@ -2,20 +2,20 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe 'Core Extensions' do
4
4
  describe Hash do
5
- describe 'stringify_keys' do
5
+ describe '#stringify_keys' do
6
6
  it 'converts keys into strings' do
7
7
  expect({ :key1 => 'value1', 'key2' => 'value2' }.stringify_keys).to eq('key1' => 'value1', 'key2' => 'value2')
8
8
  end
9
9
  end
10
10
 
11
- describe 'symbolize_keys' do
12
- it 'converts keys into strings' do
11
+ describe '#symbolize_keys' do
12
+ it 'converts keys into symbols' do
13
13
  expect({ :key1 => 'value1', 'key2' => 'value2' }.symbolize_keys).to eq(:key1 => 'value1', key2: 'value2')
14
14
  end
15
15
  end
16
16
 
17
- describe 'deep_symbolize_keys' do
18
- it 'converts keys into strings' do
17
+ describe '#deep_symbolize_keys' do
18
+ it 'converts keys into symbols' do
19
19
  expect({ :key1 => 'value1',
20
20
  'key2' => 'value2',
21
21
  'key3' => {
@@ -1,13 +1,22 @@
1
1
  require 'spec_helper'
2
2
  require 'shoryuken/manager'
3
3
 
4
- describe Shoryuken::Manager do
4
+ RSpec.describe Shoryuken::Manager do
5
5
  subject do
6
6
  condvar = double(:condvar)
7
7
  allow(condvar).to receive(:signal).and_return(nil)
8
8
  Shoryuken::Manager.new(condvar)
9
9
  end
10
10
 
11
+ describe 'Invalid concurrency setting' do
12
+ it 'raises ArgumentError if concurrency is not positive number' do
13
+ Shoryuken.options[:concurrency] = -1
14
+ expect { Shoryuken::Manager.new(nil) }
15
+ .to raise_error(ArgumentError, 'Concurrency value -1 is invalid, it needs to be a positive number')
16
+ end
17
+
18
+ end
19
+
11
20
  describe 'Auto Scaling' do
12
21
  it 'decreases weight' do
13
22
  queue1 = 'shoryuken'
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoryuken::Middleware::Server::AutoExtendVisibility do
4
+ let(:queue) { 'default' }
5
+ let(:visibility_timeout) { 3 }
6
+ let(:extend_upfront) { 1 }
7
+ let(:sqs_queue) { instance_double Shoryuken::Queue, visibility_timeout: visibility_timeout }
8
+
9
+ def build_message
10
+ double Shoryuken::Message, queue_url: queue, body: 'test', receipt_handle: SecureRandom.uuid
11
+ end
12
+
13
+ # We need to run our worker inside actor context.
14
+ class Runner
15
+ include Celluloid
16
+
17
+ def run_and_sleep(worker, queue, sqs_msg, interval)
18
+ Shoryuken::Middleware::Server::AutoExtendVisibility.new.call(worker, queue, sqs_msg, sqs_msg.body) do
19
+ sleep interval
20
+ end
21
+ end
22
+
23
+ def run_and_raise(worker, queue, sqs_msg, error_class)
24
+ Shoryuken::Middleware::Server::AutoExtendVisibility.new.call(worker, queue, sqs_msg, sqs_msg.body) do
25
+ raise error_class.new
26
+ end
27
+ end
28
+ end
29
+
30
+ let(:sqs_msg) { build_message }
31
+
32
+ before do
33
+ allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
34
+ stub_const('Shoryuken::Middleware::Server::AutoExtendVisibility::EXTEND_UPFRONT_SECONDS', extend_upfront)
35
+ end
36
+
37
+ it 'extends message visibility if jobs takes a long time' do
38
+ TestWorker.get_shoryuken_options['auto_visibility_timeout'] = true
39
+
40
+ allow(sqs_msg).to receive(:queue) { sqs_queue }
41
+ expect(sqs_msg).to receive(:change_visibility).with(visibility_timeout: visibility_timeout)
42
+
43
+ Runner.new.run_and_sleep(TestWorker.new, queue, sqs_msg, visibility_timeout)
44
+ end
45
+
46
+ it 'does not extend message visibility if worker raises' do
47
+ TestWorker.get_shoryuken_options['auto_visibility_timeout'] = true
48
+
49
+ allow(sqs_msg).to receive(:queue) { sqs_queue }
50
+ expect(sqs_msg).to_not receive(:change_visibility)
51
+
52
+ expect { Runner.new.run_and_raise(TestWorker.new, queue, sqs_msg, StandardError) }.to raise_error(StandardError)
53
+ end
54
+
55
+ it 'does not extend message visibility if auto_visibility_timeout is not true' do
56
+ TestWorker.get_shoryuken_options['auto_visibility_timeout'] = false
57
+
58
+ allow(sqs_msg).to receive(:queue) { sqs_queue }
59
+ expect(sqs_msg).to_not receive(:change_visibility)
60
+
61
+ Runner.new.run_and_sleep(TestWorker.new, queue, sqs_msg, visibility_timeout)
62
+ end
63
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
  require 'shoryuken/processor'
3
3
  require 'shoryuken/manager'
4
4
 
5
- describe Shoryuken::Processor do
5
+ RSpec.describe Shoryuken::Processor do
6
6
  let(:manager) { double Shoryuken::Manager, processor_done: nil }
7
7
  let(:sqs_queue) { double Shoryuken::Queue, visibility_timeout: 30 }
8
8
  let(:queue) { 'default' }
@@ -100,14 +100,16 @@ describe Shoryuken::Processor do
100
100
 
101
101
  it 'logs the error' do
102
102
  expect(subject.logger).to receive(:error) do |&block|
103
- expect(block.call).to eq("Error parsing the message body: 757: unexpected token at 'invalid json'\nbody_parser: json\nsqs_msg.body: invalid json")
103
+ expect(block.call).
104
+ to include("unexpected token at 'invalid json'\nbody_parser: json\nsqs_msg.body: invalid json")
104
105
  end
105
106
 
106
107
  subject.process(queue, sqs_msg) rescue nil
107
108
  end
108
109
 
109
110
  it 're raises the error' do
110
- expect{ subject.process(queue, sqs_msg) }.to raise_error(JSON::ParserError, "757: unexpected token at 'invalid json'")
111
+ expect { subject.process(queue, sqs_msg) }.
112
+ to raise_error(JSON::ParserError, /unexpected token at 'invalid json'/)
111
113
  end
112
114
  end
113
115
 
@@ -257,50 +259,4 @@ describe Shoryuken::Processor do
257
259
  end
258
260
  end
259
261
  end
260
-
261
- describe '#auto_visibility_timeout' do
262
- let(:heartbeat) { sqs_queue.visibility_timeout - 5 }
263
- let(:visibility_timeout) { sqs_queue.visibility_timeout }
264
-
265
- before do
266
- TestWorker.get_shoryuken_options['auto_visibility_timeout'] = true
267
-
268
- allow(sqs_queue).to receive(:visibility_timeout).and_return(15)
269
- end
270
-
271
- context 'when the worker takes a long time', slow: true do
272
- it 'extends the message invisibility to prevent it from being dequeued concurrently' do
273
- TestWorker.get_shoryuken_options['body_parser'] = proc do
274
- sleep visibility_timeout
275
- 'test'
276
- end
277
-
278
- expect(sqs_msg).to receive(:visibility_timeout=).with(visibility_timeout).once
279
- expect(manager).to receive(:processor_done).with(queue, subject)
280
-
281
- allow(sqs_msg).to receive(:body).and_return('test')
282
-
283
- subject.process(queue, sqs_msg)
284
- end
285
- end
286
-
287
- context 'when the worker takes a short time' do
288
- it 'does not extend the message invisibility' do
289
- expect(sqs_msg).to receive(:visibility_timeout=).never
290
- expect(manager).to receive(:processor_done).with(queue, subject)
291
-
292
- allow(sqs_msg).to receive(:body).and_return('test')
293
-
294
- subject.process(queue, sqs_msg)
295
- end
296
- end
297
-
298
- context 'when the worker fails' do
299
- it 'does not extend the message invisibility' do
300
- expect(sqs_msg).to receive(:visibility_timeout=).never
301
- expect_any_instance_of(TestWorker).to receive(:perform).and_raise 'worker failed'
302
- expect { subject.process(queue, sqs_msg) }.to raise_error(RuntimeError, 'worker failed')
303
- end
304
- end
305
- end
306
262
  end
@@ -45,7 +45,7 @@ describe Shoryuken do
45
45
  def perform(sqs_msg, body); end
46
46
  end
47
47
  }.to raise_error("Could not register BatchableWorker for 'default', because TestWorker is already registered for this queue, " \
48
- "and Shoryuken doesn't support a batchable worker for a queue with multiple workers.")
48
+ "and Shoryuken doesn't support a batchable worker for a queue with multiple workers")
49
49
  end
50
50
  end
51
51
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoryuken
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 2.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pablo Cantero
8
+ - Mario Kostelac
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2016-02-14 00:00:00.000000000 Z
12
+ date: 2016-07-02 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
@@ -125,6 +126,7 @@ dependencies:
125
126
  description: Shoryuken is a super efficient AWS SQS thread based message processor
126
127
  email:
127
128
  - pablo@pablocantero.com
129
+ - mariokostelac@gmail.com
128
130
  executables:
129
131
  - shoryuken
130
132
  extensions: []
@@ -158,6 +160,7 @@ files:
158
160
  - lib/shoryuken/middleware/chain.rb
159
161
  - lib/shoryuken/middleware/server/active_record.rb
160
162
  - lib/shoryuken/middleware/server/auto_delete.rb
163
+ - lib/shoryuken/middleware/server/auto_extend_visibility.rb
161
164
  - lib/shoryuken/middleware/server/exponential_backoff_retry.rb
162
165
  - lib/shoryuken/middleware/server/timing.rb
163
166
  - lib/shoryuken/processor.rb
@@ -172,6 +175,7 @@ files:
172
175
  - shoryuken.jpg
173
176
  - spec/integration/launcher_spec.rb
174
177
  - spec/shoryuken.yml
178
+ - spec/shoryuken/cli_spec.rb
175
179
  - spec/shoryuken/client_spec.rb
176
180
  - spec/shoryuken/core_ext_spec.rb
177
181
  - spec/shoryuken/default_worker_registry_spec.rb
@@ -179,6 +183,7 @@ files:
179
183
  - spec/shoryuken/manager_spec.rb
180
184
  - spec/shoryuken/middleware/chain_spec.rb
181
185
  - spec/shoryuken/middleware/server/auto_delete_spec.rb
186
+ - spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb
182
187
  - spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb
183
188
  - spec/shoryuken/middleware/server/timing_spec.rb
184
189
  - spec/shoryuken/processor_spec.rb
@@ -210,13 +215,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
210
215
  version: '0'
211
216
  requirements: []
212
217
  rubyforge_project:
213
- rubygems_version: 2.2.5
218
+ rubygems_version: 2.5.1
214
219
  signing_key:
215
220
  specification_version: 4
216
221
  summary: Shoryuken is a super efficient AWS SQS thread based message processor
217
222
  test_files:
218
223
  - spec/integration/launcher_spec.rb
219
224
  - spec/shoryuken.yml
225
+ - spec/shoryuken/cli_spec.rb
220
226
  - spec/shoryuken/client_spec.rb
221
227
  - spec/shoryuken/core_ext_spec.rb
222
228
  - spec/shoryuken/default_worker_registry_spec.rb
@@ -224,6 +230,7 @@ test_files:
224
230
  - spec/shoryuken/manager_spec.rb
225
231
  - spec/shoryuken/middleware/chain_spec.rb
226
232
  - spec/shoryuken/middleware/server/auto_delete_spec.rb
233
+ - spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb
227
234
  - spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb
228
235
  - spec/shoryuken/middleware/server/timing_spec.rb
229
236
  - spec/shoryuken/processor_spec.rb