shoryuken 0.0.2 → 0.0.3
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/README.md +51 -36
- data/Rakefile +9 -5
- data/examples/all.rb +3 -3
- data/examples/default_worker.rb +9 -0
- data/examples/high_priority_worker.rb +9 -0
- data/examples/low_priority_worker.rb +11 -0
- data/lib/shoryuken.rb +4 -4
- data/lib/shoryuken/cli.rb +33 -13
- data/lib/shoryuken/client.rb +4 -0
- data/lib/shoryuken/launcher.rb +1 -1
- data/lib/shoryuken/middleware/chain.rb +18 -0
- data/lib/shoryuken/middleware/server/auto_delete.rb +18 -0
- data/lib/shoryuken/middleware/server/{logging.rb → timing.rb} +5 -5
- data/lib/shoryuken/processor.rb +36 -9
- data/lib/shoryuken/util.rb +1 -0
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker.rb +4 -0
- data/shoryuken.gemspec +0 -1
- data/spec/integration/launcher_spec.rb +18 -10
- data/spec/shoryuken/client_spec.rb +14 -0
- data/spec/shoryuken/fetcher_spec.rb +3 -11
- data/spec/shoryuken/middleware/chain_spec.rb +24 -3
- data/spec/shoryuken/middleware/server/auto_delete_spec.rb +50 -0
- data/spec/shoryuken/middleware/server/timing_spec.rb +37 -0
- data/spec/shoryuken/processor_spec.rb +79 -21
- data/spec/shoryuken/worker_spec.rb +24 -10
- data/spec/spec_helper.rb +12 -1
- metadata +11 -23
- data/examples/shoryuken_worker.rb +0 -11
- data/examples/sidekiq_worker.rb +0 -11
- data/examples/uppercut_worker.rb +0 -11
- data/lib/shoryuken/middleware/server/delete.rb +0 -17
- data/spec/shoryuken/middleware/server/delete_spec.rb +0 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3afa1e42164a3fc885caf45222d317ac77692c3b
|
|
4
|
+
data.tar.gz: 9abfa8e2c9ed9b4540bbb422fba35a3f00591364
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35934726404b3d5454b1bc97875d3ba8934d2ef4efa4a354dbff97af95959c647757259ffc6fefc53a8ffaab5c1cfeffad73d79aff50a3575f5e65d39cbbb553
|
|
7
|
+
data.tar.gz: 6cafc6fbfd06fff62541acc41169877440454eba4afb1688728bbd69330ed53fa9a62d1c28b2b9a057de853952cff26d537d013747a8cc5b6c47a8b637f3c1f2
|
data/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Shoryuken _sho-ryu-ken_ is a super efficient [AWS SQS](https://aws.amazon.com/sq
|
|
|
10
10
|
|
|
11
11
|
### Load balancing
|
|
12
12
|
|
|
13
|
-
Yeah, Shoryuken load balances the messages consumption
|
|
13
|
+
Yeah, Shoryuken load balances the messages consumption!
|
|
14
14
|
|
|
15
15
|
Given this configuration:
|
|
16
16
|
|
|
@@ -18,19 +18,21 @@ Given this configuration:
|
|
|
18
18
|
concurrency: 25,
|
|
19
19
|
delay: 25,
|
|
20
20
|
queues:
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
21
|
+
- [high_priority, 6]
|
|
22
|
+
- [default, 2]
|
|
23
|
+
- [low_priority, 1]
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
And supposing all the queues are full of messages, the configuration above will make Shoryuken to process
|
|
27
|
-
splitting the work among the 25 available processors.
|
|
26
|
+
And supposing all the queues are full of messages, the configuration above will make Shoryuken to process `high_priority` 3 times more than `default` and 6 times more than `low_priority`,
|
|
27
|
+
splitting the work among the `concurrency: 25` available processors.
|
|
28
28
|
|
|
29
|
-
If
|
|
29
|
+
If `high_priority` gets empty, Shoryuken will keep using the 25 processors, but only to process `default` (2 times more than `low_priority`) and `low_priority`.
|
|
30
30
|
|
|
31
|
-
If
|
|
31
|
+
If `high_priority` receives a new message, Shoryuken will smoothly increase back the `high_priority` weight one by one until it reaches the weight of 6 again, which is the maximum configured for `high_priority`.
|
|
32
32
|
|
|
33
|
-
If all queues get empty, all processors will be changed to the waiting state and the queues will be checked every `delay: 25`. If any queue
|
|
33
|
+
If all queues get empty, all processors will be changed to the waiting state and the queues will be checked every `delay: 25`. If any queue receives a new message, Shoryuken will start processing again.
|
|
34
|
+
|
|
35
|
+
*You can set `delay: 0` to continuously check the queues without pausing even if they are empty.*
|
|
34
36
|
|
|
35
37
|
### Fetch in batches
|
|
36
38
|
|
|
@@ -42,6 +44,10 @@ Add this line to your application's Gemfile:
|
|
|
42
44
|
|
|
43
45
|
gem 'shoryuken'
|
|
44
46
|
|
|
47
|
+
**Require Shoryuken from GitHub to get the latest updates:**
|
|
48
|
+
|
|
49
|
+
gem 'shoryuken', github: 'phstc/shoryuken', branch: 'master'
|
|
50
|
+
|
|
45
51
|
And then execute:
|
|
46
52
|
|
|
47
53
|
$ bundle
|
|
@@ -55,24 +61,51 @@ Or install it yourself as:
|
|
|
55
61
|
### Worker class
|
|
56
62
|
|
|
57
63
|
```ruby
|
|
58
|
-
class
|
|
64
|
+
class MyWorker
|
|
59
65
|
include Shoryuken::Worker
|
|
60
66
|
|
|
61
|
-
shoryuken_options queue: '
|
|
62
|
-
# shoryuken_options queue: ->{ "#{ENV['environment']
|
|
67
|
+
shoryuken_options queue: 'default', delete: true
|
|
68
|
+
# shoryuken_options queue: ->{ "#{ENV['environment']_default" }
|
|
69
|
+
|
|
70
|
+
# shoryuken_options body_parser: :json
|
|
71
|
+
# shoryuken_options body_parser: ->(sqs_msg){ REXML::Document.new(sqs_msg.body) }
|
|
72
|
+
# shoryuken_options body_parser: JSON
|
|
63
73
|
|
|
64
|
-
def perform(sqs_msg)
|
|
65
|
-
puts
|
|
74
|
+
def perform(sqs_msg, body)
|
|
75
|
+
puts body
|
|
66
76
|
end
|
|
67
77
|
end
|
|
68
78
|
```
|
|
69
79
|
|
|
80
|
+
[Check the Worker options documention](https://github.com/phstc/shoryuken/wiki/Worker-options).
|
|
81
|
+
|
|
70
82
|
### Sending a message
|
|
71
83
|
|
|
72
84
|
```ruby
|
|
73
|
-
|
|
85
|
+
MyWorker.perform_async('Pablo')
|
|
86
|
+
# or
|
|
87
|
+
Shoryuken::Client.queues('default').send_message('Pablo')
|
|
88
|
+
|
|
89
|
+
# delaying a message
|
|
90
|
+
MyWorker.perform_async('Pablo', delay_seconds: 60)
|
|
91
|
+
# or
|
|
92
|
+
Shoryuken::Client.queues('default').send_message('Pablo', delay_seconds: 60)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Midleware
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
class MyMiddleware
|
|
99
|
+
def call(worker_instance, queue, sqs_msg, body)
|
|
100
|
+
puts 'Before work'
|
|
101
|
+
yield
|
|
102
|
+
puts 'After work'
|
|
103
|
+
end
|
|
104
|
+
end
|
|
74
105
|
```
|
|
75
106
|
|
|
107
|
+
[Check the Middleware documentation](https://github.com/phstc/shoryuken/wiki/Middleware).
|
|
108
|
+
|
|
76
109
|
### Configuration
|
|
77
110
|
|
|
78
111
|
Sample configuration file `shoryuken.yml`.
|
|
@@ -90,9 +123,9 @@ aws:
|
|
|
90
123
|
concurrency: 25, # The number of allocated threads to process messages. Default 25
|
|
91
124
|
delay: 25, # The delay in seconds to pause a queue when it's empty. Default 0
|
|
92
125
|
queues:
|
|
93
|
-
- [
|
|
94
|
-
- [
|
|
95
|
-
- [
|
|
126
|
+
- [high_priority, 6]
|
|
127
|
+
- [default, 2]
|
|
128
|
+
- [low_priority, 1]
|
|
96
129
|
```
|
|
97
130
|
|
|
98
131
|
### Start Shoryuken
|
|
@@ -120,24 +153,6 @@ shoryuken [options]
|
|
|
120
153
|
...
|
|
121
154
|
```
|
|
122
155
|
|
|
123
|
-
### Middleware
|
|
124
|
-
|
|
125
|
-
```ruby
|
|
126
|
-
class MyServerHook
|
|
127
|
-
def call(worker_instance, queue, sqs_msg)
|
|
128
|
-
puts 'Before work'
|
|
129
|
-
yield
|
|
130
|
-
puts 'After work'
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
Shoryuken.configure_server do |config|
|
|
135
|
-
config.server_middleware do |chain|
|
|
136
|
-
chain.add MyServerHook
|
|
137
|
-
# chain.remove MyServerHook
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
```
|
|
141
156
|
|
|
142
157
|
## More Information
|
|
143
158
|
|
data/Rakefile
CHANGED
|
@@ -22,7 +22,7 @@ task :console do
|
|
|
22
22
|
Pry.start
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
desc 'Push test messages to
|
|
25
|
+
desc 'Push test messages to high_priority, default and low_priority'
|
|
26
26
|
task :push_test, :size do |t, args|
|
|
27
27
|
require 'yaml'
|
|
28
28
|
require 'shoryuken'
|
|
@@ -31,13 +31,17 @@ task :push_test, :size do |t, args|
|
|
|
31
31
|
|
|
32
32
|
AWS.config(config['aws'])
|
|
33
33
|
|
|
34
|
+
Shoryuken::Client.sqs.queues.create('default')
|
|
35
|
+
Shoryuken::Client.sqs.queues.create('high_priority')
|
|
36
|
+
Shoryuken::Client.sqs.queues.create('low_priority')
|
|
37
|
+
|
|
34
38
|
(args[:size] || 1).to_i.times.map do |i|
|
|
35
39
|
Thread.new do
|
|
36
|
-
|
|
37
|
-
Shoryuken::Client.queues('uppercut').send_message("uppercut #{i}")
|
|
38
|
-
Shoryuken::Client.queues('sidekiq').send_message("sidekiq #{i}")
|
|
40
|
+
puts "Pushing test ##{i}"
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
Shoryuken::Client.queues('high_priority').send_message("test #{i}")
|
|
43
|
+
Shoryuken::Client.queues('default').send_message("test #{i}")
|
|
44
|
+
Shoryuken::Client.queues('low_priority').send_message("test #{i}")
|
|
41
45
|
end
|
|
42
46
|
end.each &:join
|
|
43
47
|
end
|
data/examples/all.rb
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class LowPriorityWorker
|
|
2
|
+
include Shoryuken::Worker
|
|
3
|
+
|
|
4
|
+
shoryuken_options queue: 'low_priority', delete: true, batch: true
|
|
5
|
+
|
|
6
|
+
def perform(sqs_msgs, bodies)
|
|
7
|
+
bodies.each_with_index do |body, index|
|
|
8
|
+
puts "LowPriorityWorker (#{index}): '#{body}'"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/shoryuken.rb
CHANGED
|
@@ -9,8 +9,8 @@ require 'shoryuken/client'
|
|
|
9
9
|
require 'shoryuken/worker'
|
|
10
10
|
require 'shoryuken/logging'
|
|
11
11
|
require 'shoryuken/middleware/chain'
|
|
12
|
-
require 'shoryuken/middleware/server/
|
|
13
|
-
require 'shoryuken/middleware/server/
|
|
12
|
+
require 'shoryuken/middleware/server/auto_delete'
|
|
13
|
+
require 'shoryuken/middleware/server/timing'
|
|
14
14
|
|
|
15
15
|
module Shoryuken
|
|
16
16
|
DEFAULTS = {
|
|
@@ -64,8 +64,8 @@ module Shoryuken
|
|
|
64
64
|
|
|
65
65
|
def self.default_server_middleware
|
|
66
66
|
Middleware::Chain.new do |m|
|
|
67
|
-
m.add Middleware::Server::
|
|
68
|
-
m.add Middleware::Server::
|
|
67
|
+
m.add Middleware::Server::Timing
|
|
68
|
+
m.add Middleware::Server::AutoDelete
|
|
69
69
|
# TODO m.add Middleware::Server::RetryJobs
|
|
70
70
|
end
|
|
71
71
|
end
|
data/lib/shoryuken/cli.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Shoryuken
|
|
|
16
16
|
def run(args)
|
|
17
17
|
self_read, self_write = IO.pipe
|
|
18
18
|
|
|
19
|
-
%w
|
|
19
|
+
%w[INT TERM USR1 USR2 TTIN].each do |sig|
|
|
20
20
|
trap sig do
|
|
21
21
|
self_write.puts(sig)
|
|
22
22
|
end
|
|
@@ -24,11 +24,12 @@ module Shoryuken
|
|
|
24
24
|
|
|
25
25
|
setup_options(args)
|
|
26
26
|
initialize_logger
|
|
27
|
+
require_workers
|
|
27
28
|
validate!
|
|
29
|
+
patch_deprecated_workers!
|
|
28
30
|
daemonize
|
|
29
31
|
write_pid
|
|
30
32
|
load_celluloid
|
|
31
|
-
require_workers
|
|
32
33
|
|
|
33
34
|
require 'shoryuken/launcher'
|
|
34
35
|
@launcher = Shoryuken::Launcher.new
|
|
@@ -144,7 +145,7 @@ module Shoryuken
|
|
|
144
145
|
|
|
145
146
|
@parser.banner = 'shoryuken [options]'
|
|
146
147
|
@parser.on_tail '-h', '--help', 'Show help' do
|
|
147
|
-
|
|
148
|
+
logger.info @parser
|
|
148
149
|
exit 1
|
|
149
150
|
end
|
|
150
151
|
@parser.parse!(argv)
|
|
@@ -152,22 +153,22 @@ module Shoryuken
|
|
|
152
153
|
end
|
|
153
154
|
|
|
154
155
|
def handle_signal(sig)
|
|
155
|
-
|
|
156
|
+
logger.info "Got #{sig} signal"
|
|
156
157
|
|
|
157
158
|
case sig
|
|
158
159
|
when 'USR1'
|
|
159
|
-
|
|
160
|
+
logger.info "Received USR1, will soft shutdown down"
|
|
160
161
|
|
|
161
162
|
launcher.stop
|
|
162
163
|
|
|
163
164
|
exit 0
|
|
164
165
|
when 'TTIN'
|
|
165
166
|
Thread.list.each do |thread|
|
|
166
|
-
|
|
167
|
+
logger.info "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
|
167
168
|
if thread.backtrace
|
|
168
|
-
|
|
169
|
+
logger.info thread.backtrace.join("\n")
|
|
169
170
|
else
|
|
170
|
-
|
|
171
|
+
logger.info "<no backtrace available>"
|
|
171
172
|
end
|
|
172
173
|
end
|
|
173
174
|
|
|
@@ -175,9 +176,9 @@ module Shoryuken
|
|
|
175
176
|
busy = launcher.manager.instance_variable_get(:@busy).size
|
|
176
177
|
queues = launcher.manager.instance_variable_get(:@queues)
|
|
177
178
|
|
|
178
|
-
|
|
179
|
+
logger.info "Ready: #{ready}, Busy: #{busy}, Active Queues: #{unparse_queues(queues)}"
|
|
179
180
|
else
|
|
180
|
-
|
|
181
|
+
logger.info "Received #{sig}, will shutdown down"
|
|
181
182
|
|
|
182
183
|
raise Interrupt
|
|
183
184
|
end
|
|
@@ -210,13 +211,18 @@ module Shoryuken
|
|
|
210
211
|
end
|
|
211
212
|
|
|
212
213
|
def validate!
|
|
214
|
+
raise ArgumentError, 'No queues supplied' if Shoryuken.queues.empty?
|
|
215
|
+
|
|
216
|
+
if queue_without_worker = Shoryuken.queues.find { |queue| Shoryuken.workers[queue].nil? }
|
|
217
|
+
raise ArgumentError, "No worker supplied for #{queue_without_worker}"
|
|
218
|
+
end
|
|
219
|
+
|
|
213
220
|
if Shoryuken.options[:aws][:access_key_id].nil? && Shoryuken.options[:aws][:secret_access_key].nil?
|
|
214
221
|
if ENV['AWS_ACCESS_KEY_ID'].nil? && ENV['AWS_SECRET_ACCESS_KEY'].nil?
|
|
215
222
|
raise ArgumentError, 'No AWS credentials supplied'
|
|
216
223
|
end
|
|
217
224
|
end
|
|
218
225
|
|
|
219
|
-
|
|
220
226
|
initialize_aws
|
|
221
227
|
|
|
222
228
|
Shoryuken.queues.uniq.each do |queue|
|
|
@@ -229,8 +235,6 @@ module Shoryuken
|
|
|
229
235
|
raise
|
|
230
236
|
end
|
|
231
237
|
end
|
|
232
|
-
|
|
233
|
-
raise ArgumentError, 'No queues supplied' if Shoryuken.queues.empty?
|
|
234
238
|
end
|
|
235
239
|
|
|
236
240
|
def initialize_aws
|
|
@@ -250,5 +254,21 @@ module Shoryuken
|
|
|
250
254
|
def parse_queue(queue, weight = nil)
|
|
251
255
|
[weight.to_i, 1].max.times { Shoryuken.queues << queue }
|
|
252
256
|
end
|
|
257
|
+
|
|
258
|
+
def patch_deprecated_workers!
|
|
259
|
+
Shoryuken.workers.each do |queue, worker_class|
|
|
260
|
+
if worker_class.instance_method(:perform).arity == 1
|
|
261
|
+
logger.warn "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)"
|
|
262
|
+
|
|
263
|
+
worker_class.class_eval do
|
|
264
|
+
alias_method :deprecated_perform, :perform
|
|
265
|
+
|
|
266
|
+
def perform(sqs_msg, body = nil)
|
|
267
|
+
deprecated_perform(sqs_msg)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
253
273
|
end
|
|
254
274
|
end
|
data/lib/shoryuken/client.rb
CHANGED
data/lib/shoryuken/launcher.rb
CHANGED
|
@@ -34,7 +34,7 @@ module Shoryuken
|
|
|
34
34
|
|
|
35
35
|
def actor_died(actor, reason)
|
|
36
36
|
return if @done
|
|
37
|
-
|
|
37
|
+
logger.warn 'Shoryuken died due to the following error, cannot recover, process exiting'
|
|
38
38
|
exit 1
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -101,11 +101,29 @@ module Shoryuken
|
|
|
101
101
|
def initialize(klass, *args)
|
|
102
102
|
@klass = klass
|
|
103
103
|
@args = args
|
|
104
|
+
|
|
105
|
+
patch_deprecated_middleware!(klass)
|
|
104
106
|
end
|
|
105
107
|
|
|
106
108
|
def make_new
|
|
107
109
|
@klass.new(*@args)
|
|
108
110
|
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def patch_deprecated_middleware!(klass)
|
|
115
|
+
if klass.instance_method(:call).arity == 3
|
|
116
|
+
Shoryuken.logger.warn "[DEPRECATION] #{klass.name}#call(worker_instance, queue, sqs_msg) is deprecated. Please use #{klass.name}#call(worker_instance, queue, sqs_msg, body)"
|
|
117
|
+
|
|
118
|
+
klass.class_eval do
|
|
119
|
+
alias_method :deprecated_call, :call
|
|
120
|
+
|
|
121
|
+
def call(worker_instance, queue, sqs_msg, body = nil, &block)
|
|
122
|
+
deprecated_call(worker_instance, queue, sqs_msg, &block)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
109
127
|
end
|
|
110
128
|
end
|
|
111
129
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Shoryuken
|
|
2
|
+
module Middleware
|
|
3
|
+
module Server
|
|
4
|
+
class AutoDelete
|
|
5
|
+
|
|
6
|
+
def call(worker, queue, sqs_msg, body)
|
|
7
|
+
yield
|
|
8
|
+
|
|
9
|
+
# I'm still deciding, but `auto_delete` will be probably deprecated soon
|
|
10
|
+
delete = worker.class.get_shoryuken_options['delete'] || worker.class.get_shoryuken_options['auto_delete']
|
|
11
|
+
|
|
12
|
+
Shoryuken::Client.queues(queue).batch_delete(*Array(sqs_msg)) if delete
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
module Shoryuken
|
|
2
2
|
module Middleware
|
|
3
3
|
module Server
|
|
4
|
-
class
|
|
4
|
+
class Timing
|
|
5
5
|
include Util
|
|
6
6
|
|
|
7
|
-
def call(worker, queue, sqs_msg)
|
|
7
|
+
def call(worker, queue, sqs_msg, body)
|
|
8
8
|
Shoryuken::Logging.with_context("#{worker.class.to_s}/#{queue}/#{sqs_msg.id}") do
|
|
9
9
|
begin
|
|
10
10
|
started_at = Time.now
|
|
11
11
|
|
|
12
|
-
logger.info
|
|
12
|
+
logger.info "started at #{started_at}"
|
|
13
13
|
|
|
14
14
|
yield
|
|
15
15
|
|
|
@@ -19,9 +19,9 @@ module Shoryuken
|
|
|
19
19
|
logger.warn "exceeded the queue visibility timeout by #{total_time - (timeout * 1000)} ms"
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
logger.info
|
|
22
|
+
logger.info "completed in: #{total_time} ms"
|
|
23
23
|
rescue => e
|
|
24
|
-
logger.info
|
|
24
|
+
logger.info "failed in: #{elapsed(started_at)} ms"
|
|
25
25
|
raise e
|
|
26
26
|
end
|
|
27
27
|
end
|
data/lib/shoryuken/processor.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'json'
|
|
2
2
|
|
|
3
3
|
module Shoryuken
|
|
4
4
|
class Processor
|
|
@@ -10,19 +10,46 @@ module Shoryuken
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def process(queue, sqs_msg)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
worker_class = Shoryuken.workers[queue]
|
|
14
|
+
defer do
|
|
15
|
+
body = get_body(worker_class, sqs_msg)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
worker = worker_class.new
|
|
18
|
+
|
|
19
|
+
Shoryuken.server_middleware.invoke(worker, queue, sqs_msg, body) do
|
|
20
|
+
worker.perform(sqs_msg, body)
|
|
20
21
|
end
|
|
21
|
-
else
|
|
22
|
-
logger.error "Worker not found for queue '#{queue}'"
|
|
23
22
|
end
|
|
24
23
|
|
|
25
24
|
@manager.async.processor_done(queue, current_actor)
|
|
26
25
|
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def get_body(worker_class, sqs_msg)
|
|
30
|
+
if sqs_msg.is_a? Array
|
|
31
|
+
sqs_msg.map { |m| parse_body(worker_class, m) }
|
|
32
|
+
else
|
|
33
|
+
parse_body(worker_class, sqs_msg)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parse_body(worker_class, sqs_msg)
|
|
38
|
+
body_parser = worker_class.get_shoryuken_options['body_parser']
|
|
39
|
+
|
|
40
|
+
case body_parser
|
|
41
|
+
when :json
|
|
42
|
+
JSON.parse(sqs_msg.body)
|
|
43
|
+
when Proc
|
|
44
|
+
body_parser.call(sqs_msg)
|
|
45
|
+
when :text, nil
|
|
46
|
+
sqs_msg.body
|
|
47
|
+
else
|
|
48
|
+
body_parser.parse(sqs_msg.body) if body_parser.respond_to?(:parse) # i.e. JSON.parse(...)
|
|
49
|
+
end
|
|
50
|
+
rescue => e
|
|
51
|
+
logger.error "Error parsing the message body: #{e.message}\nbody_parser: #{body_parser}\nsqs_msg.body: #{sqs_msg.body}"
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
27
54
|
end
|
|
28
55
|
end
|
data/lib/shoryuken/util.rb
CHANGED
data/lib/shoryuken/version.rb
CHANGED
data/lib/shoryuken/worker.rb
CHANGED
|
@@ -5,6 +5,10 @@ module Shoryuken
|
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
module ClassMethods
|
|
8
|
+
def perform_async(body, options = {})
|
|
9
|
+
Shoryuken::Client.send_message(get_shoryuken_options['queue'], body, options)
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def shoryuken_options(opts = {})
|
|
9
13
|
@shoryuken_options = get_shoryuken_options.merge(stringify_keys(Hash(opts)))
|
|
10
14
|
queue = @shoryuken_options['queue']
|
data/shoryuken.gemspec
CHANGED
|
@@ -11,20 +11,28 @@ describe Shoryuken::Launcher do
|
|
|
11
11
|
|
|
12
12
|
subject.run
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
ShoryukenWorker.received_messages = 0
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
after
|
|
18
|
-
subject.stop
|
|
19
|
-
end
|
|
17
|
+
after { subject.stop }
|
|
20
18
|
|
|
21
19
|
class ShoryukenWorker
|
|
22
20
|
include Shoryuken::Worker
|
|
23
21
|
|
|
22
|
+
@@received_messages = 0
|
|
23
|
+
|
|
24
24
|
shoryuken_options queue: 'shoryuken', delete: true
|
|
25
25
|
|
|
26
|
-
def perform(sqs_msg)
|
|
27
|
-
|
|
26
|
+
def perform(sqs_msg, body)
|
|
27
|
+
@@received_messages = Array(sqs_msg).size
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.received_messages
|
|
31
|
+
@@received_messages
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.received_messages=(received_messages)
|
|
35
|
+
@@received_messages = received_messages
|
|
28
36
|
end
|
|
29
37
|
end
|
|
30
38
|
|
|
@@ -34,11 +42,11 @@ describe Shoryuken::Launcher do
|
|
|
34
42
|
Shoryuken::Client.queues('shoryuken').send_message('Yo')
|
|
35
43
|
|
|
36
44
|
10.times do
|
|
37
|
-
break if
|
|
45
|
+
break if ShoryukenWorker.received_messages > 0
|
|
38
46
|
sleep 0.2
|
|
39
47
|
end
|
|
40
48
|
|
|
41
|
-
expect(
|
|
49
|
+
expect(ShoryukenWorker.received_messages).to eq 1
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
it 'consumes a batch' do
|
|
@@ -47,12 +55,12 @@ describe Shoryuken::Launcher do
|
|
|
47
55
|
Shoryuken::Client.queues('shoryuken').batch_send *(['Yo'] * 10)
|
|
48
56
|
|
|
49
57
|
10.times do
|
|
50
|
-
break if
|
|
58
|
+
break if ShoryukenWorker.received_messages > 0
|
|
51
59
|
sleep 0.2
|
|
52
60
|
end
|
|
53
61
|
|
|
54
62
|
# the fetch result is uncertain, should be greater than 1, but hard to tell the exact size
|
|
55
|
-
expect(
|
|
63
|
+
expect(ShoryukenWorker.received_messages).to be > 1
|
|
56
64
|
end
|
|
57
65
|
end
|
|
58
66
|
end
|
|
@@ -21,6 +21,20 @@ describe Shoryuken::Client do
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
describe '.send_message' do
|
|
25
|
+
it 'enqueues a message' do
|
|
26
|
+
expect(sqs_queue).to receive(:send_message).with('test', {})
|
|
27
|
+
|
|
28
|
+
described_class.send_message(queue, 'test')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'enqueues a message with options' do
|
|
32
|
+
expect(sqs_queue).to receive(:send_message).with('test2', delay_seconds: 60)
|
|
33
|
+
|
|
34
|
+
described_class.send_message(queue, 'test2', delay_seconds: 60)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
24
38
|
describe '.visibility_timeout' do
|
|
25
39
|
it 'memoizes visibility_timeout' do
|
|
26
40
|
expect(sqs_queue).to receive(:visibility_timeout).once.and_return(30)
|
|
@@ -5,8 +5,8 @@ require 'shoryuken/fetcher'
|
|
|
5
5
|
describe Shoryuken::Fetcher do
|
|
6
6
|
let(:manager) { double Shoryuken::Manager }
|
|
7
7
|
let(:sqs_queue) { double 'sqs_queue' }
|
|
8
|
-
let(:queue) { '
|
|
9
|
-
let(:sqs_msg) { double
|
|
8
|
+
let(:queue) { 'default' }
|
|
9
|
+
let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test' }
|
|
10
10
|
|
|
11
11
|
subject { described_class.new(manager) }
|
|
12
12
|
|
|
@@ -16,14 +16,6 @@ describe Shoryuken::Fetcher do
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class Shoryuken2Worker
|
|
20
|
-
include Shoryuken::Worker
|
|
21
|
-
|
|
22
|
-
shoryuken_options queue: 'shoryuken2'
|
|
23
|
-
|
|
24
|
-
def perform(sqs_msg); end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
19
|
describe '#fetch' do
|
|
28
20
|
it 'calls pause when no message' do
|
|
29
21
|
allow(sqs_queue).to receive(:receive_message).with(limit: 1).and_return([])
|
|
@@ -45,7 +37,7 @@ describe Shoryuken::Fetcher do
|
|
|
45
37
|
end
|
|
46
38
|
|
|
47
39
|
it 'assigns messages in batch' do
|
|
48
|
-
|
|
40
|
+
TestWorker.get_shoryuken_options['batch'] = true
|
|
49
41
|
|
|
50
42
|
allow(sqs_queue).to receive(:receive_message).with(limit: described_class::FETCH_LIMIT).and_return(sqs_msg)
|
|
51
43
|
|
|
@@ -20,7 +20,7 @@ describe Shoryuken::Middleware::Chain do
|
|
|
20
20
|
expect(CustomMiddleware).to eq subject.entries.last.klass
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
it '
|
|
23
|
+
it 'invokes a middleware' do
|
|
24
24
|
recorder = []
|
|
25
25
|
subject.add CustomMiddleware, 'Pablo', recorder
|
|
26
26
|
|
|
@@ -31,8 +31,7 @@ describe Shoryuken::Middleware::Chain do
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
class NonYieldingMiddleware
|
|
34
|
-
def call(*args)
|
|
35
|
-
end
|
|
34
|
+
def call(*args); end
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
it 'allows middleware to abruptly stop processing rest of chain' do
|
|
@@ -45,4 +44,26 @@ describe Shoryuken::Middleware::Chain do
|
|
|
45
44
|
expect(final_action).to eq nil
|
|
46
45
|
expect(recorder).to eq []
|
|
47
46
|
end
|
|
47
|
+
|
|
48
|
+
class DeprecatedMiddleware
|
|
49
|
+
def call(worker_instance, queue, sqs_msg)
|
|
50
|
+
@@success = true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.success?
|
|
54
|
+
!!@@success
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'patches deprecated middleware' do
|
|
59
|
+
subject.clear
|
|
60
|
+
|
|
61
|
+
expect(Shoryuken.logger).to receive(:warn).with("[DEPRECATION] DeprecatedMiddleware#call(worker_instance, queue, sqs_msg) is deprecated. Please use DeprecatedMiddleware#call(worker_instance, queue, sqs_msg, body)")
|
|
62
|
+
|
|
63
|
+
subject.add DeprecatedMiddleware
|
|
64
|
+
|
|
65
|
+
subject.invoke TestWorker, 'test', double('SQS msg', body: 'test'), 'test'
|
|
66
|
+
|
|
67
|
+
expect(DeprecatedMiddleware.success?).to eq true
|
|
68
|
+
end
|
|
48
69
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Shoryuken::Middleware::Server::AutoDelete do
|
|
4
|
+
let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test' }
|
|
5
|
+
let(:queue) { 'default' }
|
|
6
|
+
let(:sqs_queue) { double AWS::SQS::Queue }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'deletes a message' do
|
|
13
|
+
TestWorker.get_shoryuken_options['delete'] = true
|
|
14
|
+
|
|
15
|
+
expect(sqs_queue).to receive(:batch_delete).with(sqs_msg)
|
|
16
|
+
|
|
17
|
+
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'deletes a batch' do
|
|
21
|
+
TestWorker.get_shoryuken_options['delete'] = true
|
|
22
|
+
|
|
23
|
+
sqs_msg2 = double 'SQS msg', body: 'test'
|
|
24
|
+
sqs_msg3 = double 'SQS msg', body: 'test'
|
|
25
|
+
|
|
26
|
+
sqs_msgs = [sqs_msg, sqs_msg2, sqs_msg3]
|
|
27
|
+
|
|
28
|
+
expect(sqs_queue).to receive(:batch_delete).with(*sqs_msgs)
|
|
29
|
+
|
|
30
|
+
subject.call(TestWorker.new, queue, sqs_msgs, [sqs_msg.body, sqs_msg2.body, sqs_msg3.body]) {}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'does not delete a message' do
|
|
34
|
+
TestWorker.get_shoryuken_options['delete'] = false
|
|
35
|
+
|
|
36
|
+
expect(sqs_queue).to_not receive(:batch_delete)
|
|
37
|
+
|
|
38
|
+
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) {}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'when exception' do
|
|
42
|
+
it 'does not delete a message' do
|
|
43
|
+
expect(sqs_queue).to_not receive(:batch_delete)
|
|
44
|
+
|
|
45
|
+
expect {
|
|
46
|
+
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise }
|
|
47
|
+
}.to raise_error
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Shoryuken::Middleware::Server::Timing do
|
|
4
|
+
let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test' }
|
|
5
|
+
let(:queue) { 'default' }
|
|
6
|
+
|
|
7
|
+
xit 'logs timing' do
|
|
8
|
+
expect(Shoryuken.logger).to receive(:info).with(/started at/)
|
|
9
|
+
expect(Shoryuken.logger).to receive(:info).with(/completed in/)
|
|
10
|
+
|
|
11
|
+
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'when exceeded the `visibility_timeout`' do
|
|
15
|
+
it 'logs exceeded' do
|
|
16
|
+
allow(Shoryuken::Client).to receive(:visibility_timeout).and_return(60)
|
|
17
|
+
allow(subject).to receive(:elapsed).and_return(120000)
|
|
18
|
+
|
|
19
|
+
expect(Shoryuken.logger).to receive(:info).with(/started at/)
|
|
20
|
+
expect(Shoryuken.logger).to receive(:info).with(/completed in/)
|
|
21
|
+
expect(Shoryuken.logger).to receive(:warn).with('exceeded the queue visibility timeout by 60000 ms')
|
|
22
|
+
|
|
23
|
+
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) {}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'when exception' do
|
|
28
|
+
it 'logs failed in' do
|
|
29
|
+
expect(Shoryuken.logger).to receive(:info).with(/started at/)
|
|
30
|
+
expect(Shoryuken.logger).to receive(:info).with(/failed in/)
|
|
31
|
+
|
|
32
|
+
expect {
|
|
33
|
+
subject.call(TestWorker.new, queue, sqs_msg, sqs_msg.body) { raise }
|
|
34
|
+
}.to raise_error
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
require 'shoryuken/processor'
|
|
3
|
+
require 'shoryuken/manager'
|
|
3
4
|
|
|
4
5
|
describe Shoryuken::Processor do
|
|
5
|
-
let(:manager) { double Shoryuken::Manager }
|
|
6
|
-
let(:sqs_queue) { double
|
|
7
|
-
let(:queue) { '
|
|
8
|
-
let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e' }
|
|
6
|
+
let(:manager) { double Shoryuken::Manager, processor_done: nil }
|
|
7
|
+
let(:sqs_queue) { double AWS::SQS::Queue, visibility_timeout: 30 }
|
|
8
|
+
let(:queue) { 'default' }
|
|
9
|
+
let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test' }
|
|
9
10
|
|
|
10
11
|
subject { described_class.new(manager) }
|
|
11
12
|
|
|
@@ -15,27 +16,84 @@ describe Shoryuken::Processor do
|
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
describe '#process' do
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
it 'parses the body into JSON' do
|
|
20
|
+
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
body = { 'test' => 'hi' }
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
25
|
+
|
|
26
|
+
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
27
|
+
|
|
28
|
+
subject.process(queue, sqs_msg)
|
|
24
29
|
end
|
|
25
30
|
|
|
26
|
-
it '
|
|
27
|
-
|
|
31
|
+
it 'parses the body calling the proc' do
|
|
32
|
+
TestWorker.get_shoryuken_options['body_parser'] = Proc.new { |sqs_msg| "*#{sqs_msg.body}*" }
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, '*test*')
|
|
35
|
+
|
|
36
|
+
allow(sqs_msg).to receive(:body).and_return('test')
|
|
37
|
+
|
|
38
|
+
subject.process(queue, sqs_msg)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'parses the body as text' do
|
|
42
|
+
TestWorker.get_shoryuken_options['body_parser'] = :text
|
|
43
|
+
|
|
44
|
+
body = 'test'
|
|
45
|
+
|
|
46
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
47
|
+
|
|
48
|
+
allow(sqs_msg).to receive(:body).and_return(body)
|
|
49
|
+
|
|
50
|
+
subject.process(queue, sqs_msg)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'parses calling `.parse`' do
|
|
54
|
+
TestWorker.get_shoryuken_options['body_parser'] = JSON
|
|
55
|
+
|
|
56
|
+
body = { 'test' => 'hi' }
|
|
30
57
|
|
|
31
|
-
|
|
58
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
59
|
+
|
|
60
|
+
allow(sqs_msg).to receive(:body).and_return(JSON.dump(body))
|
|
32
61
|
|
|
33
62
|
subject.process(queue, sqs_msg)
|
|
34
63
|
end
|
|
35
64
|
|
|
65
|
+
context 'when parse errors' do
|
|
66
|
+
it 'does not fail' do
|
|
67
|
+
TestWorker.get_shoryuken_options['body_parser'] = :json
|
|
68
|
+
|
|
69
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, nil)
|
|
70
|
+
|
|
71
|
+
allow(sqs_msg).to receive(:body).and_return('invalid json')
|
|
72
|
+
|
|
73
|
+
expect(subject.logger).to receive(:error).with("Error parsing the message body: 757: unexpected token at 'invalid json'\nbody_parser: json\nsqs_msg.body: invalid json")
|
|
74
|
+
|
|
75
|
+
subject.process(queue, sqs_msg)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context 'when `object_type: nil`' do
|
|
80
|
+
it 'parses the body as text' do
|
|
81
|
+
TestWorker.get_shoryuken_options['body_parser'] = nil
|
|
82
|
+
|
|
83
|
+
body = 'test'
|
|
84
|
+
|
|
85
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, body)
|
|
86
|
+
|
|
87
|
+
allow(sqs_msg).to receive(:body).and_return(body)
|
|
88
|
+
|
|
89
|
+
subject.process(queue, sqs_msg)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
36
93
|
context 'when custom middleware' do
|
|
37
94
|
class WorkerCalledMiddleware
|
|
38
|
-
def call(worker, queue, sqs_msg)
|
|
95
|
+
def call(worker, queue, sqs_msg, body)
|
|
96
|
+
# called is defined with `allow(...).to receive(...)`
|
|
39
97
|
worker.called(sqs_msg, queue)
|
|
40
98
|
yield
|
|
41
99
|
end
|
|
@@ -60,33 +118,33 @@ describe Shoryuken::Processor do
|
|
|
60
118
|
it 'invokes middleware' do
|
|
61
119
|
expect(manager).to receive(:processor_done).with(queue, subject)
|
|
62
120
|
|
|
63
|
-
expect_any_instance_of(
|
|
64
|
-
expect_any_instance_of(
|
|
121
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
122
|
+
expect_any_instance_of(TestWorker).to receive(:called).with(sqs_msg, queue)
|
|
65
123
|
|
|
66
124
|
subject.process(queue, sqs_msg)
|
|
67
125
|
end
|
|
68
126
|
end
|
|
69
127
|
|
|
70
128
|
it 'performs with delete' do
|
|
71
|
-
|
|
129
|
+
TestWorker.get_shoryuken_options['delete'] = true
|
|
72
130
|
|
|
73
131
|
expect(manager).to receive(:processor_done).with(queue, subject)
|
|
74
132
|
|
|
75
|
-
expect_any_instance_of(
|
|
133
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
76
134
|
|
|
77
|
-
expect(
|
|
135
|
+
expect(sqs_queue).to receive(:batch_delete).with(sqs_msg)
|
|
78
136
|
|
|
79
137
|
subject.process(queue, sqs_msg)
|
|
80
138
|
end
|
|
81
139
|
|
|
82
140
|
it 'performs without delete' do
|
|
83
|
-
|
|
141
|
+
TestWorker.get_shoryuken_options['delete'] = false
|
|
84
142
|
|
|
85
143
|
expect(manager).to receive(:processor_done).with(queue, subject)
|
|
86
144
|
|
|
87
|
-
expect_any_instance_of(
|
|
145
|
+
expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
|
|
88
146
|
|
|
89
|
-
expect(
|
|
147
|
+
expect(sqs_queue).to_not receive(:batch_delete)
|
|
90
148
|
|
|
91
149
|
subject.process(queue, sqs_msg)
|
|
92
150
|
end
|
|
@@ -1,28 +1,42 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe 'Shoryuken::Worker' do
|
|
4
|
+
let(:sqs_queue) { double 'SQS Queue' }
|
|
5
|
+
let(:queue) { 'default' }
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
include Shoryuken::Worker
|
|
7
|
+
before do
|
|
8
|
+
allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
|
|
9
|
+
end
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
describe '.perform_async' do
|
|
12
|
+
it 'enqueues a message' do
|
|
13
|
+
expect(sqs_queue).to receive(:send_message).with('message', {})
|
|
14
|
+
|
|
15
|
+
TestWorker.perform_async('message')
|
|
16
|
+
end
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
it 'enqueues a message with options' do
|
|
19
|
+
expect(sqs_queue).to receive(:send_message).with('delayed message', delay_seconds: 60)
|
|
20
|
+
|
|
21
|
+
TestWorker.perform_async('delayed message', delay_seconds: 60)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '.shoryuken_options' do
|
|
26
|
+
it 'registers a worker' do
|
|
27
|
+
expect(Shoryuken.workers['default']).to eq TestWorker
|
|
14
28
|
end
|
|
15
29
|
|
|
16
30
|
it 'accepts a block as queue name' do
|
|
17
31
|
$queue_prefix = 'production'
|
|
18
32
|
|
|
19
|
-
class
|
|
33
|
+
class NewTestWorker
|
|
20
34
|
include Shoryuken::Worker
|
|
21
35
|
|
|
22
|
-
shoryuken_options queue: ->{ "#{$queue_prefix}
|
|
36
|
+
shoryuken_options queue: ->{ "#{$queue_prefix}_default" }
|
|
23
37
|
end
|
|
24
38
|
|
|
25
|
-
expect(Shoryuken.workers['
|
|
39
|
+
expect(Shoryuken.workers['production_default']).to eq NewTestWorker
|
|
26
40
|
end
|
|
27
41
|
end
|
|
28
42
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -2,8 +2,8 @@ require 'bundler/setup'
|
|
|
2
2
|
Bundler.setup
|
|
3
3
|
|
|
4
4
|
require 'pry-byebug'
|
|
5
|
-
require 'shoryuken'
|
|
6
5
|
require 'celluloid'
|
|
6
|
+
require 'shoryuken'
|
|
7
7
|
|
|
8
8
|
options_file = File.join(File.expand_path('../..', __FILE__), 'shoryuken.yml')
|
|
9
9
|
|
|
@@ -18,6 +18,14 @@ end
|
|
|
18
18
|
Shoryuken.logger.level = Logger::UNKNOWN
|
|
19
19
|
Celluloid.logger.level = Logger::UNKNOWN
|
|
20
20
|
|
|
21
|
+
class TestWorker
|
|
22
|
+
include Shoryuken::Worker
|
|
23
|
+
|
|
24
|
+
shoryuken_options queue: 'default'
|
|
25
|
+
|
|
26
|
+
def perform(sqs_msg, body); end
|
|
27
|
+
end
|
|
28
|
+
|
|
21
29
|
RSpec.configure do |config|
|
|
22
30
|
config.filter_run_excluding slow: true unless ENV['SPEC_ALL']
|
|
23
31
|
|
|
@@ -38,5 +46,8 @@ RSpec.configure do |config|
|
|
|
38
46
|
Shoryuken.options[:timeout] = 1
|
|
39
47
|
|
|
40
48
|
Shoryuken.options[:aws] = {}
|
|
49
|
+
|
|
50
|
+
TestWorker.get_shoryuken_options.clear
|
|
51
|
+
TestWorker.get_shoryuken_options['queue'] = 'default'
|
|
41
52
|
end
|
|
42
53
|
end
|
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: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pablo Cantero
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-10-
|
|
11
|
+
date: 2014-10-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -94,20 +94,6 @@ dependencies:
|
|
|
94
94
|
- - '>='
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: multi_json
|
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
|
100
|
-
requirements:
|
|
101
|
-
- - '>='
|
|
102
|
-
- !ruby/object:Gem::Version
|
|
103
|
-
version: '0'
|
|
104
|
-
type: :runtime
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - '>='
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0'
|
|
111
97
|
description: Shoryuken is a super efficient AWS SQS thread based message processor
|
|
112
98
|
email:
|
|
113
99
|
- pablo@pablocantero.com
|
|
@@ -125,9 +111,9 @@ files:
|
|
|
125
111
|
- Rakefile
|
|
126
112
|
- bin/shoryuken
|
|
127
113
|
- examples/all.rb
|
|
128
|
-
- examples/
|
|
129
|
-
- examples/
|
|
130
|
-
- examples/
|
|
114
|
+
- examples/default_worker.rb
|
|
115
|
+
- examples/high_priority_worker.rb
|
|
116
|
+
- examples/low_priority_worker.rb
|
|
131
117
|
- lib/shoryuken.rb
|
|
132
118
|
- lib/shoryuken/cli.rb
|
|
133
119
|
- lib/shoryuken/client.rb
|
|
@@ -137,8 +123,8 @@ files:
|
|
|
137
123
|
- lib/shoryuken/logging.rb
|
|
138
124
|
- lib/shoryuken/manager.rb
|
|
139
125
|
- lib/shoryuken/middleware/chain.rb
|
|
140
|
-
- lib/shoryuken/middleware/server/
|
|
141
|
-
- lib/shoryuken/middleware/server/
|
|
126
|
+
- lib/shoryuken/middleware/server/auto_delete.rb
|
|
127
|
+
- lib/shoryuken/middleware/server/timing.rb
|
|
142
128
|
- lib/shoryuken/processor.rb
|
|
143
129
|
- lib/shoryuken/util.rb
|
|
144
130
|
- lib/shoryuken/version.rb
|
|
@@ -151,7 +137,8 @@ files:
|
|
|
151
137
|
- spec/shoryuken/fetcher_spec.rb
|
|
152
138
|
- spec/shoryuken/manager_spec.rb
|
|
153
139
|
- spec/shoryuken/middleware/chain_spec.rb
|
|
154
|
-
- spec/shoryuken/middleware/server/
|
|
140
|
+
- spec/shoryuken/middleware/server/auto_delete_spec.rb
|
|
141
|
+
- spec/shoryuken/middleware/server/timing_spec.rb
|
|
155
142
|
- spec/shoryuken/processor_spec.rb
|
|
156
143
|
- spec/shoryuken/util_spec.rb
|
|
157
144
|
- spec/shoryuken/worker_spec.rb
|
|
@@ -187,7 +174,8 @@ test_files:
|
|
|
187
174
|
- spec/shoryuken/fetcher_spec.rb
|
|
188
175
|
- spec/shoryuken/manager_spec.rb
|
|
189
176
|
- spec/shoryuken/middleware/chain_spec.rb
|
|
190
|
-
- spec/shoryuken/middleware/server/
|
|
177
|
+
- spec/shoryuken/middleware/server/auto_delete_spec.rb
|
|
178
|
+
- spec/shoryuken/middleware/server/timing_spec.rb
|
|
191
179
|
- spec/shoryuken/processor_spec.rb
|
|
192
180
|
- spec/shoryuken/util_spec.rb
|
|
193
181
|
- spec/shoryuken/worker_spec.rb
|
data/examples/sidekiq_worker.rb
DELETED
data/examples/uppercut_worker.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Shoryuken
|
|
2
|
-
module Middleware
|
|
3
|
-
module Server
|
|
4
|
-
class Delete
|
|
5
|
-
def call(worker, queue, sqs_msg)
|
|
6
|
-
yield
|
|
7
|
-
|
|
8
|
-
# auto_delete is deprecated
|
|
9
|
-
delete = worker.class.get_shoryuken_options['delete'] || worker.class.get_shoryuken_options['auto_delete']
|
|
10
|
-
|
|
11
|
-
Array(sqs_msg).each(&:delete) if delete
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Shoryuken::Middleware::Server::Delete do
|
|
4
|
-
let(:sqs_msg) { double 'SQS msg' }
|
|
5
|
-
|
|
6
|
-
before do
|
|
7
|
-
DeleteWorker.get_shoryuken_options['delete'] = true
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
class DeleteWorker
|
|
11
|
-
include Shoryuken::Worker
|
|
12
|
-
|
|
13
|
-
shoryuken_options queue: 'delete', delete: true
|
|
14
|
-
|
|
15
|
-
def perform; end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'deletes a message' do
|
|
19
|
-
expect(sqs_msg).to receive(:delete)
|
|
20
|
-
|
|
21
|
-
subject.call(DeleteWorker.new, 'delete', sqs_msg) {}
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it 'deletes a batch' do
|
|
25
|
-
sqs_msg2 = double
|
|
26
|
-
sqs_msg3 = double
|
|
27
|
-
|
|
28
|
-
expect(sqs_msg).to receive(:delete)
|
|
29
|
-
expect(sqs_msg2).to receive(:delete)
|
|
30
|
-
expect(sqs_msg3).to receive(:delete)
|
|
31
|
-
|
|
32
|
-
subject.call(DeleteWorker.new, 'delete', [sqs_msg, sqs_msg2, sqs_msg3]) {}
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'does not delete a message' do
|
|
36
|
-
DeleteWorker.get_shoryuken_options['delete'] = false
|
|
37
|
-
|
|
38
|
-
expect(sqs_msg).to_not receive(:delete)
|
|
39
|
-
|
|
40
|
-
subject.call(DeleteWorker.new, 'delete', sqs_msg) {}
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
context 'when exception' do
|
|
44
|
-
it 'does not delete a message' do
|
|
45
|
-
expect(sqs_msg).to_not receive(:delete)
|
|
46
|
-
|
|
47
|
-
expect {
|
|
48
|
-
subject.call(DeleteWorker.new, 'delete', sqs_msg) { raise }
|
|
49
|
-
}.to raise_error
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|