shoryuken 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 767009617d3e6954d4b1bec86f33811fdfd6c4e0
4
- data.tar.gz: 578e87e222d8f980a72981f75ae57a7ad6a5275f
3
+ metadata.gz: fd50ddb123d1c14d3ff0a0ac562c3e156afd2dfd
4
+ data.tar.gz: 9bcd1a88456f1d8454609d7440d209bc78b5a89b
5
5
  SHA512:
6
- metadata.gz: 6c8493dc99b8b11ee02e7c5ae1d51acf499e60b0ba1b94c2202b40d7e36f4ea3d1ea03ab9f5d2570e7943d87f4cd2b0c54940c62b12c08903f83584967897203
7
- data.tar.gz: b04c12cd3ef6efe7ac4ca4a8ab3f6011bee687aa5053db2ed0fd03205d32e76a3cd45809a4aa8e75e8b7f50d311334dd43aa698cc25e366019ddc9d932bd8a94
6
+ metadata.gz: 64680cf91fbf51372720f872f0ac7cced4d42e2b86c61b66137fd5993f1e58ca2ab86c907ff49eb8c464817c5c4efe53ea75e234a4ee01a4d558164535086c46
7
+ data.tar.gz: 75fbaab18cd22e600ccd6e705eef80532faf0236a434803cf0678dc14cf6b7fac30e40d0350e8be2b47eb8dd54289667eeccb12a49af0febbcafc0d5594e05f5
data/.codeclimate.yml CHANGED
@@ -1,8 +1,6 @@
1
1
  ---
2
2
  engines:
3
- brakeman:
4
- enabled: true
5
- bundler-audit:
3
+ reek:
6
4
  enabled: true
7
5
  duplication:
8
6
  enabled: true
@@ -11,13 +9,10 @@ engines:
11
9
  - ruby
12
10
  fixme:
13
11
  enabled: true
14
- reek:
15
- enabled: true
16
12
  rubocop:
17
13
  enabled: true
18
14
  ratings:
19
15
  paths:
20
16
  - "**.rb"
21
- # exclude_paths:
22
- # - "**/vendor/**/*"
23
- # - "*/spec/**/*"
17
+ exclude_paths:
18
+ - spec/
data/.travis.yml CHANGED
@@ -14,3 +14,7 @@ before_install:
14
14
  - gem update bundler
15
15
  after_success:
16
16
  - bundle exec codeclimate-test-reporter
17
+
18
+ addons:
19
+ code_climate:
20
+ repo_token: 7709fd21981bb9d2658647a66d959415a1029a83f1c199573828797944f26c52
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## [v2.1.2] - 2016-12-22
2
+ - Fix loading `logfile` from shoryuken.yml
3
+ - [#296](https://github.com/phstc/shoryuken/pull/296)
4
+
5
+ - Add support for Strict priority polling (pending documentation)
6
+ - [#288](https://github.com/phstc/shoryuken/pull/288)
7
+
8
+ - Add `test_workers` for end-to-end testing supporting
9
+ - [#286](https://github.com/phstc/shoryuken/pull/286)
10
+
11
+ - Update README documenting `configure_client` and `configure_server`
12
+ - [#283](https://github.com/phstc/shoryuken/pull/283)
13
+
14
+ - Fix memory leak caused by async tracking busy threads
15
+ - [#289](https://github.com/phstc/shoryuken/pull/289)
16
+
17
+ - Refactor fetcher, polling strategy and manager
18
+ - [#284](https://github.com/phstc/shoryuken/pull/284)
19
+
1
20
  ## [v2.1.1] - 2016-12-05
2
21
  - Fix aws deprecation warning message
3
22
  - [#279](https://github.com/phstc/shoryuken/pull/279)
data/README.md CHANGED
@@ -110,15 +110,6 @@ end
110
110
  Sample configuration file `shoryuken.yml`.
111
111
 
112
112
  ```yaml
113
- aws:
114
- access_key_id: ... # or <%= ENV['AWS_ACCESS_KEY_ID'] %>
115
- secret_access_key: ... # or <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
116
- region: us-east-1 # or <%= ENV['AWS_REGION'] %>
117
- receive_message: # See http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#receive_message-instance_method
118
- # wait_time_seconds: N # The number of seconds to wait for new messages when polling. Defaults to the #wait_time_seconds defined on the queue
119
- attribute_names:
120
- - ApproximateReceiveCount
121
- - SentTimestamp
122
113
  concurrency: 25 # The number of allocated threads to process messages. Default 25
123
114
  delay: 25 # The delay in seconds to pause a queue when it's empty. Default 0
124
115
  queues:
@@ -127,24 +118,80 @@ queues:
127
118
  - [low_priority, 1]
128
119
  ```
129
120
 
130
- The ```aws``` section is used to configure both the Aws objects used by Shoryuken internally, and also to set up some Shoryuken-specific config. The Shoryuken-specific keys are listed below, and you can expect any other key defined in that block to be passed on untouched to ```Aws::SQS::Client#initialize```:
121
+ And setup ```aws``` options to use ```configure_client``` in `config/initializers/shoryuken.rb`:
131
122
 
132
- - ```account_id``` is used when generating SNS ARNs
133
- - ```sns_endpoint``` can be used to explicitly override the SNS endpoint
134
- - ```sqs_endpoint``` can be used to explicitly override the SQS endpoint
135
- - ```receive_message``` can be used to define the options passed to the http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#receive_message-instance_method
123
+ ```ruby
124
+ Shoryuken.configure_client do |config|
125
+ config.aws = {
126
+ secret_access_key: ..., # or ENV["AWS_SECRET_ACCESS_KEY"]
127
+ access_key_id: ..., # or ENV["AWS_ACCESS_KEY_ID"]
128
+ region: "us-east-1", # or ENV["AWS_REGION"]
129
+ receive_message: { # See http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#receive_message-instance_method
130
+ # wait_time_seconds: N, # The number of seconds to wait for new messages when polling. Defaults to the #wait_time_seconds defined on the queue
131
+ attribute_names: [
132
+ "ApproximateReceiveCount",
133
+ "SentTimestamp"
134
+ ]
135
+ }
136
+ }
137
+ end
138
+ ```
139
+
140
+ If you use Shoryuken with plain ruby worker class (not Rails), please call `configure_client` at the beginning of the worker file:
141
+
142
+ ```ruby
143
+ Shoryuken.configure_client do |config|
144
+ config.aws = {
145
+ secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
146
+ access_key_id: ENV["AWS_ACCESS_KEY_ID"],
147
+ region: ENV["AWS_REGION"]
148
+ }
149
+ end
150
+
151
+ class MyWorker
152
+ end
153
+ ```
136
154
 
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.
155
+ The `aws` section is used to configure both the Aws objects used by Shoryuken internally, and also to set up some Shoryuken-specific config. The Shoryuken-specific keys are listed below, and you can expect any other key defined in that block to be passed on untouched to `Aws::SQS::Client#initialize`:
156
+
157
+ - `account_id` is used when generating SNS ARNs
158
+ - `sns_endpoint` can be used to explicitly override the SNS endpoint
159
+ - `sqs_endpoint` can be used to explicitly override the SQS endpoint
160
+ - `receive_message` can be used to define the options passed to the http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html#receive_message-instance_method
161
+
162
+ 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
163
 
139
164
  ### Configuration (producer side)
140
165
 
141
166
  'Producer' processes need permissions to put messages into SQS. There are a few ways:
142
167
 
168
+ * Use the `configure_server` in Rails initializer
143
169
  * Ensure the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` env vars are set.
144
170
  * Create a `~/.aws/credentials` file.
145
171
  * Set `Aws.config[:credentials]` from Ruby code (e.g. in a Rails initializer)
146
172
  * Use the Instance Profiles feature. The IAM role of the targeted machine must have an adequate SQS Policy.
147
173
 
174
+ For example, use the `configure_server` in `config/initializers/shoryuken.rb`:
175
+
176
+ ```ruby
177
+ Shoryuken.configure_client do |config|
178
+ config.aws = {
179
+ secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
180
+ access_key_id: ENV["AWS_ACCESS_KEY_ID"],
181
+ region: ENV["AWS_REGION"]
182
+ }
183
+ end
184
+
185
+ Shoryuken.configure_server do |config|
186
+ config.aws = {
187
+ secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
188
+ access_key_id: ENV["AWS_ACCESS_KEY_ID"],
189
+ region: ENV["AWS_REGION"]
190
+ }
191
+ end
192
+ ```
193
+
194
+
148
195
  Note that storing your credentials into Amazon instances represents a security risk. Instance Profiles tends to be the best choice.
149
196
 
150
197
  You can read about these in more detail [here](http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html).
data/lib/shoryuken.rb CHANGED
@@ -21,6 +21,7 @@ require 'shoryuken/middleware/server/exponential_backoff_retry'
21
21
  require 'shoryuken/middleware/server/timing'
22
22
  require 'shoryuken/sns_arn'
23
23
  require 'shoryuken/topic'
24
+ require 'shoryuken/polling'
24
25
 
25
26
  module Shoryuken
26
27
  DEFAULTS = {
@@ -33,7 +34,8 @@ module Shoryuken
33
34
  startup: [],
34
35
  quiet: [],
35
36
  shutdown: [],
36
- }
37
+ },
38
+ polling_strategy: Polling::WeightedRoundRobin,
37
39
  }
38
40
 
39
41
  @@queues = []
@@ -60,8 +60,8 @@ module Shoryuken
60
60
  end
61
61
 
62
62
  def initialize_logger
63
- Shoryuken::Logging.initialize_logger(options[:logfile]) if options[:logfile]
64
- Shoryuken.logger.level = Logger::DEBUG if options[:verbose]
63
+ Shoryuken::Logging.initialize_logger(Shoryuken.options[:logfile]) if Shoryuken.options[:logfile]
64
+ Shoryuken.logger.level = Logger::DEBUG if Shoryuken.options[:verbose]
65
65
  end
66
66
 
67
67
  def load_rails
@@ -1,26 +1,9 @@
1
1
  module Shoryuken
2
2
  class Fetcher
3
- include Celluloid
4
3
  include Util
5
4
 
6
5
  FETCH_LIMIT = 10
7
6
 
8
- def initialize(manager)
9
- @manager = manager
10
- end
11
-
12
- def receive_messages(queue, limit)
13
- # AWS limits the batch size by 10
14
- limit = limit > FETCH_LIMIT ? FETCH_LIMIT : limit
15
-
16
- options = (Shoryuken::AwsConfig.options[:receive_message] || {}).dup
17
- options[:max_number_of_messages] = limit
18
- options[:message_attribute_names] = %w(All)
19
- options[:attribute_names] = %w(All)
20
-
21
- Shoryuken::Client.queues(queue).receive_messages options
22
- end
23
-
24
7
  def fetch(queue, available_processors)
25
8
  watchdog('Fetcher#fetch died') do
26
9
  started_at = Time.now
@@ -28,46 +11,34 @@ module Shoryuken
28
11
  logger.debug { "Looking for new messages in '#{queue}'" }
29
12
 
30
13
  begin
31
- batch = Shoryuken.worker_registry.batch_receive_messages?(queue)
32
- limit = batch ? FETCH_LIMIT : available_processors
33
-
34
- if (sqs_msgs = Array(receive_messages(queue, limit))).any?
35
- logger.debug { "Found #{sqs_msgs.size} messages for '#{queue}'" }
36
-
37
- if batch
38
- @manager.async.assign(queue, patch_sqs_msgs!(sqs_msgs))
39
- else
40
- sqs_msgs.each { |sqs_msg| @manager.async.assign(queue, sqs_msg) }
41
- end
42
-
43
- @manager.async.rebalance_queue_weight!(queue)
44
- else
45
- logger.debug { "No message found for '#{queue}'" }
46
-
47
- @manager.async.pause_queue!(queue)
48
- end
14
+ limit = available_processors > FETCH_LIMIT ? FETCH_LIMIT : available_processors
49
15
 
16
+ sqs_msgs = Array(receive_messages(queue, limit))
17
+ logger.info { "Found #{sqs_msgs.size} messages for '#{queue.name}'" }
50
18
  logger.debug { "Fetcher for '#{queue}' completed in #{elapsed(started_at)} ms" }
19
+ sqs_msgs
51
20
  rescue => ex
52
21
  logger.error { "Error fetching message: #{ex}" }
53
22
  logger.error { ex.backtrace.first }
23
+ []
54
24
  end
55
-
56
- @manager.async.dispatch
57
25
  end
58
-
59
26
  end
60
27
 
61
28
  private
62
29
 
63
- def patch_sqs_msgs!(sqs_msgs)
64
- sqs_msgs.instance_eval do
65
- def message_id
66
- "batch-with-#{size}-messages"
67
- end
68
- end
30
+ def receive_messages(queue, limit)
31
+ # AWS limits the batch size by 10
32
+ limit = limit > FETCH_LIMIT ? FETCH_LIMIT : limit
33
+
34
+ options = (Shoryuken.options[:aws][:receive_message] || {}).dup
35
+ options[:max_number_of_messages] = limit
36
+ options[:message_attribute_names] = %w(All)
37
+ options[:attribute_names] = %w(All)
38
+
39
+ options.merge!(queue.options)
69
40
 
70
- sqs_msgs
41
+ Shoryuken::Client.queues(queue.name).receive_messages(options)
71
42
  end
72
43
  end
73
44
  end
@@ -10,17 +10,16 @@ module Shoryuken
10
10
  def initialize
11
11
  @condvar = Celluloid::Condition.new
12
12
  @manager = Shoryuken::Manager.new_link(@condvar)
13
- @fetcher = Shoryuken::Fetcher.new_link(manager)
14
13
 
15
14
  @done = false
16
15
 
17
- manager.fetcher = @fetcher
16
+ manager.fetcher = Shoryuken::Fetcher.new
17
+ manager.polling_strategy = Shoryuken.options[:polling_strategy].new(Shoryuken.queues)
18
18
  end
19
19
 
20
20
  def stop(options = {})
21
21
  watchdog('Launcher#stop') do
22
22
  @done = true
23
- @fetcher.terminate if @fetcher.alive?
24
23
 
25
24
  manager.async.stop(shutdown: !!options[:shutdown], timeout: Shoryuken.options[:timeout])
26
25
  @condvar.wait
@@ -7,9 +7,14 @@ module Shoryuken
7
7
  include Util
8
8
 
9
9
  attr_accessor :fetcher
10
+ attr_accessor :polling_strategy
11
+
12
+ exclusive :dispatch
10
13
 
11
14
  trap_exit :processor_died
12
15
 
16
+ BATCH_LIMIT = 10
17
+
13
18
  def initialize(condvar)
14
19
  @count = Shoryuken.options[:concurrency] || 25
15
20
  raise(ArgumentError, "Concurrency value #{@count} is invalid, it needs to be a positive number") unless @count > 0
@@ -18,9 +23,9 @@ module Shoryuken
18
23
 
19
24
  @done = false
20
25
 
21
- @busy = []
22
- @ready = @count.times.map { build_processor }
23
- @threads = {}
26
+ @busy_processors = []
27
+ @busy_threads = {}
28
+ @ready_processors = @count.times.map { build_processor }
24
29
  end
25
30
 
26
31
  def start
@@ -40,16 +45,14 @@ module Shoryuken
40
45
 
41
46
  fire_event(:shutdown, true)
42
47
 
43
- @fetcher.terminate if @fetcher.alive?
48
+ logger.info { "Shutting down #{@ready_processors.size} quiet workers" }
44
49
 
45
- logger.info { "Shutting down #{@ready.size} quiet workers" }
46
-
47
- @ready.each do |processor|
50
+ @ready_processors.each do |processor|
48
51
  processor.terminate if processor.alive?
49
52
  end
50
- @ready.clear
53
+ @ready_processors.clear
51
54
 
52
- return after(0) { @finished.signal } if @busy.empty?
55
+ return after(0) { @finished.signal } if @busy_processors.empty?
53
56
 
54
57
  if options[:shutdown]
55
58
  hard_shutdown_in(options[:timeout])
@@ -63,14 +66,15 @@ module Shoryuken
63
66
  watchdog('Manager#processor_done died') do
64
67
  logger.debug { "Process done for '#{queue}'" }
65
68
 
66
- @threads.delete(processor.object_id)
67
- @busy.delete processor
69
+ @busy_processors.delete(processor)
70
+ @busy_threads.delete(processor.object_id)
68
71
 
69
72
  if stopped?
70
73
  processor.terminate if processor.alive?
71
- return after(0) { @finished.signal } if @busy.empty?
74
+ return after(0) { @finished.signal } if @busy_processors.empty?
72
75
  else
73
- @ready << processor
76
+ @ready_processors << processor
77
+ async.dispatch
74
78
  end
75
79
  end
76
80
  end
@@ -79,13 +83,14 @@ module Shoryuken
79
83
  watchdog("Manager#processor_died died") do
80
84
  logger.error { "Process died, reason: #{reason}" }
81
85
 
82
- @threads.delete(processor.object_id)
83
- @busy.delete processor
86
+ @busy_processors.delete(processor)
87
+ @busy_threads.delete(processor.object_id)
84
88
 
85
89
  if stopped?
86
- return after(0) { @finished.signal } if @busy.empty?
90
+ return after(0) { @finished.signal } if @busy_processors.empty?
87
91
  else
88
- @ready << build_processor
92
+ @ready_processors << build_processor
93
+ async.dispatch
89
94
  end
90
95
  end
91
96
  end
@@ -94,61 +99,27 @@ module Shoryuken
94
99
  @done
95
100
  end
96
101
 
97
- def assign(queue, sqs_msg)
98
- watchdog('Manager#assign died') do
99
- logger.debug { "Assigning #{sqs_msg.message_id}" }
100
-
101
- processor = @ready.pop
102
- @busy << processor
103
-
104
- processor.async.process(queue, sqs_msg)
105
- end
106
- end
107
-
108
- def rebalance_queue_weight!(queue)
109
- watchdog('Manager#rebalance_queue_weight! died') do
110
- if (original = original_queue_weight(queue)) > (current = current_queue_weight(queue))
111
- logger.info { "Increasing '#{queue}' weight to #{current + 1}, max: #{original}" }
112
-
113
- @queues << queue
114
- end
115
- end
116
- end
117
-
118
- def pause_queue!(queue)
119
- return if !@queues.include?(queue) || Shoryuken.options[:delay].to_f <= 0
120
-
121
- logger.debug { "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because it's empty" }
122
-
123
- @queues.delete(queue)
124
-
125
- after(Shoryuken.options[:delay].to_f) { async.restart_queue!(queue) }
126
- end
127
-
128
-
129
102
  def dispatch
130
103
  return if stopped?
131
104
 
132
- logger.debug { "Ready: #{@ready.size}, Busy: #{@busy.size}, Active Queues: #{unparse_queues(@queues)}" }
105
+ logger.debug { "Ready: #{@ready_processors.size}, Busy: #{@busy_processors.size}, Active Queues: #{polling_strategy.active_queues}" }
133
106
 
134
- if @ready.empty?
107
+ if @ready_processors.empty?
135
108
  logger.debug { 'Pausing fetcher, because all processors are busy' }
136
-
137
109
  dispatch_later
138
110
  return
139
111
  end
140
112
 
141
- if (queue = next_queue)
142
- @fetcher.async.fetch(queue, @ready.size)
143
- else
113
+ queue = polling_strategy.next_queue
114
+ if queue.nil?
144
115
  logger.debug { 'Pausing fetcher, because all queues are paused' }
145
-
146
- @fetcher_paused = true
116
+ dispatch_later
117
+ return
147
118
  end
148
- end
149
119
 
150
- def real_thread(proxy_id, thr)
151
- @threads[proxy_id] = thr
120
+ batched_queue?(queue) ? dispatch_batch(queue) : dispatch_single_messages(queue)
121
+
122
+ async.dispatch
152
123
  end
153
124
 
154
125
  private
@@ -160,67 +131,48 @@ module Shoryuken
160
131
  end
161
132
  end
162
133
 
163
- def build_processor
164
- processor = Processor.new_link(current_actor)
165
- processor.proxy_id = processor.object_id
166
- processor
167
- end
168
-
169
- def restart_queue!(queue)
170
- return if stopped?
171
-
172
- unless @queues.include? queue
173
- logger.debug { "Restarting '#{queue}'" }
174
-
175
- @queues << queue
176
-
177
- if @fetcher_paused
178
- logger.debug { 'Restarting fetcher' }
134
+ def assign(queue, sqs_msg)
135
+ watchdog('Manager#assign died') do
136
+ logger.debug { "Assigning #{sqs_msg.message_id}" }
179
137
 
180
- @fetcher_paused = false
138
+ processor = @ready_processors.pop
139
+ @busy_threads[processor.object_id] = processor.running_thread
140
+ @busy_processors << processor
181
141
 
182
- dispatch
183
- end
142
+ processor.async.process(queue, sqs_msg)
184
143
  end
185
144
  end
186
145
 
187
- def current_queue_weight(queue)
188
- queue_weight(@queues, queue)
146
+ def dispatch_batch(queue)
147
+ batch = fetcher.fetch(queue, BATCH_LIMIT)
148
+ polling_strategy.messages_found(queue.name, batch.size)
149
+ assign(queue.name, patch_batch!(batch))
189
150
  end
190
151
 
191
- def original_queue_weight(queue)
192
- queue_weight(Shoryuken.queues, queue)
152
+ def dispatch_single_messages(queue)
153
+ messages = fetcher.fetch(queue, @ready_processors.size)
154
+ polling_strategy.messages_found(queue.name, messages.size)
155
+ messages.each { |message| assign(queue.name, message) }
193
156
  end
194
157
 
195
- def queue_weight(queues, queue)
196
- queues.count { |q| q == queue }
158
+ def batched_queue?(queue)
159
+ Shoryuken.worker_registry.batch_receive_messages?(queue.name)
197
160
  end
198
161
 
199
- def next_queue
200
- return nil if @queues.empty?
201
-
202
- # get/remove the first queue in the list
203
- queue = @queues.shift
204
-
205
- unless defined?(::ActiveJob) || !Shoryuken.worker_registry.workers(queue).empty?
206
- # when no worker registered pause the queue to avoid endless recursion
207
- logger.debug { "Pausing '#{queue}' for #{Shoryuken.options[:delay].to_f} seconds, because no workers registered" }
208
-
209
- after(Shoryuken.options[:delay].to_f) { async.restart_queue!(queue) }
210
-
211
- return next_queue
212
- end
213
-
214
- # add queue back to the end of the list
215
- @queues << queue
162
+ def delay
163
+ Shoryuken.options[:delay].to_f
164
+ end
216
165
 
217
- queue
166
+ def build_processor
167
+ processor = Processor.new_link(current_actor)
168
+ processor.proxy_id = processor.object_id
169
+ processor
218
170
  end
219
171
 
220
172
  def soft_shutdown(delay)
221
- logger.info { "Waiting for #{@busy.size} busy workers" }
173
+ logger.info { "Waiting for #{@busy_processors.size} busy workers" }
222
174
 
223
- if @busy.size > 0
175
+ if @busy_processors.size > 0
224
176
  after(delay) { soft_shutdown(delay) }
225
177
  else
226
178
  @finished.signal
@@ -228,16 +180,16 @@ module Shoryuken
228
180
  end
229
181
 
230
182
  def hard_shutdown_in(delay)
231
- logger.info { "Waiting for #{@busy.size} busy workers" }
183
+ logger.info { "Waiting for #{@busy_processors.size} busy workers" }
232
184
  logger.info { "Pausing up to #{delay} seconds to allow workers to finish..." }
233
185
 
234
186
  after(delay) do
235
187
  watchdog('Manager#hard_shutdown_in died') do
236
- if @busy.size > 0
237
- logger.info { "Hard shutting down #{@busy.size} busy workers" }
188
+ if @busy_processors.size > 0
189
+ logger.info { "Hard shutting down #{@busy_processors.size} busy workers" }
238
190
 
239
- @busy.each do |processor|
240
- if processor.alive? && t = @threads.delete(processor.object_id)
191
+ @busy_processors.each do |processor|
192
+ if processor.alive? && t = @busy_threads.delete(processor.object_id)
241
193
  t.raise Shutdown
242
194
  end
243
195
  end
@@ -247,5 +199,15 @@ module Shoryuken
247
199
  end
248
200
  end
249
201
  end
202
+
203
+ def patch_batch!(sqs_msgs)
204
+ sqs_msgs.instance_eval do
205
+ def message_id
206
+ "batch-with-#{size}-messages"
207
+ end
208
+ end
209
+
210
+ sqs_msgs
211
+ end
250
212
  end
251
213
  end