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 +4 -4
- data/CHANGELOG.md +52 -1
- data/README.md +14 -1
- data/Rakefile +7 -0
- data/lib/shoryuken.rb +4 -2
- data/lib/shoryuken/cli.rb +22 -14
- data/lib/shoryuken/default_worker_registry.rb +2 -2
- data/lib/shoryuken/environment_loader.rb +14 -13
- data/lib/shoryuken/extensions/active_job_adapter.rb +1 -1
- data/lib/shoryuken/fetcher.rb +2 -4
- data/lib/shoryuken/manager.rb +6 -2
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +53 -0
- data/lib/shoryuken/processor.rb +0 -33
- data/lib/shoryuken/version.rb +1 -1
- data/shoryuken.gemspec +2 -2
- data/spec/shoryuken/cli_spec.rb +69 -0
- data/spec/shoryuken/core_ext_spec.rb +5 -5
- data/spec/shoryuken/manager_spec.rb +10 -1
- data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +63 -0
- data/spec/shoryuken/processor_spec.rb +5 -49
- data/spec/shoryuken_spec.rb +1 -1
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9859d893c7542fbdac0224f68e55a8b49f0fd984
|
4
|
+
data.tar.gz: d3e5419d74c6766c95c4b8ad20333bdced3cfd57
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb5725e4def20f4c82a785db1c8a055ba59892979608ce95855888bfbd321c5310f491284a2d6ef319e7bc1b3c1f186d91e376cc6b35faef600c26d4fa453325
|
7
|
+
data.tar.gz: 15274a15cd3dc7f067d4ae54a40921b90b703e09e2002ed32049ce41f6dd1261629de3b3fa842656d574455f01b13e4558536997c93d4f5ebd05880f1c80b9e2
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,55 @@
|
|
1
|
-
## [v2.0.
|
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
|
-
|
130
|
-
|
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
|
78
|
-
|
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
|
-
|
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(
|
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 =
|
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 = {
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/shoryuken/fetcher.rb
CHANGED
@@ -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
|
data/lib/shoryuken/manager.rb
CHANGED
@@ -11,7 +11,8 @@ module Shoryuken
|
|
11
11
|
trap_exit :processor_died
|
12
12
|
|
13
13
|
def initialize(condvar)
|
14
|
-
@count
|
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
|
-
|
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
|
data/lib/shoryuken/processor.rb
CHANGED
@@ -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) }
|
data/lib/shoryuken/version.rb
CHANGED
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
|
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
|
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).
|
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) }.
|
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
|
data/spec/shoryuken_spec.rb
CHANGED
@@ -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
|
+
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
|
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.
|
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
|