shoryuken 3.0.6 → 4.0.0
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/.rubocop.yml +90 -24
- data/.travis.yml +17 -5
- data/CHANGELOG.md +265 -62
- data/Gemfile +9 -1
- data/Gemfile.aws-sdk-core-v2 +13 -0
- data/README.md +19 -113
- data/Rakefile +1 -1
- data/bin/cli/base.rb +0 -3
- data/bin/cli/sqs.rb +42 -16
- data/bin/shoryuken +4 -9
- data/examples/bootstrap_queues.rb +3 -3
- data/examples/default_worker.rb +2 -2
- data/lib/shoryuken/body_parser.rb +27 -0
- data/lib/shoryuken/client.rb +6 -2
- data/lib/shoryuken/core_ext.rb +1 -1
- data/lib/shoryuken/default_worker_registry.rb +2 -2
- data/lib/shoryuken/environment_loader.rb +60 -24
- data/lib/shoryuken/extensions/active_job_adapter.rb +21 -11
- data/lib/shoryuken/fetcher.rb +58 -19
- data/lib/shoryuken/launcher.rb +70 -7
- data/lib/shoryuken/logging.rb +1 -6
- data/lib/shoryuken/manager.rb +50 -80
- data/lib/shoryuken/middleware/chain.rb +4 -0
- data/lib/shoryuken/middleware/server/active_record.rb +1 -1
- data/lib/shoryuken/middleware/server/auto_delete.rb +4 -9
- data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +6 -9
- data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +9 -3
- data/lib/shoryuken/middleware/server/timing.rb +12 -16
- data/lib/shoryuken/options.rb +225 -0
- data/lib/shoryuken/polling/base.rb +67 -0
- data/lib/shoryuken/polling/strict_priority.rb +77 -0
- data/lib/shoryuken/polling/weighted_round_robin.rb +66 -0
- data/lib/shoryuken/processor.rb +30 -39
- data/lib/shoryuken/queue.rb +41 -10
- data/lib/shoryuken/runner.rb +13 -17
- data/lib/shoryuken/util.rb +3 -3
- data/lib/shoryuken/version.rb +1 -1
- data/lib/shoryuken/worker/default_executor.rb +33 -0
- data/lib/shoryuken/worker/inline_executor.rb +37 -0
- data/lib/shoryuken/worker.rb +76 -31
- data/lib/shoryuken/worker_registry.rb +4 -4
- data/lib/shoryuken.rb +54 -173
- data/shoryuken.gemspec +6 -6
- data/spec/integration/launcher_spec.rb +14 -8
- data/spec/shoryuken/body_parser_spec.rb +89 -0
- data/spec/shoryuken/client_spec.rb +1 -1
- data/spec/shoryuken/core_ext_spec.rb +6 -6
- data/spec/shoryuken/default_worker_registry_spec.rb +2 -4
- data/spec/shoryuken/environment_loader_spec.rb +32 -12
- data/spec/shoryuken/extensions/active_job_adapter_spec.rb +64 -0
- data/spec/shoryuken/fetcher_spec.rb +101 -18
- data/spec/shoryuken/manager_spec.rb +54 -26
- data/spec/shoryuken/middleware/chain_spec.rb +17 -5
- data/spec/shoryuken/middleware/server/auto_delete_spec.rb +9 -7
- data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +4 -4
- data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +6 -4
- data/spec/shoryuken/middleware/server/timing_spec.rb +5 -3
- data/spec/shoryuken/options_spec.rb +180 -0
- data/spec/shoryuken/{polling_spec.rb → polling/strict_priority_spec.rb} +2 -101
- data/spec/shoryuken/polling/weighted_round_robin_spec.rb +99 -0
- data/spec/shoryuken/processor_spec.rb +26 -127
- data/spec/shoryuken/queue_spec.rb +115 -41
- data/spec/shoryuken/runner_spec.rb +3 -4
- data/spec/shoryuken/util_spec.rb +24 -0
- data/spec/shoryuken/worker/default_executor_spec.rb +105 -0
- data/spec/shoryuken/worker/inline_executor_spec.rb +49 -0
- data/spec/shoryuken/worker_spec.rb +35 -96
- data/spec/shoryuken_spec.rb +0 -59
- data/spec/spec_helper.rb +14 -3
- data/test_workers/endless_interruptive_worker.rb +2 -2
- data/test_workers/endless_uninterruptive_worker.rb +4 -4
- metadata +31 -12
- data/lib/shoryuken/polling.rb +0 -204
data/Gemfile
CHANGED
|
@@ -4,7 +4,15 @@ source 'https://rubygems.org'
|
|
|
4
4
|
gemspec
|
|
5
5
|
|
|
6
6
|
group :test do
|
|
7
|
+
gem 'activejob', '~> 4'
|
|
8
|
+
gem 'aws-sdk-core', '~> 3'
|
|
9
|
+
gem 'aws-sdk-sqs'
|
|
7
10
|
gem 'codeclimate-test-reporter', require: nil
|
|
8
|
-
gem '
|
|
11
|
+
gem 'httparty'
|
|
9
12
|
gem 'multi_xml'
|
|
13
|
+
gem 'simplecov'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
group :development do
|
|
17
|
+
gem 'rubocop'
|
|
10
18
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
# Specify your gem's dependencies in shoryuken.gemspec
|
|
4
|
+
gemspec
|
|
5
|
+
|
|
6
|
+
group :test do
|
|
7
|
+
gem 'activejob', '~> 4'
|
|
8
|
+
gem 'aws-sdk-core', '~> 2'
|
|
9
|
+
gem 'codeclimate-test-reporter', require: nil
|
|
10
|
+
gem 'httparty'
|
|
11
|
+
gem 'multi_xml'
|
|
12
|
+
gem 'simplecov'
|
|
13
|
+
end
|
data/README.md
CHANGED
|
@@ -1,42 +1,23 @@
|
|
|
1
1
|
# Shoryuken
|
|
2
2
|
|
|
3
|
-

|
|
3
|
+

|
|
4
4
|
|
|
5
|
-
Shoryuken _sho-ryu-ken_ is a super-efficient [
|
|
5
|
+
Shoryuken _sho-ryu-ken_ is a super-efficient [Amazon SQS](https://aws.amazon.com/sqs/) thread-based message processor.
|
|
6
6
|
|
|
7
7
|
[](https://travis-ci.org/phstc/shoryuken)
|
|
8
8
|
[](https://codeclimate.com/github/phstc/shoryuken)
|
|
9
9
|
|
|
10
10
|
## Key features
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
queues:
|
|
22
|
-
- [high_priority, 6]
|
|
23
|
-
- [normal_priority, 2]
|
|
24
|
-
- [low_priority, 1]
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
And supposing all the queues are full of messages, the configuration above will make Shoryuken to process `high_priority` 3 times more than `normal_priority` and 6 times more than `low_priority`,
|
|
28
|
-
splitting the work load among all available processors `concurrency: 25` .
|
|
29
|
-
|
|
30
|
-
If `high_priority` gets empty, Shoryuken will keep using the 25 processors, but only to process `normal_priority` and `low_priority`.
|
|
31
|
-
|
|
32
|
-
If `high_priority` receives a new message, Shoryuken will smoothly increase back its weight one by one until it reaches the weight of 6 again.
|
|
33
|
-
|
|
34
|
-
[If a queue gets empty, Shoryuken will pause checking it for `delay: 25`](https://github.com/phstc/shoryuken/wiki/Shoryuken-options#delay).
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### Fetch in batches
|
|
38
|
-
|
|
39
|
-
To be even more performant and cost effective, Shoryuken fetches SQS messages in batches, so a single SQS request can fetch up to 10 messages.
|
|
12
|
+
- [Rails Active Job](https://github.com/phstc/shoryuken/wiki/Rails-Integration-Active-Job)
|
|
13
|
+
- [Queue Load balancing](https://github.com/phstc/shoryuken/wiki/Shoryuken-options#load-balancing)
|
|
14
|
+
- [Concurrency per queue](https://github.com/phstc/shoryuken/wiki/Processing-Groups)
|
|
15
|
+
- [Long Polling](https://github.com/phstc/shoryuken/wiki/Long-Polling)
|
|
16
|
+
- [Batch processing](https://github.com/phstc/shoryuken/wiki/Worker-options#batch)
|
|
17
|
+
- [Auto extend visibility timeout](https://github.com/phstc/shoryuken/wiki/Worker-options#auto_visibility_timeout)
|
|
18
|
+
- [Exponential backoff](https://github.com/phstc/shoryuken/wiki/Worker-options#retry_intervals)
|
|
19
|
+
- [Middleware support](https://github.com/phstc/shoryuken/wiki/Middleware)
|
|
20
|
+
- Amazon SQS CLI. See `shoryuken help sqs`
|
|
40
21
|
|
|
41
22
|
## Requirements
|
|
42
23
|
|
|
@@ -50,102 +31,27 @@ Add this line to your application's Gemfile:
|
|
|
50
31
|
gem 'shoryuken'
|
|
51
32
|
```
|
|
52
33
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```ruby
|
|
56
|
-
gem 'shoryuken', github: 'phstc/shoryuken', branch: 'master'
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
And then execute:
|
|
60
|
-
|
|
61
|
-
$ bundle
|
|
62
|
-
|
|
63
|
-
Or install it yourself as:
|
|
64
|
-
|
|
65
|
-
$ gem install shoryuken
|
|
66
|
-
|
|
67
|
-
## Usage
|
|
68
|
-
|
|
69
|
-
### Worker class
|
|
70
|
-
|
|
71
|
-
```ruby
|
|
72
|
-
class MyWorker
|
|
73
|
-
include Shoryuken::Worker
|
|
74
|
-
|
|
75
|
-
shoryuken_options queue: 'default', auto_delete: true
|
|
76
|
-
# shoryuken_options queue: ->{ "#{ENV['environment']}_default" }
|
|
77
|
-
|
|
78
|
-
# shoryuken_options body_parser: :json
|
|
79
|
-
# shoryuken_options body_parser: ->(sqs_msg){ REXML::Document.new(sqs_msg.body) }
|
|
80
|
-
# shoryuken_options body_parser: JSON
|
|
81
|
-
|
|
82
|
-
def perform(sqs_msg, body)
|
|
83
|
-
puts body
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
[Check the Worker options documention](https://github.com/phstc/shoryuken/wiki/Worker-options).
|
|
89
|
-
|
|
90
|
-
### Sending a message
|
|
91
|
-
|
|
92
|
-
[Check the Sending a message documentation](https://github.com/phstc/shoryuken/wiki/Sending-a-message)
|
|
93
|
-
|
|
94
|
-
### Middleware
|
|
34
|
+
If you are using AWS SDK version 3, please also add this line:
|
|
95
35
|
|
|
96
36
|
```ruby
|
|
97
|
-
|
|
98
|
-
def call(worker_instance, queue, sqs_msg, body)
|
|
99
|
-
puts 'Before work'
|
|
100
|
-
yield
|
|
101
|
-
puts 'After work'
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
[Check the Middleware documentation](https://github.com/phstc/shoryuken/wiki/Middleware).
|
|
107
|
-
|
|
108
|
-
### Shoryuken Configuration
|
|
109
|
-
|
|
110
|
-
Sample configuration file `shoryuken.yml`.
|
|
111
|
-
|
|
112
|
-
```yaml
|
|
113
|
-
concurrency: 25 # The number of allocated threads to process messages. Default 25
|
|
114
|
-
delay: 25 # The delay in seconds to pause a queue when it's empty. Default 0
|
|
115
|
-
queues:
|
|
116
|
-
- [high_priority, 6]
|
|
117
|
-
- [normal_priority, 2]
|
|
118
|
-
- [low_priority, 1]
|
|
37
|
+
gem 'aws-sdk-sqs'
|
|
119
38
|
```
|
|
120
39
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
[Check the Configure AWS Client documentation](https://github.com/phstc/shoryuken/wiki/Configure-the-AWS-Client)
|
|
124
|
-
|
|
125
|
-
### Rails Integration
|
|
40
|
+
The extra gem `aws-sdk-sqs` is required in order to keep Shoryuken compatible with AWS SDK version 2 and 3.
|
|
126
41
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
### Start Shoryuken
|
|
42
|
+
And then execute:
|
|
130
43
|
|
|
131
44
|
```shell
|
|
132
|
-
bundle
|
|
45
|
+
$ bundle
|
|
133
46
|
```
|
|
134
47
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
#### SQS commands
|
|
138
|
-
|
|
139
|
-
Check also some available SQS commands `bundle exec shoryuken help sqs`, such as:
|
|
48
|
+
## Usage
|
|
140
49
|
|
|
141
|
-
|
|
142
|
-
- `mv` move messages from one queue to another
|
|
143
|
-
- `dump` dump messages from a queue into a JSON lines file
|
|
144
|
-
- `requeue` requeue messages from a dump file
|
|
50
|
+
Check the [Getting Started](https://github.com/phstc/shoryuken/wiki/Getting-Started) page.
|
|
145
51
|
|
|
146
52
|
## More Information
|
|
147
53
|
|
|
148
|
-
For more information
|
|
54
|
+
For more information check the [wiki page](https://github.com/phstc/shoryuken/wiki).
|
|
149
55
|
|
|
150
56
|
## Credits
|
|
151
57
|
|
data/Rakefile
CHANGED
|
@@ -12,7 +12,7 @@ task :console do
|
|
|
12
12
|
require 'pry'
|
|
13
13
|
require 'shoryuken'
|
|
14
14
|
|
|
15
|
-
config_file = File.join File.expand_path(
|
|
15
|
+
config_file = File.join File.expand_path(__dir__), 'shoryuken.yml'
|
|
16
16
|
|
|
17
17
|
if File.exist? config_file
|
|
18
18
|
config = YAML.load File.read(config_file)
|
data/bin/cli/base.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# rubocop:disable Metrics/BlockLength
|
|
2
1
|
module Shoryuken
|
|
3
2
|
module CLI
|
|
4
3
|
class Base < Thor
|
|
@@ -25,10 +24,8 @@ module Shoryuken
|
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
def print_format_column(column, size)
|
|
28
|
-
size = 40 if size > 40
|
|
29
27
|
size_with_padding = size + 4
|
|
30
28
|
column = column.to_s.ljust(size_with_padding)
|
|
31
|
-
column = "#{column[0...size - 2]}.." if column.size > size_with_padding
|
|
32
29
|
column
|
|
33
30
|
end
|
|
34
31
|
|
data/bin/cli/sqs.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'date'
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Metrics/
|
|
3
|
+
# rubocop:disable Metrics/BlockLength
|
|
4
4
|
module Shoryuken
|
|
5
5
|
module CLI
|
|
6
6
|
class SQS < Base
|
|
@@ -8,12 +8,15 @@ module Shoryuken
|
|
|
8
8
|
|
|
9
9
|
no_commands do
|
|
10
10
|
def normalize_dump_message(message)
|
|
11
|
-
|
|
12
|
-
message[:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
# symbolize_keys is needed for keeping it compatible with `requeue`
|
|
12
|
+
attributes = message[:attributes].symbolize_keys
|
|
13
|
+
{
|
|
14
|
+
id: message[:message_id],
|
|
15
|
+
message_body: message[:body],
|
|
16
|
+
message_attributes: message[:message_attributes],
|
|
17
|
+
message_deduplication_id: attributes[:MessageDeduplicationId],
|
|
18
|
+
message_group_id: attributes[:MessageGroupId]
|
|
19
|
+
}
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def sqs
|
|
@@ -32,20 +35,23 @@ module Shoryuken
|
|
|
32
35
|
queue_url: url,
|
|
33
36
|
entries: batch.map { |message| { id: message.message_id, receipt_handle: message.receipt_handle } }
|
|
34
37
|
).failed.any? do |failure|
|
|
35
|
-
say
|
|
38
|
+
say(
|
|
39
|
+
"Could not delete #{failure.id}, code: #{failure.code}, message: #{failure.message}, sender_fault: #{failure.sender_fault}",
|
|
40
|
+
:yellow
|
|
41
|
+
)
|
|
36
42
|
end
|
|
37
43
|
end
|
|
38
44
|
end
|
|
39
45
|
|
|
40
|
-
def batch_send(url, messages)
|
|
41
|
-
messages.to_a.flatten.map(&method(:normalize_dump_message)).each_slice(
|
|
46
|
+
def batch_send(url, messages, messages_per_batch = 10)
|
|
47
|
+
messages.to_a.flatten.map(&method(:normalize_dump_message)).each_slice(messages_per_batch) do |batch|
|
|
42
48
|
sqs.send_message_batch(queue_url: url, entries: batch).failed.any? do |failure|
|
|
43
49
|
say "Could not requeue #{failure.id}, code: #{failure.code}", :yellow
|
|
44
50
|
end
|
|
45
51
|
end
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
def find_all(url, limit
|
|
54
|
+
def find_all(url, limit)
|
|
49
55
|
count = 0
|
|
50
56
|
batch_size = limit > 10 ? 10 : limit
|
|
51
57
|
|
|
@@ -56,6 +62,7 @@ module Shoryuken
|
|
|
56
62
|
messages = sqs.receive_message(
|
|
57
63
|
queue_url: url,
|
|
58
64
|
max_number_of_messages: batch_size,
|
|
65
|
+
attribute_names: ['All'],
|
|
59
66
|
message_attribute_names: ['All']
|
|
60
67
|
).messages
|
|
61
68
|
|
|
@@ -71,7 +78,7 @@ module Shoryuken
|
|
|
71
78
|
end
|
|
72
79
|
|
|
73
80
|
def list_and_print_queues(urls)
|
|
74
|
-
attrs = %w
|
|
81
|
+
attrs = %w[QueueArn ApproximateNumberOfMessages ApproximateNumberOfMessagesNotVisible LastModifiedTimestamp]
|
|
75
82
|
|
|
76
83
|
entries = urls.map { |u| sqs.get_queue_attributes(queue_url: u, attribute_names: attrs).attributes }.map do |q|
|
|
77
84
|
[
|
|
@@ -93,8 +100,8 @@ module Shoryuken
|
|
|
93
100
|
end
|
|
94
101
|
|
|
95
102
|
desc 'ls [QUEUE-NAME-PREFIX]', 'Lists queues'
|
|
96
|
-
method_option :watch,
|
|
97
|
-
method_option :
|
|
103
|
+
method_option :watch, aliases: '-w', type: :boolean, desc: 'watch queues'
|
|
104
|
+
method_option :interval, aliases: '-n', type: :numeric, default: 2, desc: 'watch interval in seconds'
|
|
98
105
|
def ls(queue_name_prefix = '')
|
|
99
106
|
trap('SIGINT', 'EXIT') # expect ctrl-c from loop
|
|
100
107
|
|
|
@@ -105,7 +112,7 @@ module Shoryuken
|
|
|
105
112
|
|
|
106
113
|
break unless options[:watch]
|
|
107
114
|
|
|
108
|
-
sleep options[:
|
|
115
|
+
sleep options[:interval]
|
|
109
116
|
puts
|
|
110
117
|
end
|
|
111
118
|
end
|
|
@@ -145,12 +152,13 @@ module Shoryuken
|
|
|
145
152
|
end
|
|
146
153
|
|
|
147
154
|
desc 'requeue QUEUE-NAME PATH', 'Requeues messages from a dump file'
|
|
155
|
+
method_option :batch_size, aliases: '-n', type: :numeric, default: 10, desc: 'number of messages per batch to send'
|
|
148
156
|
def requeue(queue_name, path)
|
|
149
157
|
fail_task "Path #{path} not found" unless File.exist?(path)
|
|
150
158
|
|
|
151
159
|
messages = File.readlines(path).map { |line| JSON.parse(line, symbolize_names: true) }
|
|
152
160
|
|
|
153
|
-
batch_send(find_queue_url(queue_name), messages)
|
|
161
|
+
batch_send(find_queue_url(queue_name), messages, options[:batch_size])
|
|
154
162
|
|
|
155
163
|
say "Requeued #{messages.size} messages from #{path} to #{queue_name}", :green
|
|
156
164
|
end
|
|
@@ -182,6 +190,24 @@ module Shoryuken
|
|
|
182
190
|
|
|
183
191
|
say "Purge request sent for #{queue_name}. The message deletion process takes up to 60 seconds", :yellow
|
|
184
192
|
end
|
|
193
|
+
|
|
194
|
+
desc 'create QUEUE-NAME', 'Create a queue'
|
|
195
|
+
method_option :attributes, aliases: '-a', type: :hash, default: {}, desc: 'queue attributes'
|
|
196
|
+
def create(queue_name)
|
|
197
|
+
attributes = options[:attributes]
|
|
198
|
+
attributes['FifoQueue'] ||= 'true' if queue_name.end_with?('.fifo')
|
|
199
|
+
|
|
200
|
+
queue_url = sqs.create_queue(queue_name: queue_name, attributes: attributes).queue_url
|
|
201
|
+
|
|
202
|
+
say "Queue #{queue_name} was successfully created. Queue URL #{queue_url}", :green
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
desc 'delete QUEUE-NAME', 'delete a queue'
|
|
206
|
+
def delete(queue_name)
|
|
207
|
+
sqs.delete_queue(queue_url: find_queue_url(queue_name))
|
|
208
|
+
|
|
209
|
+
say "Queue #{queue_name} was successfully delete", :green
|
|
210
|
+
end
|
|
185
211
|
end
|
|
186
212
|
end
|
|
187
213
|
end
|
data/bin/shoryuken
CHANGED
|
@@ -7,8 +7,6 @@ require 'aws-sdk-core'
|
|
|
7
7
|
require_relative 'cli/base'
|
|
8
8
|
require_relative 'cli/sqs'
|
|
9
9
|
require_relative '../lib/shoryuken/runner'
|
|
10
|
-
|
|
11
|
-
# rubocop:disable Metrics/AbcSize
|
|
12
10
|
module Shoryuken
|
|
13
11
|
module CLI
|
|
14
12
|
class Runner < Base
|
|
@@ -21,6 +19,7 @@ module Shoryuken
|
|
|
21
19
|
method_option :daemon, aliases: '-d', type: :boolean, desc: 'Daemonize process'
|
|
22
20
|
method_option :queues, aliases: '-q', type: :array, desc: 'Queues to process with optional weights'
|
|
23
21
|
method_option :require, aliases: '-r', type: :string, desc: 'Dir or path of the workers'
|
|
22
|
+
method_option :timeout, aliases: '-t', type: :numeric, desc: 'Hard shutdown timeout'
|
|
24
23
|
method_option :config, aliases: '-C', type: :string, desc: 'Path to config file'
|
|
25
24
|
method_option :config_file, type: :string, desc: 'Path to config file (backwards compatibility)'
|
|
26
25
|
method_option :rails, aliases: '-R', type: :boolean, desc: 'Load Rails'
|
|
@@ -31,18 +30,14 @@ module Shoryuken
|
|
|
31
30
|
def start
|
|
32
31
|
opts = options.to_h.symbolize_keys
|
|
33
32
|
|
|
34
|
-
if opts[:config_file]
|
|
35
|
-
say "[DEPRECATED] Please use --config instead of --config-file", :yellow
|
|
36
|
-
end
|
|
33
|
+
say '[DEPRECATED] Please use --config instead of --config-file', :yellow if opts[:config_file]
|
|
37
34
|
|
|
38
35
|
opts[:config_file] = opts.delete(:config) if opts[:config]
|
|
39
36
|
|
|
40
37
|
# Keep compatibility with old CLI queue format
|
|
41
|
-
opts[:queues] =
|
|
38
|
+
opts[:queues] = opts[:queues].reject(&:empty?).map { |q| q.split(',') } if opts[:queues]
|
|
42
39
|
|
|
43
|
-
if
|
|
44
|
-
fail_task "You should set a logfile if you're going to daemonize"
|
|
45
|
-
end
|
|
40
|
+
fail_task "You should set a logfile if you're going to daemonize" if opts[:daemon] && opts[:logfile].nil?
|
|
46
41
|
|
|
47
42
|
Shoryuken::Runner.instance.run(opts.freeze)
|
|
48
43
|
end
|
|
@@ -2,7 +2,7 @@ require 'yaml'
|
|
|
2
2
|
require 'shoryuken'
|
|
3
3
|
|
|
4
4
|
# load SQS credentials
|
|
5
|
-
config = YAML.load File.read(File.join(File.expand_path(
|
|
5
|
+
config = YAML.load File.read(File.join(File.expand_path(__dir__), 'shoryuken.yml'))
|
|
6
6
|
|
|
7
7
|
Aws.config = config['aws']
|
|
8
8
|
|
|
@@ -18,11 +18,11 @@ if sqs.config['endpoint'] =~ /amazonaws.com/
|
|
|
18
18
|
|
|
19
19
|
dead_letter_queue_arn = sqs.get_queue_attributes(
|
|
20
20
|
queue_url: dead_letter_queue_url,
|
|
21
|
-
attribute_names: %w
|
|
21
|
+
attribute_names: %w[QueueArn]
|
|
22
22
|
).attributes['QueueArn']
|
|
23
23
|
|
|
24
24
|
attributes = {}
|
|
25
|
-
attributes['RedrivePolicy'] = %
|
|
25
|
+
attributes['RedrivePolicy'] = %({"maxReceiveCount":"7", "deadLetterTargetArn":"#{dead_letter_queue_arn}"})
|
|
26
26
|
|
|
27
27
|
sqs.set_queue_attributes queue_url: default_queue_url, attributes: attributes
|
|
28
28
|
end
|
data/examples/default_worker.rb
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Shoryuken
|
|
2
|
+
class BodyParser
|
|
3
|
+
class << self
|
|
4
|
+
def parse(worker_class, sqs_msg)
|
|
5
|
+
body_parser = worker_class.get_shoryuken_options['body_parser']
|
|
6
|
+
|
|
7
|
+
case body_parser
|
|
8
|
+
when :json
|
|
9
|
+
JSON.parse(sqs_msg.body)
|
|
10
|
+
when Proc
|
|
11
|
+
body_parser.call(sqs_msg)
|
|
12
|
+
when :text, nil
|
|
13
|
+
sqs_msg.body
|
|
14
|
+
else
|
|
15
|
+
if body_parser.respond_to?(:parse)
|
|
16
|
+
# JSON.parse
|
|
17
|
+
body_parser.parse(sqs_msg.body)
|
|
18
|
+
elsif body_parser.respond_to?(:load)
|
|
19
|
+
# see https://github.com/phstc/shoryuken/pull/91
|
|
20
|
+
# JSON.load
|
|
21
|
+
body_parser.load(sqs_msg.body)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/shoryuken/client.rb
CHANGED
|
@@ -8,11 +8,15 @@ module Shoryuken
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def sqs
|
|
11
|
-
|
|
11
|
+
Shoryuken.sqs_client
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def sqs=(sqs)
|
|
15
|
-
@@sqs
|
|
15
|
+
# Since the @@queues values (Shoryuken::Queue objects) are built referencing @@sqs, if it changes, we need to
|
|
16
|
+
# re-build them on subsequent calls to `.queues(name)`.
|
|
17
|
+
@@queues = {}
|
|
18
|
+
|
|
19
|
+
Shoryuken.sqs_client = sqs
|
|
16
20
|
end
|
|
17
21
|
end
|
|
18
22
|
end
|
data/lib/shoryuken/core_ext.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Shoryuken
|
|
|
24
24
|
@workers[queue]
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
worker_class.new
|
|
27
|
+
worker_class.new if worker_class
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def queues
|
|
@@ -34,7 +34,7 @@ module Shoryuken
|
|
|
34
34
|
def register_worker(queue, clazz)
|
|
35
35
|
if (worker_class = @workers[queue])
|
|
36
36
|
if worker_class.get_shoryuken_options['batch'] == true || clazz.get_shoryuken_options['batch'] == true
|
|
37
|
-
fail ArgumentError, "Could not register #{clazz} for
|
|
37
|
+
fail ArgumentError, "Could not register #{clazz} for #{queue}, "\
|
|
38
38
|
"because #{worker_class} is already registered for this queue, "\
|
|
39
39
|
"and Shoryuken doesn't support a batchable worker for a queue with multiple workers"
|
|
40
40
|
end
|
|
@@ -24,7 +24,7 @@ module Shoryuken
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def load
|
|
27
|
-
load_rails if options[:rails]
|
|
27
|
+
load_rails if Shoryuken.options[:rails]
|
|
28
28
|
prefix_active_job_queue_names
|
|
29
29
|
parse_queues
|
|
30
30
|
require_workers
|
|
@@ -42,9 +42,13 @@ module Shoryuken
|
|
|
42
42
|
def config_file_options
|
|
43
43
|
return {} unless (path = options[:config_file])
|
|
44
44
|
|
|
45
|
-
fail ArgumentError, "The supplied config file
|
|
45
|
+
fail ArgumentError, "The supplied config file #{path} does not exist" unless File.exist?(path)
|
|
46
46
|
|
|
47
|
-
YAML.load(ERB.new(IO.read(path)).result)
|
|
47
|
+
if (result = YAML.load(ERB.new(IO.read(path)).result))
|
|
48
|
+
result.deep_symbolize_keys
|
|
49
|
+
else
|
|
50
|
+
{}
|
|
51
|
+
end
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
def initialize_logger
|
|
@@ -62,10 +66,12 @@ module Shoryuken
|
|
|
62
66
|
else
|
|
63
67
|
# Painful contortions, see 1791 for discussion
|
|
64
68
|
require File.expand_path('config/application.rb')
|
|
65
|
-
::Rails::
|
|
66
|
-
::Rails.
|
|
69
|
+
if ::Rails::VERSION::MAJOR == 4
|
|
70
|
+
::Rails::Application.initializer 'shoryuken.eager_load' do
|
|
71
|
+
::Rails.application.config.eager_load = true
|
|
72
|
+
end
|
|
67
73
|
end
|
|
68
|
-
require 'shoryuken/extensions/active_job_adapter' if
|
|
74
|
+
require 'shoryuken/extensions/active_job_adapter' if Shoryuken.active_job?
|
|
69
75
|
require File.expand_path('config/environment.rb')
|
|
70
76
|
end
|
|
71
77
|
end
|
|
@@ -81,28 +87,53 @@ module Shoryuken
|
|
|
81
87
|
end
|
|
82
88
|
end
|
|
83
89
|
|
|
84
|
-
def
|
|
85
|
-
return unless defined? ::ActiveJob
|
|
86
|
-
return unless Shoryuken.active_job_queue_name_prefixing
|
|
87
|
-
|
|
90
|
+
def prefix_active_job_queue_name(queue_name, weight)
|
|
88
91
|
queue_name_prefix = ::ActiveJob::Base.queue_name_prefix
|
|
89
92
|
queue_name_delimiter = ::ActiveJob::Base.queue_name_delimiter
|
|
90
93
|
|
|
91
94
|
# See https://github.com/rails/rails/blob/master/activejob/lib/active_job/queue_name.rb#L27
|
|
95
|
+
name_parts = [queue_name_prefix.presence, queue_name]
|
|
96
|
+
prefixed_queue_name = name_parts.compact.join(queue_name_delimiter)
|
|
97
|
+
[prefixed_queue_name, weight]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def prefix_active_job_queue_names
|
|
101
|
+
return unless Shoryuken.active_job?
|
|
102
|
+
return unless Shoryuken.active_job_queue_name_prefixing
|
|
103
|
+
|
|
92
104
|
Shoryuken.options[:queues].to_a.map! do |queue_name, weight|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
prefix_active_job_queue_name(queue_name, weight)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
Shoryuken.options[:groups].to_a.map! do |group, options|
|
|
109
|
+
if options[:queues]
|
|
110
|
+
options[:queues].map! do |queue_name, weight|
|
|
111
|
+
prefix_active_job_queue_name(queue_name, weight)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
[group, options]
|
|
96
116
|
end
|
|
97
117
|
end
|
|
98
118
|
|
|
99
|
-
def parse_queue(queue, weight
|
|
100
|
-
Shoryuken.add_queue(queue, [weight.to_i, 1].max)
|
|
119
|
+
def parse_queue(queue, weight, group)
|
|
120
|
+
Shoryuken.add_queue(queue, [weight.to_i, 1].max, group)
|
|
101
121
|
end
|
|
102
122
|
|
|
103
123
|
def parse_queues
|
|
104
|
-
Shoryuken.options[:queues].to_a.
|
|
105
|
-
|
|
124
|
+
if Shoryuken.options[:queues].to_a.any?
|
|
125
|
+
Shoryuken.add_group('default', Shoryuken.options.fetch(:concurrency, 25))
|
|
126
|
+
|
|
127
|
+
Shoryuken.options[:queues].to_a.each do |queue, weight|
|
|
128
|
+
parse_queue(queue, weight, 'default')
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
Shoryuken.options[:groups].to_a.each do |group, options|
|
|
133
|
+
Shoryuken.add_group(group, options.fetch(:concurrency, 25))
|
|
134
|
+
options[:queues].to_a.each do |queue, weight|
|
|
135
|
+
parse_queue(queue, weight, group)
|
|
136
|
+
end
|
|
106
137
|
end
|
|
107
138
|
end
|
|
108
139
|
|
|
@@ -119,29 +150,34 @@ module Shoryuken
|
|
|
119
150
|
end
|
|
120
151
|
|
|
121
152
|
def validate_queues
|
|
122
|
-
Shoryuken.logger.warn { 'No queues supplied' } if Shoryuken.
|
|
153
|
+
return Shoryuken.logger.warn { 'No queues supplied' } if Shoryuken.ungrouped_queues.empty?
|
|
123
154
|
|
|
124
155
|
non_existent_queues = []
|
|
125
156
|
|
|
126
|
-
Shoryuken.
|
|
157
|
+
Shoryuken.ungrouped_queues.uniq.each do |queue|
|
|
127
158
|
begin
|
|
128
159
|
Shoryuken::Client.queues(queue)
|
|
129
|
-
rescue Aws::SQS::Errors::NonExistentQueue
|
|
160
|
+
rescue Aws::Errors::NoSuchEndpointError, Aws::SQS::Errors::NonExistentQueue
|
|
130
161
|
non_existent_queues << queue
|
|
131
162
|
end
|
|
132
163
|
end
|
|
133
164
|
|
|
134
|
-
|
|
165
|
+
return if non_existent_queues.none?
|
|
166
|
+
|
|
167
|
+
fail(
|
|
168
|
+
ArgumentError,
|
|
169
|
+
"The specified queue(s) #{non_existent_queues.join(', ')} do not exist.\nTry 'shoryuken sqs create QUEUE-NAME' for creating a queue with default settings"
|
|
170
|
+
)
|
|
135
171
|
end
|
|
136
172
|
|
|
137
173
|
def validate_workers
|
|
138
|
-
return if
|
|
174
|
+
return if Shoryuken.active_job?
|
|
139
175
|
|
|
140
|
-
all_queues = Shoryuken.
|
|
176
|
+
all_queues = Shoryuken.ungrouped_queues
|
|
141
177
|
queues_with_workers = Shoryuken.worker_registry.queues
|
|
142
178
|
|
|
143
179
|
(all_queues - queues_with_workers).each do |queue|
|
|
144
|
-
Shoryuken.logger.warn { "No worker supplied for
|
|
180
|
+
Shoryuken.logger.warn { "No worker supplied for #{queue}" }
|
|
145
181
|
end
|
|
146
182
|
end
|
|
147
183
|
end
|