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
@@ -1,40 +1,18 @@
1
1
  module Shoryuken
2
2
  class Client
3
3
  @@queues = {}
4
- @@topics = {}
5
4
 
6
5
  class << self
7
6
  def queues(name)
8
7
  @@queues[name.to_s] ||= Shoryuken::Queue.new(sqs, name)
9
8
  end
10
9
 
11
- def sns
12
- @sns ||= Aws::SNS::Client.new(aws_client_options(:sns_endpoint))
13
- end
14
-
15
- def sns_arn
16
- @sns_arn ||= SnsArn
17
- end
18
-
19
10
  def sqs
20
- @sqs ||= Aws::SQS::Client.new(aws_client_options(:sqs_endpoint))
11
+ @@sqs ||= Shoryuken.sqs_client
21
12
  end
22
13
 
23
- def topics(name)
24
- @@topics[name.to_s] ||= Topic.new(name, sns)
25
- end
26
-
27
- attr_accessor :account_id
28
- attr_writer :sns, :sqs, :sqs_resource, :sns_arn
29
-
30
- private
31
-
32
- def aws_client_options(service_endpoint_key)
33
- environment_endpoint = ENV["AWS_#{service_endpoint_key.to_s.upcase}"]
34
- explicit_endpoint = Shoryuken.options[:aws][service_endpoint_key] || environment_endpoint
35
- options = {}
36
- options[:endpoint] = explicit_endpoint unless explicit_endpoint.to_s.empty?
37
- options
14
+ def sqs=(sqs)
15
+ @@sqs = sqs
38
16
  end
39
17
  end
40
18
  end
@@ -14,11 +14,15 @@ module Shoryuken
14
14
 
15
15
  def fetch_worker(queue, message)
16
16
  worker_class = !message.is_a?(Array) &&
17
- message.message_attributes &&
18
- message.message_attributes['shoryuken_class'] &&
19
- message.message_attributes['shoryuken_class'][:string_value]
20
-
21
- worker_class = (worker_class.constantize rescue nil) || @workers[queue]
17
+ message.message_attributes &&
18
+ message.message_attributes['shoryuken_class'] &&
19
+ message.message_attributes['shoryuken_class'][:string_value]
20
+
21
+ worker_class = begin
22
+ worker_class.constantize
23
+ rescue
24
+ @workers[queue]
25
+ end
22
26
 
23
27
  worker_class.new
24
28
  end
@@ -2,30 +2,34 @@ module Shoryuken
2
2
  class EnvironmentLoader
3
3
  attr_reader :options
4
4
 
5
- def self.load(options)
6
- new(options).load
5
+ def self.setup_options(options)
6
+ instance = new(options)
7
+ instance.setup_options
8
+ instance
7
9
  end
8
10
 
9
11
  def self.load_for_rails_console
10
- load(config_file: (Rails.root + 'config' + 'shoryuken.yml'))
12
+ instance = setup_options(config_file: (Rails.root + 'config' + 'shoryuken.yml'))
13
+ instance.load
11
14
  end
12
15
 
13
16
  def initialize(options)
14
17
  @options = options
15
18
  end
16
19
 
17
- def load
18
- load_rails if options[:rails]
20
+ def setup_options
19
21
  initialize_options
20
22
  initialize_logger
21
23
  merge_cli_defined_queues
24
+ end
25
+
26
+ def load
27
+ load_rails if options[:rails]
22
28
  prefix_active_job_queue_names
23
29
  parse_queues
24
30
  require_workers
25
- initialize_aws
26
31
  validate_queues
27
32
  validate_workers
28
- patch_deprecated_workers
29
33
  end
30
34
 
31
35
  private
@@ -43,40 +47,9 @@ module Shoryuken
43
47
  YAML.load(ERB.new(IO.read(path)).result).deep_symbolize_keys
44
48
  end
45
49
 
46
- def initialize_aws
47
- # aws-sdk tries to load the credentials from the ENV variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
48
- # when not explicit supplied
49
- return if Shoryuken.options[:aws].empty?
50
-
51
- shoryuken_keys = %w(
52
- account_id
53
- sns_endpoint
54
- sqs_endpoint
55
- receive_message).map(&:to_sym)
56
-
57
- aws_options = Shoryuken.options[:aws].reject do |k, v|
58
- shoryuken_keys.include?(k)
59
- end
60
-
61
- # assume credentials based authentication
62
- credentials = Aws::Credentials.new(
63
- aws_options.delete(:access_key_id),
64
- aws_options.delete(:secret_access_key))
65
-
66
- # but only if the configuration options have valid values
67
- aws_options = aws_options.merge(credentials: credentials) if credentials.set?
68
-
69
- if (callback = Shoryuken.aws_initialization_callback)
70
- Shoryuken.logger.info { 'Calling Shoryuken.on_aws_initialization block' }
71
- callback.call(aws_options)
72
- end
73
-
74
- Aws.config = aws_options
75
- end
76
-
77
50
  def initialize_logger
78
- Shoryuken::Logging.initialize_logger(options[:logfile]) if options[:logfile]
79
- Shoryuken.logger.level = Logger::DEBUG if options[:verbose]
51
+ Shoryuken::Logging.initialize_logger(Shoryuken.options[:logfile]) if Shoryuken.options[:logfile]
52
+ Shoryuken.logger.level = Logger::DEBUG if Shoryuken.options[:verbose]
80
53
  end
81
54
 
82
55
  def load_rails
@@ -98,12 +71,11 @@ module Shoryuken
98
71
  end
99
72
 
100
73
  def merge_cli_defined_queues
101
- cli_defined_queues = options.delete(:queues) || []
74
+ cli_defined_queues = options[:queues].to_a
102
75
 
103
76
  cli_defined_queues.each do |cli_defined_queue|
104
- Shoryuken.options[:queues].delete_if do |config_file_queue|
105
- config_file_queue[0] == cli_defined_queue[0]
106
- end
77
+ # CLI defined queues override config_file defined queues
78
+ Shoryuken.options[:queues].delete_if { |config_file_queue| config_file_queue[0] == cli_defined_queue[0] }
107
79
 
108
80
  Shoryuken.options[:queues] << cli_defined_queue
109
81
  end
@@ -125,7 +97,7 @@ module Shoryuken
125
97
  end
126
98
 
127
99
  def parse_queue(queue, weight = nil)
128
- [weight.to_i, 1].max.times { Shoryuken.queues << queue }
100
+ Shoryuken.add_queue(queue, [weight.to_i, 1].max)
129
101
  end
130
102
 
131
103
  def parse_queues
@@ -134,28 +106,18 @@ module Shoryuken
134
106
  end
135
107
  end
136
108
 
137
- def patch_deprecated_workers
138
- Shoryuken.worker_registry.queues.each do |queue|
139
- Shoryuken.worker_registry.workers(queue).each do |worker_class|
140
- if worker_class.instance_method(:perform).arity == 1
141
- Shoryuken.logger.warn { "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)" }
109
+ def require_workers
110
+ required = Shoryuken.options[:require]
142
111
 
143
- worker_class.class_eval do
144
- alias_method :deprecated_perform, :perform
112
+ return unless required
145
113
 
146
- def perform(sqs_msg, body = nil)
147
- deprecated_perform(sqs_msg)
148
- end
149
- end
150
- end
151
- end
114
+ if File.directory?(required)
115
+ Dir[File.join(required, '**', '*.rb')].each(&method(:require))
116
+ else
117
+ require required
152
118
  end
153
119
  end
154
120
 
155
- def require_workers
156
- require Shoryuken.options[:require] if Shoryuken.options[:require]
157
- end
158
-
159
121
  def validate_queues
160
122
  Shoryuken.logger.warn { 'No queues supplied' } if Shoryuken.queues.empty?
161
123
 
@@ -163,7 +125,7 @@ module Shoryuken
163
125
 
164
126
  Shoryuken.queues.uniq.each do |queue|
165
127
  begin
166
- Shoryuken::Client.queues queue
128
+ Shoryuken::Client.queues(queue)
167
129
  rescue Aws::SQS::Errors::NonExistentQueue
168
130
  non_existent_queues << queue
169
131
  end
@@ -173,13 +135,13 @@ module Shoryuken
173
135
  end
174
136
 
175
137
  def validate_workers
138
+ return if defined?(::ActiveJob)
139
+
176
140
  all_queues = Shoryuken.queues
177
141
  queues_with_workers = Shoryuken.worker_registry.queues
178
142
 
179
- unless defined?(::ActiveJob)
180
- (all_queues - queues_with_workers).each do |queue|
181
- Shoryuken.logger.warn { "No worker supplied for '#{queue}'" }
182
- end
143
+ (all_queues - queues_with_workers).each do |queue|
144
+ Shoryuken.logger.warn { "No worker supplied for '#{queue}'" }
183
145
  end
184
146
  end
185
147
  end
@@ -1,73 +1,42 @@
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
7
+ def fetch(queue, available_processors)
8
+ started_at = Time.now
9
+
10
+ logger.debug { "Looking for new messages in '#{queue}'" }
11
+
12
+ begin
13
+ limit = available_processors > FETCH_LIMIT ? FETCH_LIMIT : available_processors
14
+
15
+ sqs_msgs = Array(receive_messages(queue, limit))
16
+ logger.info { "Found #{sqs_msgs.size} messages for '#{queue.name}'" } unless sqs_msgs.empty?
17
+ logger.debug { "Fetcher for '#{queue}' completed in #{elapsed(started_at)} ms" }
18
+ sqs_msgs
19
+ rescue => ex
20
+ logger.error { "Error fetching message: #{ex}" }
21
+ logger.error { ex.backtrace.first }
22
+ []
23
+ end
10
24
  end
11
25
 
26
+ private
27
+
12
28
  def receive_messages(queue, limit)
13
29
  # AWS limits the batch size by 10
14
30
  limit = limit > FETCH_LIMIT ? FETCH_LIMIT : limit
15
31
 
16
- options = (Shoryuken.options[:aws][:receive_message] || {}).dup
32
+ options = Shoryuken.sqs_client_receive_message_opts.to_h.dup
17
33
  options[:max_number_of_messages] = limit
18
34
  options[:message_attribute_names] = %w(All)
19
35
  options[:attribute_names] = %w(All)
20
36
 
21
- Shoryuken::Client.queues(queue).receive_messages options
22
- end
23
-
24
- def fetch(queue, available_processors)
25
- watchdog('Fetcher#fetch died') do
26
- started_at = Time.now
27
-
28
- logger.debug { "Looking for new messages in '#{queue}'" }
29
-
30
- 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.info { "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
49
-
50
- logger.debug { "Fetcher for '#{queue}' completed in #{elapsed(started_at)} ms" }
51
- rescue => ex
52
- logger.error { "Error fetching message: #{ex}" }
53
- logger.error { ex.backtrace.first }
54
- end
55
-
56
- @manager.async.dispatch
57
- end
58
-
59
- end
60
-
61
- private
62
-
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
37
+ options.merge!(queue.options)
69
38
 
70
- sqs_msgs
39
+ Shoryuken::Client.queues(queue.name).receive_messages(options)
71
40
  end
72
41
  end
73
42
  end
@@ -1,43 +1,19 @@
1
1
  module Shoryuken
2
2
  class Launcher
3
- include Celluloid
4
3
  include Util
5
4
 
6
- trap_exit :actor_died
7
-
8
- attr_accessor :manager
9
-
10
5
  def initialize
11
- @condvar = Celluloid::Condition.new
12
- @manager = Shoryuken::Manager.new_link(@condvar)
13
- @fetcher = Shoryuken::Fetcher.new_link(manager)
14
-
15
- @done = false
16
-
17
- manager.fetcher = @fetcher
6
+ @manager = Shoryuken::Manager.new(Shoryuken::Fetcher.new,
7
+ Shoryuken.options[:polling_strategy].new(Shoryuken.queues))
18
8
  end
19
9
 
20
10
  def stop(options = {})
21
- watchdog('Launcher#stop') do
22
- @done = true
23
- @fetcher.terminate if @fetcher.alive?
24
-
25
- manager.async.stop(shutdown: !!options[:shutdown], timeout: Shoryuken.options[:timeout])
26
- @condvar.wait
27
- manager.terminate
28
- end
11
+ @manager.stop(shutdown: !options[:shutdown].nil?,
12
+ timeout: Shoryuken.options[:timeout])
29
13
  end
30
14
 
31
15
  def run
32
- watchdog('Launcher#run') do
33
- manager.async.start
34
- end
35
- end
36
-
37
- def actor_died(actor, reason)
38
- return if @done
39
- logger.warn { 'Shoryuken died due to the following error, cannot recover, process exiting' }
40
- exit 1
16
+ @manager.start
41
17
  end
42
18
  end
43
19
  end