shoryuken 2.0.11 → 3.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +20 -0
  3. data/.rubocop.yml +8 -2
  4. data/.travis.yml +7 -5
  5. data/CHANGELOG.md +92 -10
  6. data/Gemfile +1 -0
  7. data/README.md +20 -57
  8. data/Rakefile +0 -1
  9. data/bin/cli/base.rb +42 -0
  10. data/bin/cli/sqs.rb +188 -0
  11. data/bin/shoryuken +47 -9
  12. data/examples/default_worker.rb +1 -12
  13. data/lib/shoryuken/client.rb +3 -25
  14. data/lib/shoryuken/default_worker_registry.rb +9 -5
  15. data/lib/shoryuken/environment_loader.rb +29 -67
  16. data/lib/shoryuken/fetcher.rb +22 -53
  17. data/lib/shoryuken/launcher.rb +5 -29
  18. data/lib/shoryuken/manager.rb +72 -184
  19. data/lib/shoryuken/message.rb +4 -13
  20. data/lib/shoryuken/middleware/chain.rb +1 -18
  21. data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +21 -18
  22. data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +26 -19
  23. data/lib/shoryuken/polling.rb +204 -0
  24. data/lib/shoryuken/processor.rb +6 -14
  25. data/lib/shoryuken/queue.rb +36 -38
  26. data/lib/shoryuken/runner.rb +143 -0
  27. data/lib/shoryuken/util.rb +3 -9
  28. data/lib/shoryuken/version.rb +1 -1
  29. data/lib/shoryuken/worker.rb +1 -1
  30. data/lib/shoryuken.rb +78 -39
  31. data/shoryuken.gemspec +6 -6
  32. data/spec/integration/launcher_spec.rb +4 -3
  33. data/spec/shoryuken/client_spec.rb +2 -43
  34. data/spec/shoryuken/default_worker_registry_spec.rb +12 -10
  35. data/spec/shoryuken/environment_loader_spec.rb +34 -0
  36. data/spec/shoryuken/fetcher_spec.rb +18 -52
  37. data/spec/shoryuken/manager_spec.rb +56 -97
  38. data/spec/shoryuken/middleware/chain_spec.rb +0 -24
  39. data/spec/shoryuken/middleware/server/auto_delete_spec.rb +2 -2
  40. data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +7 -3
  41. data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +56 -33
  42. data/spec/shoryuken/polling_spec.rb +239 -0
  43. data/spec/shoryuken/processor_spec.rb +5 -5
  44. data/spec/shoryuken/queue_spec.rb +110 -63
  45. data/spec/shoryuken/{cli_spec.rb → runner_spec.rb} +10 -24
  46. data/spec/shoryuken_spec.rb +13 -1
  47. data/spec/spec_helper.rb +8 -20
  48. data/test_workers/endless_interruptive_worker.rb +41 -0
  49. data/test_workers/endless_uninterruptive_worker.rb +44 -0
  50. metadata +34 -35
  51. data/.hound.yml +0 -6
  52. data/lib/shoryuken/cli.rb +0 -210
  53. data/lib/shoryuken/sns_arn.rb +0 -27
  54. data/lib/shoryuken/topic.rb +0 -17
  55. data/spec/shoryuken/sns_arn_spec.rb +0 -42
  56. data/spec/shoryuken/topic_spec.rb +0 -32
  57. data/spec/shoryuken_endpoint.yml +0 -6
  58. /data/{LICENSE.txt → LICENSE} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9859d893c7542fbdac0224f68e55a8b49f0fd984
4
- data.tar.gz: d3e5419d74c6766c95c4b8ad20333bdced3cfd57
3
+ metadata.gz: 3810f509ebbedaf23dfcd756b17f487097d99ab7
4
+ data.tar.gz: 2dabc0be8dbab5536377b7fe6cb6e371b1c0a71c
5
5
  SHA512:
6
- metadata.gz: cb5725e4def20f4c82a785db1c8a055ba59892979608ce95855888bfbd321c5310f491284a2d6ef319e7bc1b3c1f186d91e376cc6b35faef600c26d4fa453325
7
- data.tar.gz: 15274a15cd3dc7f067d4ae54a40921b90b703e09e2002ed32049ce41f6dd1261629de3b3fa842656d574455f01b13e4558536997c93d4f5ebd05880f1c80b9e2
6
+ metadata.gz: 5fe6096c4df914e64b56dcbcad889bda4827cbf6882ab018d4e8568cab9caf61f548a6117ba3b5607da13abcad049be58c59d8498d76abcc0b6f366579eaa79d
7
+ data.tar.gz: 61c52ec5a273c3294679baff1b00f5938d1aec218f0a304ff7503c2c28f537407306380e8fce9e954ba4caa92113e682289a68de42dd26e3d5515f9714e30d92
data/.codeclimate.yml ADDED
@@ -0,0 +1,20 @@
1
+ ---
2
+ engines:
3
+ reek:
4
+ enabled: true
5
+ duplication:
6
+ enabled: true
7
+ config:
8
+ languages:
9
+ - ruby
10
+ fixme:
11
+ enabled: true
12
+ rubocop:
13
+ enabled: true
14
+ config:
15
+ file: .rubocop.yml
16
+ ratings:
17
+ paths:
18
+ - "**.rb"
19
+ exclude_paths:
20
+ - spec/
data/.rubocop.yml CHANGED
@@ -1,9 +1,12 @@
1
- LineLength:
2
- Max: 120
1
+ AllCops:
2
+ TargetRubyVersion: 2.0
3
3
 
4
4
  Style/SignalException:
5
5
  Enabled: false
6
6
 
7
+ Style/DoubleNegation:
8
+ Enabled: false
9
+
7
10
  Style/SpaceAroundEqualsInParameterDefault:
8
11
  Enabled: false
9
12
 
@@ -28,6 +31,9 @@ Metrics/ClassLength:
28
31
  Metrics/ParameterLists:
29
32
  Enabled: false
30
33
 
34
+ Metrics/LineLength:
35
+ Max: 120
36
+
31
37
  Metrics/MethodLength :
32
38
  Enabled: false
33
39
 
data/.travis.yml CHANGED
@@ -1,13 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- # - 1.9.3
4
- # - 1.9.2
5
3
  - 2.0.0
6
4
  - 2.1.0
7
5
  - 2.2.0
8
- # - ruby-head
9
- # - jruby-19mode
10
- # - jruby-head
6
+ - 2.3.3
11
7
 
12
8
  notifications:
13
9
  email:
@@ -17,3 +13,9 @@ notifications:
17
13
  script: SPEC_ALL=true bundle exec rspec spec
18
14
  before_install:
19
15
  - gem update bundler
16
+ after_success:
17
+ - bundle exec codeclimate-test-reporter
18
+
19
+ addons:
20
+ code_climate:
21
+ repo_token: 7709fd21981bb9d2658647a66d959415a1029a83f1c199573828797944f26c52
data/CHANGELOG.md CHANGED
@@ -1,40 +1,122 @@
1
+ ## [v3.0.0] - 2017-03-12
2
+ - Replace Celluloid with Concurrent Ruby
3
+ - [#291](https://github.com/phstc/shoryuken/pull/291)
4
+
5
+ - Remove AWS configuration from Shoryuken. Now AWS should be configured from outside. Check [this](https://github.com/phstc/shoryuken/wiki/Configure-the-AWS-Client) for more details
6
+ - [#317](https://github.com/phstc/shoryuken/pull/291)
7
+
8
+ - Remove deprecation warnings
9
+ - [#326](https://github.com/phstc/shoryuken/pull/326)
10
+
11
+ - Allow dynamic adding queues
12
+ - [#322](https://github.com/phstc/shoryuken/pull/326)
13
+
14
+ - Support retry_intervals passed in as a lambda. Auto coerce intervals into integer
15
+ - [#329](https://github.com/phstc/shoryuken/pull/329)
16
+
17
+ - Add SQS commands `shoryuken help sqs`, such `ls`, `mv`, `dump` and `requeue`
18
+ - [#330](https://github.com/phstc/shoryuken/pull/330)
19
+
20
+ ## [v2.1.3] - 2017-01-27
21
+ - Show a warn message when batch isn't supported
22
+ - [#302](https://github.com/phstc/shoryuken/pull/302)
23
+
24
+ - Require Celluloid ~> 17
25
+ - [#305](https://github.com/phstc/shoryuken/pull/305)
26
+
27
+ - Fix excessive logging when 0 messages found
28
+ - [#307](https://github.com/phstc/shoryuken/pull/307)
29
+
30
+ ## [v2.1.2] - 2016-12-22
31
+ - Fix loading `logfile` from shoryuken.yml
32
+ - [#296](https://github.com/phstc/shoryuken/pull/296)
33
+
34
+ - Add support for Strict priority polling (pending documentation)
35
+ - [#288](https://github.com/phstc/shoryuken/pull/288)
36
+
37
+ - Add `test_workers` for end-to-end testing supporting
38
+ - [#286](https://github.com/phstc/shoryuken/pull/286)
39
+
40
+ - Update README documenting `configure_client` and `configure_server`
41
+ - [#283](https://github.com/phstc/shoryuken/pull/283)
42
+
43
+ - Fix memory leak caused by async tracking busy threads
44
+ - [#289](https://github.com/phstc/shoryuken/pull/289)
45
+
46
+ - Refactor fetcher, polling strategy and manager
47
+ - [#284](https://github.com/phstc/shoryuken/pull/284)
48
+
49
+ ## [v2.1.1] - 2016-12-05
50
+ - Fix aws deprecation warning message
51
+ - [#279](https://github.com/phstc/shoryuken/pull/279)
52
+
53
+ ## [v2.1.0] - 2016-12-03
54
+ - Fix celluloid "running in BACKPORTED mode" warning
55
+ - [#260](https://github.com/phstc/shoryuken/pull/260)
56
+
57
+ - Allow setting the aws configuration in 'Shoryuken.configure_server'
58
+ - [#252](https://github.com/phstc/shoryuken/pull/252)
59
+
60
+ - Allow requiring a file or dir a through `-r`
61
+ - [#248](https://github.com/phstc/shoryuken/pull/248)
62
+
63
+ - Reduce info log verbosity
64
+ - [#243](https://github.com/phstc/shoryuken/pull/243)
65
+
66
+ - Fix auto extender when using ActiveJob
67
+ - [#3213](https://github.com/phstc/shoryuken/pull/213)
68
+
69
+ - Add FIFO queue support
70
+ - [#272](https://github.com/phstc/shoryuken/issues/272)
71
+
72
+ - Deprecates initialize_aws
73
+ - [#269](https://github.com/phstc/shoryuken/pull/269)
74
+
75
+ - [Other miscellaneous updates](https://github.com/phstc/shoryuken/compare/v2.0.11...v2.1.0)
76
+
77
+ ## [v2.0.11] - 2016-07-02
78
+
79
+ - Same as 2.0.10. Unfortunately 2.0.10 was removed `yanked` by mistake from RubyGems.
80
+ - [#b255bc3](https://github.com/phstc/shoryuken/commit/b255bc3)
81
+
1
82
  ## [v2.0.10] - 2016-06-09
2
83
 
3
84
  - Fix manager #225
4
- - [#226] https://github.com/phstc/shoryuken/pull/226
85
+ - [#226](https://github.com/phstc/shoryuken/pull/226)
5
86
 
6
87
  ## [v2.0.9] - 2016-06-08
7
88
 
8
89
  - Fix daemonization broken in #219
9
- - [#224] https://github.com/phstc/shoryuken/pull/224
90
+ - [#224](https://github.com/phstc/shoryuken/pull/224)
10
91
 
11
92
  ## [v2.0.8] - 2016-06-07
12
93
 
13
94
  - Fix daemonization
14
- - [#223] https://github.com/phstc/shoryuken/pull/223
95
+ - [#223](https://github.com/phstc/shoryuken/pull/223)
15
96
 
16
97
  ## [v2.0.7] - 2016-06-06
17
98
 
18
99
  - Daemonize before loading environment
19
- - [#219] https://github.com/phstc/shoryuken/pull/219
100
+ - [#219](https://github.com/phstc/shoryuken/pull/219)
20
101
 
21
102
  - Fix initialization when using rails
22
- - [#197] https://github.com/phstc/shoryuken/pull/197
103
+ - [#197](https://github.com/phstc/shoryuken/pull/197)
23
104
 
24
105
  - Improve message fetching
25
- - https://github.com/phstc/shoryuken/pull/214 and https://github.com/phstc/shoryuken/commit/f4640d97950c1783a061195855d93994725ed64a
106
+ - [#214](https://github.com/phstc/shoryuken/pull/214)
107
+ - [#f4640d9](https://github.com/phstc/shoryuken/commit/f4640d9)
26
108
 
27
109
  - Fix hard shutdown if there are some busy workers when signal received
28
- - [#215] https://github.com/phstc/shoryuken/pull/215
110
+ - [#215](https://github.com/phstc/shoryuken/pull/215)
29
111
 
30
112
  - Fix `rake console` task
31
- - [#208] https://github.com/phstc/shoryuken/pull/208
113
+ - [#208](https://github.com/phstc/shoryuken/pull/208)
32
114
 
33
115
  - Isolate `MessageVisibilityExtender` as new middleware
34
- - [#199] https://github.com/phstc/shoryuken/pull/190
116
+ - [#199](https://github.com/phstc/shoryuken/pull/190)
35
117
 
36
118
  - Fail on non-existent queues
37
- - [#196] https://github.com/phstc/shoryuken/pull/196
119
+ - [#196](https://github.com/phstc/shoryuken/pull/196)
38
120
 
39
121
  ## [v2.0.6] - 2016-04-18
40
122
 
data/Gemfile CHANGED
@@ -5,5 +5,6 @@ gemspec
5
5
 
6
6
  group :test do
7
7
  gem 'codeclimate-test-reporter', require: nil
8
+ gem 'simplecov'
8
9
  gem 'multi_xml'
9
10
  end
data/README.md CHANGED
@@ -20,18 +20,18 @@ concurrency: 25
20
20
  delay: 25
21
21
  queues:
22
22
  - [high_priority, 6]
23
- - [default, 2]
23
+ - [normal_priority, 2]
24
24
  - [low_priority, 1]
25
25
  ```
26
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 `default` and 6 times more than `low_priority`,
28
- splitting the work among the `concurrency: 25` available processors.
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
29
 
30
- 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
+ If `high_priority` gets empty, Shoryuken will keep using the 25 processors, but only to process `normal_priority` and `low_priority`.
31
31
 
32
- 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
+ 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
33
 
34
- 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
+ [If a queue gets empty, Shoryuken will pause checking it for `delay: 25`](https://github.com/phstc/shoryuken/wiki/Shoryuken-options#delay).
35
35
 
36
36
 
37
37
  ### Fetch in batches
@@ -40,7 +40,7 @@ To be even more performant and cost effective, Shoryuken fetches SQS messages in
40
40
 
41
41
  ## Requirements
42
42
 
43
- Ruby 2.0 or greater. Ruby 1.9 is no longer supported.
43
+ Ruby 2.0 or greater.
44
44
 
45
45
  ## Installation
46
46
 
@@ -105,49 +105,22 @@ end
105
105
 
106
106
  [Check the Middleware documentation](https://github.com/phstc/shoryuken/wiki/Middleware).
107
107
 
108
- ### Configuration (worker side)
108
+ ### Shoryuken Configuration
109
109
 
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:
125
116
  - [high_priority, 6]
126
- - [default, 2]
117
+ - [normal_priority, 2]
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
+ #### AWS Configuration
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
136
-
137
- The ```sns_endpoint``` and ```sqs_endpoint``` Shoryuken-specific options will also fallback to the environment variables ```AWS_SNS_ENDPOINT``` and ```AWS_SQS_ENDPOINT``` respectively, if they are set.
138
-
139
- ### Configuration (producer side)
140
-
141
- 'Producer' processes need permissions to put messages into SQS. There are a few ways:
142
-
143
- * Ensure the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` env vars are set.
144
- * Create a `~/.aws/credentials` file.
145
- * Set `Aws.config[:credentials]` from Ruby code (e.g. in a Rails initializer)
146
- * Use the Instance Profiles feature. The IAM role of the targeted machine must have an adequate SQS Policy.
147
-
148
- Note that storing your credentials into Amazon instances represents a security risk. Instance Profiles tends to be the best choice.
149
-
150
- You can read about these in more detail [here](http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Client.html).
123
+ [Check the Configure AWS Client documentation](https://github.com/phstc/shoryuken/wiki/Configure-the-AWS-Client)
151
124
 
152
125
  ### Rails Integration
153
126
 
@@ -159,26 +132,16 @@ You can read about these in more detail [here](http://docs.aws.amazon.com/sdkfor
159
132
  bundle exec shoryuken -r worker.rb -C shoryuken.yml
160
133
  ```
161
134
 
162
- Other options:
163
-
164
- ```bash
165
- shoryuken --help
166
-
167
- shoryuken [options]
168
- -c, --concurrency INT Processor threads to use
169
- -d, --daemon Daemonize process
170
- -q, --queue QUEUE[,WEIGHT]... Queues to process with optional weights
171
- -r, --require [PATH|DIR] Location of the worker
172
- -C, --config PATH Path to YAML config file
173
- -R, --rails Attempts to load the containing Rails project
174
- -L, --logfile PATH Path to writable logfile
175
- -P, --pidfile PATH Path to pidfile
176
- -v, --verbose Print more verbose output
177
- -V, --version Print version and exit
178
- -h, --help Show help
179
- ...
180
- ```
135
+ For other options check `bundle exec shoryuken help start`
136
+
137
+ #### SQS commands
138
+
139
+ Check also some available SQS commands `bundle exec shoryuken help sqs`, such as:
181
140
 
141
+ - `ls` list queues
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
182
145
 
183
146
  ## More Information
184
147
 
data/Rakefile CHANGED
@@ -10,7 +10,6 @@ end
10
10
  desc 'Open Shoryuken pry console'
11
11
  task :console do
12
12
  require 'pry'
13
- require 'celluloid'
14
13
  require 'shoryuken'
15
14
 
16
15
  config_file = File.join File.expand_path('..', __FILE__), 'shoryuken.yml'
data/bin/cli/base.rb ADDED
@@ -0,0 +1,42 @@
1
+ # rubocop:disable Metrics/BlockLength
2
+ module Shoryuken
3
+ module CLI
4
+ class Base < Thor
5
+ no_commands do
6
+ def print_table(entries)
7
+ column_sizes = print_columns_size(entries)
8
+
9
+ entries.map do |entry|
10
+ puts entry.map.with_index { |e, i| print_format_column(e, column_sizes[i]) }.join
11
+ end
12
+ end
13
+
14
+ def print_columns_size(entries)
15
+ column_sizes = Hash.new(0)
16
+
17
+ entries.each do |entry|
18
+ entry.each_with_index do |e, i|
19
+ e = e.to_s
20
+ column_sizes[i] = e.size if column_sizes[i] < e.size
21
+ end
22
+ end
23
+
24
+ column_sizes
25
+ end
26
+
27
+ def print_format_column(column, size)
28
+ size = 40 if size > 40
29
+ size_with_padding = size + 4
30
+ column = column.to_s.ljust(size_with_padding)
31
+ column = "#{column[0...size - 2]}.." if column.size > size_with_padding
32
+ column
33
+ end
34
+
35
+ def fail_task(msg, quit = true)
36
+ say "[FAIL] #{msg}", :red
37
+ exit(1) if quit
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
data/bin/cli/sqs.rb ADDED
@@ -0,0 +1,188 @@
1
+ require 'date'
2
+
3
+ # rubocop:disable Metrics/AbcSize, Metrics/BlockLength
4
+ module Shoryuken
5
+ module CLI
6
+ class SQS < Base
7
+ namespace :sqs
8
+
9
+ no_commands do
10
+ def normalize_dump_message(message)
11
+ message[:id] = message.delete(:message_id)
12
+ message[:message_body] = message.delete(:body)
13
+ message.delete(:receipt_handle)
14
+ message.delete(:md5_of_body)
15
+ message.delete(:md5_of_message_attributes)
16
+ message
17
+ end
18
+
19
+ def sqs
20
+ @_sqs ||= Aws::SQS::Client.new
21
+ end
22
+
23
+ def find_queue_url(queue_name_prefix)
24
+ urls = sqs.list_queues(queue_name_prefix: queue_name_prefix).queue_urls
25
+
26
+ if urls.size > 1
27
+ fail_task "There's more than one queue starting with #{queue_name_prefix}: #{urls.join(', ')}"
28
+ end
29
+
30
+ url = urls.first
31
+
32
+ fail_task "Queue #{queue_name_prefix} not found" unless url
33
+
34
+ url
35
+ end
36
+
37
+ def batch_delete(url, messages)
38
+ messages.to_a.flatten.each_slice(10) do |batch|
39
+ sqs.delete_message_batch(
40
+ queue_url: url,
41
+ entries: batch.map { |message| { id: message.message_id, receipt_handle: message.receipt_handle } }
42
+ ).failed.any? do |failure|
43
+ say "Could not delete #{failure.id}, code: #{failure.code}", :yellow
44
+ end
45
+ end
46
+ end
47
+
48
+ def batch_send(url, messages)
49
+ messages.to_a.flatten.map(&method(:normalize_dump_message)).each_slice(10) do |batch|
50
+ sqs.send_message_batch(queue_url: url, entries: batch).failed.any? do |failure|
51
+ say "Could not requeue #{failure.id}, code: #{failure.code}", :yellow
52
+ end
53
+ end
54
+ end
55
+
56
+ def find_all(url, limit, &block)
57
+ count = 0
58
+ batch_size = limit > 10 ? 10 : limit
59
+
60
+ loop do
61
+ n = limit - count
62
+ batch_size = n if n < batch_size
63
+
64
+ messages = sqs.receive_message(
65
+ queue_url: url,
66
+ max_number_of_messages: batch_size,
67
+ message_attribute_names: ['All']
68
+ ).messages
69
+
70
+ messages.each { |m| yield m }
71
+
72
+ count += messages.size
73
+
74
+ break if count >= limit
75
+ break if messages.empty?
76
+ end
77
+
78
+ count
79
+ end
80
+
81
+ def list_and_print_queues(urls)
82
+ attrs = %w(QueueArn ApproximateNumberOfMessages ApproximateNumberOfMessagesNotVisible LastModifiedTimestamp)
83
+
84
+ entries = urls.map { |u| sqs.get_queue_attributes(queue_url: u, attribute_names: attrs).attributes }.map do |q|
85
+ [
86
+ q['QueueArn'].split(':').last,
87
+ q['ApproximateNumberOfMessages'],
88
+ q['ApproximateNumberOfMessagesNotVisible'],
89
+ Time.at(q['LastModifiedTimestamp'].to_i)
90
+ ]
91
+ end
92
+
93
+ entries.unshift(['Queue', 'Messages Available', 'Messages Inflight', 'Last Modified'])
94
+
95
+ print_table(entries)
96
+ end
97
+
98
+ def dump_file(path, queue_name)
99
+ File.join(path, "#{queue_name}-#{Date.today}.jsonl")
100
+ end
101
+ end
102
+
103
+ desc 'ls [QUEUE-NAME-PREFIX]', 'List queues'
104
+ method_option :watch, aliases: '-w', type: :boolean, desc: 'watch queues'
105
+ method_option :watch_interval, type: :numeric, default: 10, desc: 'watch interval'
106
+ def ls(queue_name_prefix = '')
107
+ trap('SIGINT', 'EXIT') # expect ctrl-c from loop
108
+
109
+ urls = sqs.list_queues(queue_name_prefix: queue_name_prefix).queue_urls
110
+
111
+ loop do
112
+ list_and_print_queues(urls)
113
+
114
+ break unless options.watch
115
+
116
+ sleep options.watch_interval
117
+ puts
118
+ end
119
+ end
120
+
121
+ desc 'dump QUEUE-NAME', 'Dump messages from a queue into a JSON lines file'
122
+ method_option :number, aliases: '-n', type: :numeric, default: Float::INFINITY, desc: 'number of messages to dump'
123
+ method_option :path, aliases: '-p', type: :string, default: './', desc: 'path to save the dump file'
124
+ method_option :delete, aliases: '-d', type: :boolean, default: true, desc: 'delete from the queue'
125
+ def dump(queue_name)
126
+ path = dump_file(options.path, queue_name)
127
+
128
+ fail_task "File #{path} already exists" if File.exist?(path)
129
+
130
+ url = find_queue_url(queue_name)
131
+
132
+ messages = []
133
+
134
+ file = nil
135
+
136
+ count = find_all(url, options.number) do |m|
137
+ file ||= File.open(path, 'w')
138
+
139
+ file.puts(JSON.dump(m.to_h))
140
+
141
+ messages << m if options.delete
142
+ end
143
+
144
+ batch_delete(url, messages) if options.delete
145
+
146
+ if count.zero?
147
+ say "Queue #{queue_name} is empty", :yellow
148
+ else
149
+ say "Dump saved in #{path} with #{count} messages", :green
150
+ end
151
+ ensure
152
+ file.close if file
153
+ end
154
+
155
+ desc 'requeue QUEUE-NAME PATH', 'Requeue messages from a dump file'
156
+ def requeue(queue_name, path)
157
+ fail_task "Path #{path} not found" unless File.exist?(path)
158
+
159
+ messages = File.readlines(path).map { |line| JSON.parse(line, symbolize_names: true) }
160
+
161
+ batch_send(find_queue_url(queue_name), messages)
162
+
163
+ say "Requeued #{messages.size} messages from #{path} to #{queue_name}", :green
164
+ end
165
+
166
+ desc 'mv QUEUE-NAME-SOURCE QUEUE-NAME-TARGET', 'Move messages from one queue (source) to another (target)'
167
+ method_option :number, aliases: '-n', type: :numeric, default: Float::INFINITY, desc: 'number of messages to move'
168
+ method_option :delete, aliases: '-d', type: :boolean, default: true, desc: 'delete from the queue'
169
+ def mv(queue_name_source, queue_name_target)
170
+ url_source = find_queue_url(queue_name_source)
171
+ messages = []
172
+
173
+ count = find_all(url_source, options.number) do |m|
174
+ messages << m
175
+ end
176
+
177
+ batch_send(find_queue_url(queue_name_target), messages.map(&:to_h))
178
+ batch_delete(url_source, messages) if options.delete
179
+
180
+ if count.zero?
181
+ say "Queue #{queue_name_source} is empty", :yellow
182
+ else
183
+ say "Moved #{count} messages from #{queue_name_source} to #{queue_name_target}", :green
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
data/bin/shoryuken CHANGED
@@ -1,12 +1,50 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require_relative '../lib/shoryuken/cli'
4
-
5
- begin
6
- Shoryuken::CLI.instance.run(ARGV)
7
- rescue => e
8
- raise e if $DEBUG
9
- STDERR.puts e.message
10
- STDERR.puts e.backtrace.join("\n")
11
- exit 1
3
+ require 'rubygems'
4
+
5
+ require 'thor'
6
+ require 'aws-sdk-core'
7
+ require_relative 'cli/base'
8
+ require_relative 'cli/sqs'
9
+ require_relative '../lib/shoryuken/runner'
10
+
11
+ # rubocop:disable Metrics/AbcSize
12
+ module Shoryuken
13
+ module CLI
14
+ class Runner < Base
15
+ default_task :start
16
+
17
+ register(Shoryuken::CLI::SQS, 'sqs', 'sqs COMMAND', 'SQS commands')
18
+
19
+ desc 'start', 'Start shoryuken'
20
+ method_option :concurrency, aliases: '-c', type: :numeric, desc: 'Processor threads to use'
21
+ method_option :daemon, aliases: '-d', type: :boolean, desc: 'Daemonize process'
22
+ method_option :queues, aliases: '-q', type: :array, desc: 'Queues to process with optional weights'
23
+ method_option :require, aliases: '-r', type: :string, desc: 'Dir or path of the workers'
24
+ method_option :config_file, aliases: '-C', type: :string, desc: 'Path to config file'
25
+ method_option :rails, aliases: '-R', type: :boolean, desc: 'Load Rails'
26
+ method_option :logfile, aliases: '-L', type: :string, desc: 'Path to logfile'
27
+ method_option :pidfile, aliases: '-P', type: :string, desc: 'Path to pidfile'
28
+ method_option :verbose, aliases: '-v', type: :boolean, desc: 'Print more verbose output'
29
+ def start
30
+ opts = options.to_h.symbolize_keys
31
+
32
+ # Keep compatibility with old CLI queue format
33
+ opts[:queues] = options.queues.map { |q| q.split(',') } if options.queues
34
+
35
+ if options.daemon && options.logfile.nil?
36
+ fail_task "You should set a logfile if you're going to daemonize"
37
+ end
38
+
39
+ Shoryuken::Runner.instance.run(opts.freeze)
40
+ end
41
+
42
+ desc 'version', 'Print version'
43
+ def version
44
+ say "Shoryuken #{Shoryuken::VERSION}"
45
+ end
46
+ end
47
+ end
12
48
  end
49
+
50
+ Shoryuken::CLI::Runner.start
@@ -4,17 +4,6 @@ class DefaultWorker
4
4
  shoryuken_options queue: 'default', auto_delete: true
5
5
 
6
6
  def perform(sqs_msg, body)
7
- puts "DefaultWorker: '#{body}'"
8
- end
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}'"
7
+ Shoryuken.logger.debug("Received message: '#{body}'")
19
8
  end
20
9
  end