shoryuken 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3afa1e42164a3fc885caf45222d317ac77692c3b
4
- data.tar.gz: 9abfa8e2c9ed9b4540bbb422fba35a3f00591364
3
+ metadata.gz: 70a39ae890a6fe9dcff44b0c6ceeab9c77931ab2
4
+ data.tar.gz: ff223f629ed2f60cc0abb32fa17f28e15d680d2f
5
5
  SHA512:
6
- metadata.gz: 35934726404b3d5454b1bc97875d3ba8934d2ef4efa4a354dbff97af95959c647757259ffc6fefc53a8ffaab5c1cfeffad73d79aff50a3575f5e65d39cbbb553
7
- data.tar.gz: 6cafc6fbfd06fff62541acc41169877440454eba4afb1688728bbd69330ed53fa9a62d1c28b2b9a057de853952cff26d537d013747a8cc5b6c47a8b637f3c1f2
6
+ metadata.gz: f0afdb29904c40200d1976f2b26973ae6694e7018b07bf133153c355e250a1d613cf1fd9c0500c2aaa612d77f4c5d631823ab186642b4a2051a76ea78feea4e6
7
+ data.tar.gz: 8b3d0dc3f255b3d921386a322e645e7875f2962c902094a48671d24a95211b02e5f0558be53e30619baf92551e9f3d7e23b60def11a2c11b84107525e339f141
data/README.md CHANGED
@@ -30,13 +30,12 @@ If `high_priority` gets empty, Shoryuken will keep using the 25 processors, but
30
30
 
31
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 receives a new message, Shoryuken will start processing again.
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. [Check the delay option documentation for more information](https://github.com/phstc/shoryuken/wiki/Shoryuken-options#delay).
34
34
 
35
- *You can set `delay: 0` to continuously check the queues without pausing even if they are empty.*
36
35
 
37
36
  ### Fetch in batches
38
37
 
39
- To be even more performance and cost efficient, Shoryuken fetches SQS messages in batches.
38
+ To be even more performance and cost efficient, Shoryuken fetches SQS messages in batches, so a single SQS request can fetch up to 10 messages.
40
39
 
41
40
  ## Installation
42
41
 
@@ -44,7 +43,7 @@ Add this line to your application's Gemfile:
44
43
 
45
44
  gem 'shoryuken'
46
45
 
47
- **Require Shoryuken from GitHub to get the latest updates:**
46
+ Or to get the latest updates:
48
47
 
49
48
  gem 'shoryuken', github: 'phstc/shoryuken', branch: 'master'
50
49
 
@@ -64,7 +63,7 @@ Or install it yourself as:
64
63
  class MyWorker
65
64
  include Shoryuken::Worker
66
65
 
67
- shoryuken_options queue: 'default', delete: true
66
+ shoryuken_options queue: 'default', auto_delete: true
68
67
  # shoryuken_options queue: ->{ "#{ENV['environment']_default" }
69
68
 
70
69
  # shoryuken_options body_parser: :json
@@ -81,16 +80,7 @@ end
81
80
 
82
81
  ### Sending a message
83
82
 
84
- ```ruby
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
- ```
83
+ [Check the Sending a message documentation](https://github.com/phstc/shoryuken/wiki/Sending-a-message)
94
84
 
95
85
  ### Midleware
96
86
 
@@ -128,6 +118,14 @@ queues:
128
118
  - [low_priority, 1]
129
119
  ```
130
120
 
121
+ ### Rails Integration
122
+
123
+ You can tell Shoryuken to load your Rails application by passing the `-R` or `--rails` flag to the "shoryuken" command.
124
+
125
+ If you load Rails, and assuming your workers are located in the `app/workers` directory, they will be auto-loaded. This means you don't need to require them explicitly with `-r`.
126
+
127
+ This feature works for Rails 4+, but needs to be confirmed for older versions.
128
+
131
129
  ### Start Shoryuken
132
130
 
133
131
  ```shell
@@ -145,6 +143,7 @@ shoryuken [options]
145
143
  -q, --queue QUEUE[,WEIGHT]... Queues to process with optional weights
146
144
  -r, --require [PATH|DIR] Location of the worker
147
145
  -C, --config PATH Path to YAML config file
146
+ -R, --rails Attempts to load the containing Rails project
148
147
  -L, --logfile PATH Path to writable logfile
149
148
  -P, --pidfile PATH Path to pidfile
150
149
  -v, --verbose Print more verbose output
data/Rakefile CHANGED
@@ -21,27 +21,3 @@ task :console do
21
21
  ARGV.clear
22
22
  Pry.start
23
23
  end
24
-
25
- desc 'Push test messages to high_priority, default and low_priority'
26
- task :push_test, :size do |t, args|
27
- require 'yaml'
28
- require 'shoryuken'
29
-
30
- config = YAML.load File.read(File.join(File.expand_path('..', __FILE__), 'shoryuken.yml'))
31
-
32
- AWS.config(config['aws'])
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
-
38
- (args[:size] || 1).to_i.times.map do |i|
39
- Thread.new do
40
- puts "Pushing test ##{i}"
41
-
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}")
45
- end
46
- end.each &:join
47
- end
@@ -0,0 +1,20 @@
1
+ require 'yaml'
2
+ require 'shoryuken'
3
+
4
+ # load SQS credentials
5
+ config = YAML.load File.read(File.join(File.expand_path('..', __FILE__), 'shoryuken.yml'))
6
+
7
+ AWS.config(config['aws'])
8
+
9
+ sqs = AWS::SQS.new
10
+
11
+ # create a queue and a respective dead letter queue
12
+ # after 7 attempts SQS will move the message to the dead letter queue
13
+
14
+ dl_name = 'default_failures'
15
+ dl = sqs.queues.create(dl_name)
16
+
17
+ options = {}
18
+ options[:redrive_policy] = %Q{{"maxReceiveCount":"7", "deadLetterTargetArn":"#{dl.arn}"}"}
19
+
20
+ sqs.queues.create('default', options)
@@ -1,9 +1,20 @@
1
1
  class DefaultWorker
2
2
  include Shoryuken::Worker
3
3
 
4
- shoryuken_options queue: 'default', delete: true, body_parser: ->(sqs_msg){ "new body: #{sqs_msg.body}" }
4
+ shoryuken_options queue: 'default', auto_delete: true
5
5
 
6
6
  def perform(sqs_msg, body)
7
7
  puts "DefaultWorker: '#{body}'"
8
8
  end
9
9
  end
10
+
11
+ # multiple workers for the same queue
12
+ class DefaultWorker2
13
+ include Shoryuken::Worker
14
+
15
+ shoryuken_options queue: 'default', auto_delete: true
16
+
17
+ def perform(sqs_msg, body)
18
+ puts "DefaultWorker2: '#{body}'"
19
+ end
20
+ end
data/lib/shoryuken.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'yaml'
2
- require 'aws-sdk'
2
+ require 'aws-sdk-v1'
3
3
  require 'time'
4
4
 
5
5
  require 'shoryuken/version'
@@ -7,6 +7,7 @@ require 'shoryuken/core_ext'
7
7
  require 'shoryuken/util'
8
8
  require 'shoryuken/client'
9
9
  require 'shoryuken/worker'
10
+ require 'shoryuken/worker_loader'
10
11
  require 'shoryuken/logging'
11
12
  require 'shoryuken/middleware/chain'
12
13
  require 'shoryuken/middleware/server/auto_delete'
@@ -21,52 +22,75 @@ module Shoryuken
21
22
  timeout: 8
22
23
  }
23
24
 
24
- @@workers = {}
25
- @@queues = []
25
+ @@workers = {}
26
+ @@queues = []
27
+ @@worker_loader = WorkerLoader
26
28
 
27
- def self.options
28
- @options ||= DEFAULTS.dup
29
- end
29
+ class << self
30
+ def options
31
+ @options ||= DEFAULTS.dup
32
+ end
30
33
 
31
- def self.register_worker(queue, clazz)
32
- @@workers[queue] = clazz
33
- end
34
+ def register_worker(queue, clazz)
35
+ if worker_class = @@workers[queue]
36
+ if worker_class.get_shoryuken_options['batch'] == true || clazz.get_shoryuken_options['batch'] == true
37
+ raise ArgumentError, "Could not register #{clazz} for '#{queue}', "\
38
+ "because #{worker_class} is already registered for this queue, "\
39
+ "and Shoryuken doesn't support a batchable worker for a queue with multiple workers"
40
+ end
41
+ end
34
42
 
35
- def self.workers
36
- @@workers
37
- end
43
+ @@workers[queue] = clazz
44
+ end
38
45
 
39
- def self.queues
40
- @@queues
41
- end
46
+ def workers
47
+ @@workers
48
+ end
42
49
 
43
- def self.logger
44
- Shoryuken::Logging.logger
45
- end
50
+ def queues
51
+ @@queues
52
+ end
46
53
 
47
- # Shoryuken.configure_server do |config|
48
- # config.server_middleware do |chain|
49
- # chain.add MyServerHook
50
- # end
51
- # end
52
- def self.configure_server
53
- yield self
54
- end
54
+ def logger
55
+ Shoryuken::Logging.logger
56
+ end
55
57
 
56
- def self.server_middleware
57
- @server_chain ||= default_server_middleware
58
- yield @server_chain if block_given?
59
- @server_chain
60
- end
58
+ def worker_loader=(worker_loader)
59
+ @@worker_loader = worker_loader
60
+ end
61
+
62
+ def worker_loader
63
+ @@worker_loader
64
+ end
65
+
66
+ # Shoryuken.configure_server do |config|
67
+ # config.server_middleware do |chain|
68
+ # chain.add MyServerHook
69
+ # end
70
+ # end
71
+ def configure_server
72
+ yield self
73
+ end
74
+
75
+ def server_middleware
76
+ @server_chain ||= default_server_middleware
77
+ yield @server_chain if block_given?
78
+ @server_chain
79
+ end
61
80
 
62
81
 
63
- private
82
+ private
64
83
 
65
- def self.default_server_middleware
66
- Middleware::Chain.new do |m|
67
- m.add Middleware::Server::Timing
68
- m.add Middleware::Server::AutoDelete
69
- # TODO m.add Middleware::Server::RetryJobs
84
+ def default_server_middleware
85
+ Middleware::Chain.new do |m|
86
+ m.add Middleware::Server::Timing
87
+ m.add Middleware::Server::AutoDelete
88
+ if defined?(::ActiveRecord::Base)
89
+ require 'shoryuken/middleware/server/active_record'
90
+ m.add Middleware::Server::ActiveRecord
91
+ end
92
+ # TODO m.add Middleware::Server::RetryJobs
93
+ end
70
94
  end
71
95
  end
72
96
  end
data/lib/shoryuken/cli.rb CHANGED
@@ -22,7 +22,10 @@ module Shoryuken
22
22
  end
23
23
  end
24
24
 
25
- setup_options(args)
25
+ setup_options(args) do |cli_options|
26
+ # this needs to happen before configuration is parsed, since it may depend on Rails env
27
+ load_rails if cli_options[:rails]
28
+ end
26
29
  initialize_logger
27
30
  require_workers
28
31
  validate!
@@ -61,6 +64,25 @@ module Shoryuken
61
64
  require 'shoryuken/manager'
62
65
  end
63
66
 
67
+ def load_rails
68
+ # Adapted from: https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/cli.rb
69
+
70
+ require 'rails'
71
+ if ::Rails::VERSION::MAJOR < 4
72
+ require File.expand_path("config/environment.rb")
73
+ ::Rails.application.eager_load!
74
+ else
75
+ # Painful contortions, see 1791 for discussion
76
+ require File.expand_path("config/application.rb")
77
+ ::Rails::Application.initializer "shoryuken.eager_load" do
78
+ ::Rails.application.config.eager_load = true
79
+ end
80
+ require File.expand_path("config/environment.rb")
81
+ end
82
+
83
+ logger.info "Rails environment loaded"
84
+ end
85
+
64
86
  def daemonize
65
87
  return unless Shoryuken.options[:daemon]
66
88
 
@@ -125,6 +147,10 @@ module Shoryuken
125
147
  opts[:config_file] = arg
126
148
  end
127
149
 
150
+ o.on '-R', '--rails', 'Load Rails' do |arg|
151
+ opts[:rails] = arg
152
+ end
153
+
128
154
  o.on '-L', '--logfile PATH', 'Path to writable logfile' do |arg|
129
155
  opts[:logfile] = arg
130
156
  end
@@ -187,6 +213,9 @@ module Shoryuken
187
213
  def setup_options(args)
188
214
  options = parse_options(args)
189
215
 
216
+ # yield parsed options in case we need to do more setup before configuration is parsed
217
+ yield(options) if block_given?
218
+
190
219
  config = options[:config_file] ? parse_config(options[:config_file]).deep_symbolize_keys : {}
191
220
 
192
221
  Shoryuken.options.merge!(config)
@@ -213,8 +242,8 @@ module Shoryuken
213
242
  def validate!
214
243
  raise ArgumentError, 'No queues supplied' if Shoryuken.queues.empty?
215
244
 
216
- if queue_without_worker = Shoryuken.queues.find { |queue| Shoryuken.workers[queue].nil? }
217
- raise ArgumentError, "No worker supplied for #{queue_without_worker}"
245
+ Shoryuken.queues.each do |queue|
246
+ logger.warn "No worker supplied for '#{queue}'" unless Shoryuken.workers.include? queue
218
247
  end
219
248
 
220
249
  if Shoryuken.options[:aws][:access_key_id].nil? && Shoryuken.options[:aws][:secret_access_key].nil?
@@ -17,6 +17,8 @@ module Shoryuken
17
17
  end
18
18
 
19
19
  def send_message(queue, body, options = {})
20
+ body = JSON.dump(body) if body.is_a?(Hash)
21
+
20
22
  queues(queue).send_message(body, options)
21
23
  end
22
24
 
@@ -13,17 +13,22 @@ module Shoryuken
13
13
  # AWS limits the batch size by 10
14
14
  limit = limit > FETCH_LIMIT ? FETCH_LIMIT : limit
15
15
 
16
- Shoryuken::Client.receive_message queue, Shoryuken.options[:aws][:receive_message].to_h.merge(limit: limit)
16
+ options = Shoryuken.options[:aws][:receive_message].to_h
17
+ options[:limit] = limit
18
+ options[:message_attribute_names] ||= []
19
+ options[:message_attribute_names] << 'shoryuken_class'
20
+
21
+ Shoryuken::Client.receive_message queue, options
17
22
  end
18
23
 
19
24
  def fetch(queue, available_processors)
20
25
  watchdog('Fetcher#fetch died') do
21
26
  started_at = Time.now
22
27
 
23
- logger.info "Looking for new messages '#{queue}'"
28
+ logger.debug "Looking for new messages in '#{queue}'"
24
29
 
25
30
  begin
26
- batch = !!Shoryuken.workers[queue].get_shoryuken_options['batch']
31
+ batch = !!(Shoryuken.workers[queue] && Shoryuken.workers[queue].get_shoryuken_options['batch'])
27
32
 
28
33
  limit = batch ? FETCH_LIMIT : available_processors
29
34
 
@@ -38,7 +43,7 @@ module Shoryuken
38
43
 
39
44
  @manager.async.rebalance_queue_weight!(queue)
40
45
  else
41
- logger.info "No message found for '#{queue}'"
46
+ logger.debug "No message found for '#{queue}'"
42
47
 
43
48
  @manager.async.pause_queue!(queue)
44
49
  end
@@ -103,7 +103,7 @@ module Shoryuken
103
103
  def pause_queue!(queue)
104
104
  return if !@queues.include?(queue) || Shoryuken.options[:delay].to_f <= 0
105
105
 
106
- logger.info "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because it's empty"
106
+ logger.debug "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because it's empty"
107
107
 
108
108
  @queues.delete(queue)
109
109
 
@@ -139,7 +139,7 @@ module Shoryuken
139
139
  return if stopped?
140
140
 
141
141
  unless @queues.include? queue
142
- logger.info "Restarting '#{queue}'"
142
+ logger.debug "Restarting '#{queue}'"
143
143
 
144
144
  @queues << queue
145
145
 
@@ -169,7 +169,21 @@ module Shoryuken
169
169
  def next_queue
170
170
  return nil if @queues.empty?
171
171
 
172
+ # get/remove the first queue in the list
172
173
  queue = @queues.shift
174
+
175
+
176
+ unless Shoryuken.workers.include? queue
177
+ # when no worker registered pause the queue to avoid endless recursion
178
+
179
+ logger.debug "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because of no workers registered"
180
+
181
+ after(Shoryuken.options[:delay].to_f) { async.restart_queue!(queue) }
182
+
183
+ return next_queue
184
+ end
185
+
186
+ # add queue back to the end of the list
173
187
  @queues << queue
174
188
 
175
189
  queue
@@ -0,0 +1,13 @@
1
+ module Shoryuken
2
+ module Middleware
3
+ module Server
4
+ class ActiveRecord
5
+ def call(*args)
6
+ yield
7
+ ensure
8
+ ::ActiveRecord::Base.clear_active_connections!
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,14 +2,12 @@ module Shoryuken
2
2
  module Middleware
3
3
  module Server
4
4
  class AutoDelete
5
-
6
5
  def call(worker, queue, sqs_msg, body)
7
6
  yield
8
7
 
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']
8
+ auto_delete = worker.class.get_shoryuken_options['delete'] || worker.class.get_shoryuken_options['auto_delete']
11
9
 
12
- Shoryuken::Client.queues(queue).batch_delete(*Array(sqs_msg)) if delete
10
+ Shoryuken::Client.queues(queue).batch_delete(*Array(sqs_msg)) if auto_delete
13
11
  end
14
12
  end
15
13
  end
@@ -10,11 +10,10 @@ module Shoryuken
10
10
  end
11
11
 
12
12
  def process(queue, sqs_msg)
13
- worker_class = Shoryuken.workers[queue]
14
- defer do
15
- body = get_body(worker_class, sqs_msg)
13
+ worker = Shoryuken.worker_loader.call(queue, sqs_msg)
16
14
 
17
- worker = worker_class.new
15
+ defer do
16
+ body = get_body(worker.class, sqs_msg)
18
17
 
19
18
  Shoryuken.server_middleware.invoke(worker, queue, sqs_msg, body) do
20
19
  worker.perform(sqs_msg, body)
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
@@ -6,19 +6,43 @@ module Shoryuken
6
6
 
7
7
  module ClassMethods
8
8
  def perform_async(body, options = {})
9
+ options ||= {}
10
+ options[:message_attributes] ||= {}
11
+ options[:message_attributes]['shoryuken_class'] = {
12
+ string_value: self.to_s,
13
+ data_type: 'String'
14
+ }
15
+
9
16
  Shoryuken::Client.send_message(get_shoryuken_options['queue'], body, options)
10
17
  end
11
18
 
19
+ def perform_in(interval, body, options = {})
20
+ interval = interval.to_f
21
+ now = Time.now.to_f
22
+ ts = (interval < 1_000_000_000 ? (now + interval).to_f : interval)
23
+
24
+ delay = (ts - now).ceil
25
+
26
+ raise 'The maximum allowed delay is 15 minutes' if delay > 15 * 60
27
+
28
+ perform_async(body, options.merge(delay_seconds: delay))
29
+ end
30
+
31
+ alias_method :perform_at, :perform_in
32
+
12
33
  def shoryuken_options(opts = {})
13
34
  @shoryuken_options = get_shoryuken_options.merge(stringify_keys(Hash(opts)))
14
35
  queue = @shoryuken_options['queue']
15
- queue = queue.call if queue.respond_to? :call
36
+ if queue.respond_to? :call
37
+ queue = queue.call
38
+ @shoryuken_options['queue'] = queue
39
+ end
16
40
 
17
41
  Shoryuken.register_worker(queue, self)
18
42
  end
19
43
 
20
44
  def get_shoryuken_options # :nodoc:
21
- @shoryuken_options || { 'queue' => 'default', 'delete' => false, 'batch' => false }
45
+ @shoryuken_options || { 'queue' => 'default', 'delete' => false, 'auto_delete' => false, 'batch' => false }
22
46
  end
23
47
 
24
48
  def stringify_keys(hash) # :nodoc:
@@ -0,0 +1,17 @@
1
+ module Shoryuken
2
+ class WorkerLoader
3
+ class << self
4
+ def call(queue, sqs_msg)
5
+ # missing `try?` - yes, I'm
6
+ worker_class = !sqs_msg.is_a?(Array) &&
7
+ sqs_msg.message_attributes &&
8
+ sqs_msg.message_attributes['shoryuken_class'] &&
9
+ sqs_msg.message_attributes['shoryuken_class'][:string_value]
10
+
11
+ worker_class = (worker_class.constantize rescue nil) || Shoryuken.workers[queue]
12
+
13
+ worker_class.new
14
+ end
15
+ end
16
+ end
17
+ end
data/shoryuken.gemspec CHANGED
@@ -22,6 +22,6 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "rspec"
23
23
  spec.add_development_dependency "pry-byebug"
24
24
 
25
- spec.add_dependency "aws-sdk"
26
- spec.add_dependency "celluloid"
25
+ spec.add_dependency "aws-sdk-v1"
26
+ spec.add_dependency "celluloid", "~> 0.15.2"
27
27
  end
@@ -9,19 +9,45 @@ describe Shoryuken::Launcher do
9
9
  Shoryuken.options[:aws][:receive_message] ||= {}
10
10
  Shoryuken.options[:aws][:receive_message][:wait_time_seconds] = 5
11
11
 
12
+ Shoryuken.queues << 'shoryuken'
13
+ Shoryuken.queues << 'shoryuken_command'
14
+
15
+ Shoryuken.register_worker 'shoryuken', StandardWorker
16
+ Shoryuken.register_worker 'shoryuken_command', CommandWorker
17
+
12
18
  subject.run
13
19
 
14
- ShoryukenWorker.received_messages = 0
20
+ StandardWorker.received_messages = 0
15
21
  end
16
22
 
17
23
  after { subject.stop }
18
24
 
19
- class ShoryukenWorker
25
+ class CommandWorker
26
+ include Shoryuken::Worker
27
+
28
+ @@received_messages = 0
29
+
30
+ shoryuken_options queue: 'shoryuken_command', auto_delete: true
31
+
32
+ def perform(sqs_msg, body)
33
+ @@received_messages = Array(sqs_msg).size
34
+ end
35
+
36
+ def self.received_messages
37
+ @@received_messages
38
+ end
39
+
40
+ def self.received_messages=(received_messages)
41
+ @@received_messages = received_messages
42
+ end
43
+ end
44
+
45
+ class StandardWorker
20
46
  include Shoryuken::Worker
21
47
 
22
48
  @@received_messages = 0
23
49
 
24
- shoryuken_options queue: 'shoryuken', delete: true
50
+ shoryuken_options queue: 'shoryuken', auto_delete: true
25
51
 
26
52
  def perform(sqs_msg, body)
27
53
  @@received_messages = Array(sqs_msg).size
@@ -36,31 +62,42 @@ describe Shoryuken::Launcher do
36
62
  end
37
63
  end
38
64
 
65
+ it 'consumes as a command worker' do
66
+ CommandWorker.perform_async('Yo')
67
+
68
+ 10.times do
69
+ break if CommandWorker.received_messages > 0
70
+ sleep 1
71
+ end
72
+
73
+ expect(CommandWorker.received_messages).to eq 1
74
+ end
75
+
39
76
  it 'consumes a message' do
40
- ShoryukenWorker.get_shoryuken_options['batch'] = false
77
+ StandardWorker.get_shoryuken_options['batch'] = false
41
78
 
42
79
  Shoryuken::Client.queues('shoryuken').send_message('Yo')
43
80
 
44
81
  10.times do
45
- break if ShoryukenWorker.received_messages > 0
46
- sleep 0.2
82
+ break if StandardWorker.received_messages > 0
83
+ sleep 1
47
84
  end
48
85
 
49
- expect(ShoryukenWorker.received_messages).to eq 1
86
+ expect(StandardWorker.received_messages).to eq 1
50
87
  end
51
88
 
52
89
  it 'consumes a batch' do
53
- ShoryukenWorker.get_shoryuken_options['batch'] = true
90
+ StandardWorker.get_shoryuken_options['batch'] = true
54
91
 
55
92
  Shoryuken::Client.queues('shoryuken').batch_send *(['Yo'] * 10)
56
93
 
57
94
  10.times do
58
- break if ShoryukenWorker.received_messages > 0
59
- sleep 0.2
95
+ break if StandardWorker.received_messages > 0
96
+ sleep 1
60
97
  end
61
98
 
62
99
  # the fetch result is uncertain, should be greater than 1, but hard to tell the exact size
63
- expect(ShoryukenWorker.received_messages).to be > 1
100
+ expect(StandardWorker.received_messages).to be > 1
64
101
  end
65
102
  end
66
103
  end
@@ -33,6 +33,22 @@ describe Shoryuken::Client do
33
33
 
34
34
  described_class.send_message(queue, 'test2', delay_seconds: 60)
35
35
  end
36
+
37
+ it 'parsers as JSON by default' do
38
+ msg = { field: 'test', other_field: 'other' }
39
+
40
+ expect(sqs_queue).to receive(:send_message).with(JSON.dump(msg), {})
41
+
42
+ described_class.send_message(queue, msg)
43
+ end
44
+
45
+ it 'parsers as JSON by default and keep the options' do
46
+ msg = { field: 'test', other_field: 'other' }
47
+
48
+ expect(sqs_queue).to receive(:send_message).with(JSON.dump(msg), { delay_seconds: 60 })
49
+
50
+ described_class.send_message(queue, msg, delay_seconds: 60)
51
+ end
36
52
  end
37
53
 
38
54
  describe '.visibility_timeout' do
@@ -18,7 +18,7 @@ describe Shoryuken::Fetcher do
18
18
 
19
19
  describe '#fetch' do
20
20
  it 'calls pause when no message' do
21
- allow(sqs_queue).to receive(:receive_message).with(limit: 1).and_return([])
21
+ allow(sqs_queue).to receive(:receive_message).with(limit: 1, message_attribute_names: ['shoryuken_class']).and_return([])
22
22
 
23
23
  expect(manager).to receive(:pause_queue!).with(queue)
24
24
  expect(manager).to receive(:dispatch)
@@ -27,7 +27,7 @@ describe Shoryuken::Fetcher do
27
27
  end
28
28
 
29
29
  it 'assigns messages' do
30
- allow(sqs_queue).to receive(:receive_message).with(limit: 5).and_return(sqs_msg)
30
+ allow(sqs_queue).to receive(:receive_message).with(limit: 5, message_attribute_names: ['shoryuken_class']).and_return(sqs_msg)
31
31
 
32
32
  expect(manager).to receive(:rebalance_queue_weight!).with(queue)
33
33
  expect(manager).to receive(:assign).with(queue, sqs_msg)
@@ -39,7 +39,7 @@ describe Shoryuken::Fetcher do
39
39
  it 'assigns messages in batch' do
40
40
  TestWorker.get_shoryuken_options['batch'] = true
41
41
 
42
- allow(sqs_queue).to receive(:receive_message).with(limit: described_class::FETCH_LIMIT).and_return(sqs_msg)
42
+ allow(sqs_queue).to receive(:receive_message).with(limit: described_class::FETCH_LIMIT, message_attribute_names: ['shoryuken_class']).and_return(sqs_msg)
43
43
 
44
44
  expect(manager).to receive(:rebalance_queue_weight!).with(queue)
45
45
  expect(manager).to receive(:assign).with(queue, [sqs_msg])
@@ -47,5 +47,19 @@ describe Shoryuken::Fetcher do
47
47
 
48
48
  subject.fetch(queue, 5)
49
49
  end
50
+
51
+ context 'when worker not found' do
52
+ let(:queue) { 'notfound' }
53
+
54
+ it 'ignores batch' do
55
+ allow(sqs_queue).to receive(:receive_message).with(limit: 5, message_attribute_names: ['shoryuken_class']).and_return(sqs_msg)
56
+
57
+ expect(manager).to receive(:rebalance_queue_weight!).with(queue)
58
+ expect(manager).to receive(:assign).with(queue, sqs_msg)
59
+ expect(manager).to receive(:dispatch)
60
+
61
+ subject.fetch(queue, 5)
62
+ end
63
+ end
50
64
  end
51
65
  end
@@ -71,4 +71,37 @@ describe Shoryuken::Manager do
71
71
  expect(subject.instance_variable_get('@queues')).to eq [queue2, queue1]
72
72
  end
73
73
  end
74
+
75
+ describe '#next_queue' do
76
+ it 'returns queues' do
77
+ queue1 = 'shoryuken'
78
+ queue2 = 'uppercut'
79
+
80
+ Shoryuken.queues.clear
81
+
82
+ Shoryuken.register_worker queue1, TestWorker
83
+ Shoryuken.register_worker queue2, TestWorker
84
+
85
+ Shoryuken.queues << queue1
86
+ Shoryuken.queues << queue2
87
+
88
+ expect(subject.send :next_queue).to eq queue1
89
+ expect(subject.send :next_queue).to eq queue2
90
+ end
91
+
92
+ it 'skips when no worker' do
93
+ queue1 = 'shoryuken'
94
+ queue2 = 'uppercut'
95
+
96
+ Shoryuken.queues.clear
97
+
98
+ Shoryuken.register_worker queue2, TestWorker
99
+
100
+ Shoryuken.queues << queue1
101
+ Shoryuken.queues << queue2
102
+
103
+ expect(subject.send :next_queue).to eq queue2
104
+ expect(subject.send :next_queue).to eq queue2
105
+ end
106
+ end
74
107
  end
@@ -10,7 +10,7 @@ describe Shoryuken::Middleware::Server::AutoDelete do
10
10
  end
11
11
 
12
12
  it 'deletes a message' do
13
- TestWorker.get_shoryuken_options['delete'] = true
13
+ TestWorker.get_shoryuken_options['auto_delete'] = true
14
14
 
15
15
  expect(sqs_queue).to receive(:batch_delete).with(sqs_msg)
16
16
 
@@ -18,7 +18,7 @@ describe Shoryuken::Middleware::Server::AutoDelete do
18
18
  end
19
19
 
20
20
  it 'deletes a batch' do
21
- TestWorker.get_shoryuken_options['delete'] = true
21
+ TestWorker.get_shoryuken_options['auto_delete'] = true
22
22
 
23
23
  sqs_msg2 = double 'SQS msg', body: 'test'
24
24
  sqs_msg3 = double 'SQS msg', body: 'test'
@@ -31,7 +31,7 @@ describe Shoryuken::Middleware::Server::AutoDelete do
31
31
  end
32
32
 
33
33
  it 'does not delete a message' do
34
- TestWorker.get_shoryuken_options['delete'] = false
34
+ TestWorker.get_shoryuken_options['auto_delete'] = false
35
35
 
36
36
  expect(sqs_queue).to_not receive(:batch_delete)
37
37
 
@@ -6,7 +6,7 @@ describe Shoryuken::Processor do
6
6
  let(:manager) { double Shoryuken::Manager, processor_done: nil }
7
7
  let(:sqs_queue) { double AWS::SQS::Queue, visibility_timeout: 30 }
8
8
  let(:queue) { 'default' }
9
- let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test' }
9
+ let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test', message_attributes: {} }
10
10
 
11
11
  subject { described_class.new(manager) }
12
12
 
@@ -16,7 +16,7 @@ describe Shoryuken::Processor do
16
16
  end
17
17
 
18
18
  describe '#process' do
19
- it 'parses the body into JSON' do
19
+ it 'parsers the body into JSON' do
20
20
  TestWorker.get_shoryuken_options['body_parser'] = :json
21
21
 
22
22
  body = { 'test' => 'hi' }
@@ -28,7 +28,7 @@ describe Shoryuken::Processor do
28
28
  subject.process(queue, sqs_msg)
29
29
  end
30
30
 
31
- it 'parses the body calling the proc' do
31
+ it 'parsers the body calling the proc' do
32
32
  TestWorker.get_shoryuken_options['body_parser'] = Proc.new { |sqs_msg| "*#{sqs_msg.body}*" }
33
33
 
34
34
  expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, '*test*')
@@ -38,7 +38,7 @@ describe Shoryuken::Processor do
38
38
  subject.process(queue, sqs_msg)
39
39
  end
40
40
 
41
- it 'parses the body as text' do
41
+ it 'parsers the body as text' do
42
42
  TestWorker.get_shoryuken_options['body_parser'] = :text
43
43
 
44
44
  body = 'test'
@@ -50,7 +50,7 @@ describe Shoryuken::Processor do
50
50
  subject.process(queue, sqs_msg)
51
51
  end
52
52
 
53
- it 'parses calling `.parse`' do
53
+ it 'parsers calling `.parse`' do
54
54
  TestWorker.get_shoryuken_options['body_parser'] = JSON
55
55
 
56
56
  body = { 'test' => 'hi' }
@@ -77,7 +77,7 @@ describe Shoryuken::Processor do
77
77
  end
78
78
 
79
79
  context 'when `object_type: nil`' do
80
- it 'parses the body as text' do
80
+ it 'parsers the body as text' do
81
81
  TestWorker.get_shoryuken_options['body_parser'] = nil
82
82
 
83
83
  body = 'test'
@@ -126,7 +126,7 @@ describe Shoryuken::Processor do
126
126
  end
127
127
 
128
128
  it 'performs with delete' do
129
- TestWorker.get_shoryuken_options['delete'] = true
129
+ TestWorker.get_shoryuken_options['auto_delete'] = true
130
130
 
131
131
  expect(manager).to receive(:processor_done).with(queue, subject)
132
132
 
@@ -138,7 +138,7 @@ describe Shoryuken::Processor do
138
138
  end
139
139
 
140
140
  it 'performs without delete' do
141
- TestWorker.get_shoryuken_options['delete'] = false
141
+ TestWorker.get_shoryuken_options['auto_delete'] = false
142
142
 
143
143
  expect(manager).to receive(:processor_done).with(queue, subject)
144
144
 
@@ -148,5 +148,26 @@ describe Shoryuken::Processor do
148
148
 
149
149
  subject.process(queue, sqs_msg)
150
150
  end
151
+
152
+ context 'when shoryuken_class header' do
153
+ let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test', message_attributes: {
154
+ 'shoryuken_class' => {
155
+ string_value: TestWorker.to_s,
156
+ data_type: 'String'
157
+ }
158
+ } }
159
+
160
+ it 'performs without delete' do
161
+ Shoryuken.workers.clear # unregister TestWorker
162
+
163
+ expect(manager).to receive(:processor_done).with(queue, subject)
164
+
165
+ expect_any_instance_of(TestWorker).to receive(:perform).with(sqs_msg, sqs_msg.body)
166
+
167
+ expect(sqs_queue).to_not receive(:batch_delete)
168
+
169
+ subject.process(queue, sqs_msg)
170
+ end
171
+ end
151
172
  end
152
173
  end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoryuken::WorkerLoader do
4
+ let(:queue) { 'default' }
5
+ let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test', message_attributes: { } }
6
+
7
+ describe '.call' do
8
+ it 'returns the worker using `Shoryuken.workers`' do
9
+ expect(described_class.call(queue, sqs_msg)).to be_an_instance_of TestWorker
10
+ end
11
+
12
+ context 'when `message_attributes`' do
13
+ let(:sqs_msg) { double AWS::SQS::ReceivedMessage, id: 'fc754df7-9cc2-4c41-96ca-5996a44b771e', body: 'test', message_attributes: {
14
+ 'shoryuken_class' => {
15
+ string_value: TestWorker.to_s,
16
+ data_type: 'String'
17
+ }
18
+ } }
19
+
20
+ it 'returns the worker using `message_attributes`' do
21
+ Shoryuken.workers.clear
22
+
23
+ expect(described_class.call(queue, sqs_msg)).to be_an_instance_of TestWorker
24
+ end
25
+ end
26
+ end
27
+ end
@@ -8,15 +8,74 @@ describe 'Shoryuken::Worker' do
8
8
  allow(Shoryuken::Client).to receive(:queues).with(queue).and_return(sqs_queue)
9
9
  end
10
10
 
11
+ describe '.perform_in' do
12
+ it 'delays a message' do
13
+ expect(sqs_queue).to receive(:send_message).with('message', {
14
+ message_attributes: {
15
+ 'shoryuken_class' => {
16
+ string_value: TestWorker.to_s,
17
+ data_type: 'String'
18
+ }
19
+ },
20
+ delay_seconds: 60
21
+ })
22
+
23
+ TestWorker.perform_in(60, 'message')
24
+ end
25
+
26
+ it 'raises an exception' do
27
+ expect {
28
+ TestWorker.perform_in(901, 'message')
29
+ }.to raise_error 'The maximum allowed delay is 15 minutes'
30
+ end
31
+ end
32
+
33
+ describe '.perform_at' do
34
+ it 'delays a message' do
35
+ expect(sqs_queue).to receive(:send_message).with('message', {
36
+ message_attributes: {
37
+ 'shoryuken_class' => {
38
+ string_value: TestWorker.to_s,
39
+ data_type: 'String'
40
+ }
41
+ },
42
+ delay_seconds: 60
43
+ })
44
+
45
+ TestWorker.perform_in(Time.now + 60, 'message')
46
+ end
47
+
48
+ it 'raises an exception' do
49
+ expect {
50
+ TestWorker.perform_in(Time.now + 901, 'message')
51
+ }.to raise_error 'The maximum allowed delay is 15 minutes'
52
+ end
53
+ end
54
+
11
55
  describe '.perform_async' do
12
56
  it 'enqueues a message' do
13
- expect(sqs_queue).to receive(:send_message).with('message', {})
57
+ expect(sqs_queue).to receive(:send_message).with('message', {
58
+ message_attributes: {
59
+ 'shoryuken_class' => {
60
+ string_value: TestWorker.to_s,
61
+ data_type: 'String'
62
+ }
63
+ }
64
+ })
14
65
 
15
66
  TestWorker.perform_async('message')
16
67
  end
17
68
 
18
69
  it 'enqueues a message with options' do
19
- expect(sqs_queue).to receive(:send_message).with('delayed message', delay_seconds: 60)
70
+ expect(sqs_queue).to receive(:send_message).with('delayed message', {
71
+ delay_seconds: 60,
72
+ message_attributes: {
73
+ 'shoryuken_class' => {
74
+ string_value: TestWorker.to_s,
75
+ data_type: 'String'
76
+ }
77
+ }
78
+ })
20
79
 
21
80
  TestWorker.perform_async('delayed message', delay_seconds: 60)
22
81
  end
@@ -37,6 +96,7 @@ describe 'Shoryuken::Worker' do
37
96
  end
38
97
 
39
98
  expect(Shoryuken.workers['production_default']).to eq NewTestWorker
99
+ expect(NewTestWorker.get_shoryuken_options['queue']).to eq 'production_default'
40
100
  end
41
101
  end
42
102
  end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoryuken do
4
+ describe '.register_worker' do
5
+ it 'registers a worker' do
6
+ described_class.workers.clear
7
+ described_class.register_worker('default', TestWorker)
8
+ expect(described_class.workers).to eq('default' => TestWorker)
9
+ end
10
+
11
+ it 'registers a batchable worker' do
12
+ described_class.workers.clear
13
+ TestWorker.get_shoryuken_options['batch'] = true
14
+ described_class.register_worker('default', TestWorker)
15
+ expect(described_class.workers).to eq('default' => TestWorker)
16
+ end
17
+
18
+ it 'allows multiple workers' do
19
+ described_class.workers.clear
20
+ described_class.register_worker('default', TestWorker)
21
+ expect(described_class.workers).to eq('default' => TestWorker)
22
+
23
+ class Test2Worker
24
+ include Shoryuken::Worker
25
+
26
+ shoryuken_options queue: 'default'
27
+
28
+ def perform(sqs_msg, body); end
29
+ end
30
+
31
+ expect(described_class.workers).to eq('default' => Test2Worker)
32
+ end
33
+
34
+ it 'raises an exception when mixing batchable with non batchable' do
35
+ described_class.workers.clear
36
+ TestWorker.get_shoryuken_options['batch'] = true
37
+ described_class.register_worker('default', TestWorker)
38
+
39
+ expect {
40
+ class BatchableWorker
41
+ include Shoryuken::Worker
42
+
43
+ shoryuken_options queue: 'default', batch: true
44
+
45
+ def perform(sqs_msg, body); end
46
+ end
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")
49
+ end
50
+ end
51
+ end
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,7 @@ Bundler.setup
4
4
  require 'pry-byebug'
5
5
  require 'celluloid'
6
6
  require 'shoryuken'
7
+ require 'json'
7
8
 
8
9
  options_file = File.join(File.expand_path('../..', __FILE__), 'shoryuken.yml')
9
10
 
@@ -40,7 +41,7 @@ RSpec.configure do |config|
40
41
  Shoryuken.options.merge!($options)
41
42
 
42
43
  Shoryuken.queues.clear
43
- Shoryuken.queues << 'shoryuken'
44
+
44
45
  Shoryuken.options[:concurrency] = 1
45
46
  Shoryuken.options[:delay] = 1
46
47
  Shoryuken.options[:timeout] = 1
@@ -49,5 +50,8 @@ RSpec.configure do |config|
49
50
 
50
51
  TestWorker.get_shoryuken_options.clear
51
52
  TestWorker.get_shoryuken_options['queue'] = 'default'
53
+
54
+ Shoryuken.workers.clear
55
+ Shoryuken.register_worker('default', TestWorker)
52
56
  end
53
57
  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.3
4
+ version: 0.0.4
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-26 00:00:00.000000000 Z
11
+ date: 2014-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: aws-sdk
70
+ name: aws-sdk-v1
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - '>='
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: celluloid
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ~>
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: 0.15.2
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ~>
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: 0.15.2
97
97
  description: Shoryuken is a super efficient AWS SQS thread based message processor
98
98
  email:
99
99
  - pablo@pablocantero.com
@@ -110,10 +110,8 @@ files:
110
110
  - README.md
111
111
  - Rakefile
112
112
  - bin/shoryuken
113
- - examples/all.rb
113
+ - examples/bootstrap_queues.rb
114
114
  - examples/default_worker.rb
115
- - examples/high_priority_worker.rb
116
- - examples/low_priority_worker.rb
117
115
  - lib/shoryuken.rb
118
116
  - lib/shoryuken/cli.rb
119
117
  - lib/shoryuken/client.rb
@@ -123,12 +121,14 @@ files:
123
121
  - lib/shoryuken/logging.rb
124
122
  - lib/shoryuken/manager.rb
125
123
  - lib/shoryuken/middleware/chain.rb
124
+ - lib/shoryuken/middleware/server/active_record.rb
126
125
  - lib/shoryuken/middleware/server/auto_delete.rb
127
126
  - lib/shoryuken/middleware/server/timing.rb
128
127
  - lib/shoryuken/processor.rb
129
128
  - lib/shoryuken/util.rb
130
129
  - lib/shoryuken/version.rb
131
130
  - lib/shoryuken/worker.rb
131
+ - lib/shoryuken/worker_loader.rb
132
132
  - shoryuken.gemspec
133
133
  - shoryuken.jpg
134
134
  - spec/integration/launcher_spec.rb
@@ -141,7 +141,9 @@ files:
141
141
  - spec/shoryuken/middleware/server/timing_spec.rb
142
142
  - spec/shoryuken/processor_spec.rb
143
143
  - spec/shoryuken/util_spec.rb
144
+ - spec/shoryuken/worker_loader_spec.rb
144
145
  - spec/shoryuken/worker_spec.rb
146
+ - spec/shoryuken_spec.rb
145
147
  - spec/spec_helper.rb
146
148
  homepage: https://github.com/phstc/shoryuken
147
149
  licenses:
@@ -178,5 +180,7 @@ test_files:
178
180
  - spec/shoryuken/middleware/server/timing_spec.rb
179
181
  - spec/shoryuken/processor_spec.rb
180
182
  - spec/shoryuken/util_spec.rb
183
+ - spec/shoryuken/worker_loader_spec.rb
181
184
  - spec/shoryuken/worker_spec.rb
185
+ - spec/shoryuken_spec.rb
182
186
  - spec/spec_helper.rb
data/examples/all.rb DELETED
@@ -1,5 +0,0 @@
1
- $stdout.sync = true
2
-
3
- require_relative 'high_priority_worker'
4
- require_relative 'default_worker'
5
- require_relative 'low_priority_worker'
@@ -1,9 +0,0 @@
1
- class HighPriorityWorker
2
- include Shoryuken::Worker
3
-
4
- shoryuken_options queue: 'high_priority', delete: true
5
-
6
- def perform(sqs_msg, body)
7
- puts "HighPriorityWorker: '#{body}'"
8
- end
9
- end
@@ -1,11 +0,0 @@
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