shoryuken 0.0.5 → 1.0.0

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: 93f2193b2c553192ca3d38d1dbcd981bbaa35eeb
4
- data.tar.gz: d46dcc643bb7fffeb78f4e2f6ca7f4e1b236d688
3
+ metadata.gz: 9072ce45c134cd9563ee7dddac27bc7d19733cde
4
+ data.tar.gz: 2b48cb3b0d594ef1111456d0f9101ce154056995
5
5
  SHA512:
6
- metadata.gz: 5827e0327eba8f21ab4156d197ea0fd5a8587aa86afda60d14ffc9269e5606aa9688060d59d711821bc117d69ae49a41c0837aaea09433dbb3081fc66b36b9d3
7
- data.tar.gz: fcc6b86772574af993cf9145f4d1b0f5bd3e4900937f13c2328ab7dd292dc9bbe1c9e502ea5826220734f091a6ae9d759c0ec1e836c10ce85858925071d94f01
6
+ metadata.gz: 62bde57b564da861f90c2a68816a69743766f1120dd146817f884b2d4d2e5e5119dcef8858516b7d2581517a04177ec0edb151a97f254c6185ae9092c96efab2
7
+ data.tar.gz: e011b7d261eaf6e4b87d7dc322d2f458da09fdcd5074eac90eedb8101c082b2987f303b557ba20d90c89b321a7d4df7d613852890b6888263ccbc8adb1bf77db
data/.gitignore CHANGED
@@ -23,3 +23,4 @@ mkmf.log
23
23
  shoryuken.yml
24
24
  *.pid
25
25
  *.log
26
+ .env
data/.hound.yml ADDED
@@ -0,0 +1,2 @@
1
+ StringLiterals:
2
+ EnforcedStyle: single_quotes
data/.travis.yml CHANGED
@@ -4,6 +4,7 @@ rvm:
4
4
  # - 1.9.2
5
5
  - 2.0.0
6
6
  - 2.1.0
7
+ - 2.2.0
7
8
  # - ruby-head
8
9
  # - jruby-19mode
9
10
  # - jruby-head
@@ -13,4 +14,4 @@ notifications:
13
14
  on_success: change
14
15
  on_failure: always
15
16
 
16
- script: bundle exec rspec spec
17
+ script: SPEC_ALL=true bundle exec rspec spec
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in shoryuken.gemspec
4
4
  gemspec
5
+
6
+ gem 'codeclimate-test-reporter', group: :test, require: nil
data/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  Shoryuken _sho-ryu-ken_ is a super-efficient [AWS SQS](https://aws.amazon.com/sqs/) thread-based message processor.
6
6
 
7
7
  [![Build Status](https://travis-ci.org/phstc/shoryuken.svg)](https://travis-ci.org/phstc/shoryuken)
8
+ [![Code Climate](https://codeclimate.com/github/phstc/shoryuken/badges/gpa.svg)](https://codeclimate.com/github/phstc/shoryuken)
8
9
 
9
10
  ## Key features
10
11
 
@@ -15,8 +16,8 @@ Yeah, Shoryuken load balances the messages consumption!
15
16
  Given this configuration:
16
17
 
17
18
  ```yaml
18
- concurrency: 25,
19
- delay: 25,
19
+ concurrency: 25
20
+ delay: 25
20
21
  queues:
21
22
  - [high_priority, 6]
22
23
  - [default, 2]
@@ -37,6 +38,10 @@ If all queues get empty, all processors will be changed to the waiting state and
37
38
 
38
39
  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.
39
40
 
41
+ ## Requirements
42
+
43
+ Ruby 2.0 or greater. Ruby 1.9 is no longer supported.
44
+
40
45
  ## Installation
41
46
 
42
47
  Add this line to your application's Gemfile:
@@ -109,19 +114,26 @@ aws:
109
114
  access_key_id: ... # or <%= ENV['AWS_ACCESS_KEY_ID'] %>
110
115
  secret_access_key: ... # or <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
111
116
  region: us-east-1 # or <%= ENV['AWS_REGION'] %>
112
- receive_message: # See http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SQS/Queue.html#receive_message-instance_method
117
+ receive_message: # See http://docs.aws.amazon.com/sdkforruby/api/Aws/SQS/Queue.html#receive_messages-instance_method
113
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
114
- attributes:
115
- - receive_count
116
- - sent_at
117
- concurrency: 25, # The number of allocated threads to process messages. Default 25
118
- delay: 25, # The delay in seconds to pause a queue when it's empty. Default 0
119
+ attribute_names:
120
+ - ApproximateReceiveCount
121
+ - SentTimestamp
122
+ concurrency: 25 # The number of allocated threads to process messages. Default 25
123
+ delay: 25 # The delay in seconds to pause a queue when it's empty. Default 0
119
124
  queues:
120
125
  - [high_priority, 6]
121
126
  - [default, 2]
122
127
  - [low_priority, 1]
123
128
  ```
124
129
 
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```:
131
+
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/Queue.html#receive_messages-instance_method
136
+
125
137
  ### Rails Integration
126
138
 
127
139
  You can tell Shoryuken to load your Rails application by passing the `-R` or `--rails` flag to the "shoryuken" command.
data/Rakefile CHANGED
@@ -11,11 +11,11 @@ task :console do
11
11
  if File.exist? config_file
12
12
  config = YAML.load File.read(config_file)
13
13
 
14
- AWS.config(config['aws'])
14
+ Aws.config = config['aws']
15
15
  end
16
16
 
17
17
  def push(queue, message)
18
- Shoryuken::Client.queues(queue).send_message message
18
+ Shoryuken::Client.queues(queue).send_message(message_body: message)
19
19
  end
20
20
 
21
21
  ARGV.clear
@@ -4,17 +4,25 @@ require 'shoryuken'
4
4
  # load SQS credentials
5
5
  config = YAML.load File.read(File.join(File.expand_path('..', __FILE__), 'shoryuken.yml'))
6
6
 
7
- AWS.config(config['aws'])
7
+ Aws.config = config['aws']
8
8
 
9
- sqs = AWS::SQS.new
9
+ sqs = Aws::SQS::Client.new
10
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
11
+ default_queue_url = sqs.create_queue(queue_name: 'default').queue_url
13
12
 
14
- dl_name = 'default_failures'
15
- dl = sqs.queues.create(dl_name)
13
+ if sqs.config['endpoint'] =~ /amazonaws.com/
14
+ # create a dead letter queue
15
+ # after 7 attempts SQS will move the message to the dead letter queue
16
16
 
17
- options = {}
18
- options[:redrive_policy] = %Q{{"maxReceiveCount":"7", "deadLetterTargetArn":"#{dl.arn}"}"}
17
+ dead_letter_queue_url = sqs.create_queue(queue_name: 'default_failures').queue_url
19
18
 
20
- sqs.queues.create('default', options)
19
+ dead_letter_queue_arn = sqs.get_queue_attributes(
20
+ queue_url: dead_letter_queue_url,
21
+ attribute_names: %w(QueueArn)
22
+ ).attributes['QueueArn']
23
+
24
+ attributes = {}
25
+ attributes['RedrivePolicy'] = %Q{{"maxReceiveCount":"7", "deadLetterTargetArn":"#{dead_letter_queue_arn}"}}
26
+
27
+ sqs.set_queue_attributes queue_url: default_queue_url, attributes: attributes
28
+ end
data/lib/shoryuken.rb CHANGED
@@ -1,18 +1,22 @@
1
1
  require 'yaml'
2
- require 'aws-sdk-v1'
2
+ require 'aws-sdk-core'
3
+ require 'aws-sdk-resources'
3
4
  require 'time'
4
5
 
5
6
  require 'shoryuken/version'
6
7
  require 'shoryuken/core_ext'
7
8
  require 'shoryuken/util'
9
+ require 'shoryuken/logging'
10
+ require 'shoryuken/environment_loader'
8
11
  require 'shoryuken/client'
9
12
  require 'shoryuken/worker'
10
13
  require 'shoryuken/worker_registry'
11
14
  require 'shoryuken/default_worker_registry'
12
- require 'shoryuken/logging'
13
15
  require 'shoryuken/middleware/chain'
14
16
  require 'shoryuken/middleware/server/auto_delete'
15
17
  require 'shoryuken/middleware/server/timing'
18
+ require 'shoryuken/sns_arn'
19
+ require 'shoryuken/topic'
16
20
 
17
21
  module Shoryuken
18
22
  DEFAULTS = {
@@ -57,7 +61,7 @@ module Shoryuken
57
61
  # end
58
62
  # end
59
63
  def configure_server
60
- yield self
64
+ yield self if server?
61
65
  end
62
66
 
63
67
  def server_middleware
@@ -79,6 +83,22 @@ module Shoryuken
79
83
  @@default_worker_options = options
80
84
  end
81
85
 
86
+ def on_aws_initialization(&block)
87
+ @aws_initialization_callback = block
88
+ end
89
+
90
+ def on_start(&block)
91
+ @start_callback = block
92
+ end
93
+
94
+ def on_stop(&block)
95
+ @stop_callback = block
96
+ end
97
+
98
+ attr_reader :aws_initialization_callback,
99
+ :start_callback,
100
+ :stop_callback
101
+
82
102
  private
83
103
 
84
104
  def default_server_middleware
@@ -91,6 +111,10 @@ module Shoryuken
91
111
  end
92
112
  end
93
113
  end
114
+
115
+ def server?
116
+ defined?(Shoryuken::CLI)
117
+ end
94
118
  end
95
119
  end
96
120
 
data/lib/shoryuken/cli.rb CHANGED
@@ -20,19 +20,19 @@ module Shoryuken
20
20
  self_read, self_write = IO.pipe
21
21
 
22
22
  %w[INT TERM USR1 USR2 TTIN].each do |sig|
23
- trap sig do
24
- self_write.puts(sig)
23
+ begin
24
+ trap sig do
25
+ self_write.puts(sig)
26
+ end
27
+ rescue ArgumentError
28
+ puts "Signal #{sig} not supported"
25
29
  end
26
30
  end
27
31
 
28
- setup_options(args) do |cli_options|
29
- # this needs to happen before configuration is parsed, since it may depend on Rails env
30
- initialize_logger(cli_options)
31
- load_rails if cli_options[:rails]
32
- end
33
- require_workers
34
- validate!
35
- patch_deprecated_workers!
32
+ options = parse_cli_args(args)
33
+
34
+ EnvironmentLoader.load(options)
35
+
36
36
  daemonize
37
37
  write_pid
38
38
  load_celluloid
@@ -40,6 +40,11 @@ module Shoryuken
40
40
  require 'shoryuken/launcher'
41
41
  @launcher = Shoryuken::Launcher.new
42
42
 
43
+ if callback = Shoryuken.start_callback
44
+ logger.info "Calling Shoryuken.on_start block"
45
+ callback.call
46
+ end
47
+
43
48
  begin
44
49
  launcher.run
45
50
 
@@ -67,26 +72,6 @@ module Shoryuken
67
72
  require 'shoryuken/manager'
68
73
  end
69
74
 
70
- def load_rails
71
- # Adapted from: https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/cli.rb
72
-
73
- require 'rails'
74
- if ::Rails::VERSION::MAJOR < 4
75
- require File.expand_path("config/environment.rb")
76
- ::Rails.application.eager_load!
77
- else
78
- # Painful contortions, see 1791 for discussion
79
- require File.expand_path("config/application.rb")
80
- ::Rails::Application.initializer "shoryuken.eager_load" do
81
- ::Rails.application.config.eager_load = true
82
- end
83
- require 'shoryuken/extensions/active_job_adapter' if defined?(::ActiveJob)
84
- require File.expand_path("config/environment.rb")
85
- end
86
-
87
- logger.info "Rails environment loaded"
88
- end
89
-
90
75
  def daemonize
91
76
  return unless Shoryuken.options[:daemon]
92
77
 
@@ -114,8 +99,6 @@ module Shoryuken
114
99
  io.sync = true
115
100
  end
116
101
  $stdin.reopen('/dev/null')
117
-
118
- initialize_logger
119
102
  end
120
103
 
121
104
  def write_pid
@@ -126,8 +109,8 @@ module Shoryuken
126
109
  end
127
110
  end
128
111
 
129
- def parse_options(argv)
130
- opts = {}
112
+ def parse_cli_args(argv)
113
+ opts = { queues: [] }
131
114
 
132
115
  @parser = OptionParser.new do |o|
133
116
  o.on '-c', '--concurrency INT', 'Processor threads to use' do |arg|
@@ -140,7 +123,7 @@ module Shoryuken
140
123
 
141
124
  o.on '-q', '--queue QUEUE[,WEIGHT]...', 'Queues to process with optional weights' do |arg|
142
125
  queue, weight = arg.split(',')
143
- parse_queue queue, weight
126
+ opts[:queues] << [queue, weight]
144
127
  end
145
128
 
146
129
  o.on '-r', '--require [PATH|DIR]', 'Location of the worker' do |arg|
@@ -213,96 +196,5 @@ module Shoryuken
213
196
  raise Interrupt
214
197
  end
215
198
  end
216
-
217
- def setup_options(args)
218
- options = parse_options(args)
219
-
220
- # yield parsed options in case we need to do more setup before configuration is parsed
221
- yield(options) if block_given?
222
-
223
- config = options[:config_file] ? parse_config(options[:config_file]).deep_symbolize_keys : {}
224
-
225
- Shoryuken.options.merge!(config)
226
-
227
- Shoryuken.options.merge!(options)
228
-
229
- parse_queues
230
- end
231
-
232
- def parse_config(config_file)
233
- if File.exist?(config_file)
234
- YAML.load(ERB.new(IO.read(config_file)).result)
235
- else
236
- raise ArgumentError, "Config file #{config_file} does not exist"
237
- end
238
- end
239
-
240
- def initialize_logger(options = Shoryuken.options)
241
- Shoryuken::Logging.initialize_logger(options[:logfile]) if options[:logfile]
242
-
243
- Shoryuken.logger.level = Logger::DEBUG if options[:verbose]
244
- end
245
-
246
- def validate!
247
- raise ArgumentError, 'No queues supplied' if Shoryuken.queues.empty?
248
-
249
- all_queues = Shoryuken.queues
250
- queues_with_workers = Shoryuken.worker_registry.queues
251
-
252
- unless defined?(::ActiveJob)
253
- (all_queues - queues_with_workers).each do |queue|
254
- logger.warn "No worker supplied for '#{queue}'"
255
- end
256
- end
257
-
258
- initialize_aws
259
-
260
- Shoryuken.queues.uniq.each do |queue|
261
- # validate all queues and AWS credentials consequently
262
- begin
263
- Shoryuken::Client.queues queue
264
- rescue AWS::SQS::Errors::NonExistentQueue => e
265
- raise ArgumentError, "Queue '#{queue}' does not exist"
266
- rescue => e
267
- raise
268
- end
269
- end
270
- end
271
-
272
- def initialize_aws
273
- # aws-sdk tries to load the credentials from the ENV variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
274
- # when not explicit supplied
275
- AWS.config Shoryuken.options[:aws] if Shoryuken.options[:aws]
276
- end
277
-
278
- def require_workers
279
- require Shoryuken.options[:require] if Shoryuken.options[:require]
280
- end
281
-
282
- def parse_queues
283
- Shoryuken.options[:queues].to_a.each { |queue_and_weight| parse_queue *queue_and_weight }
284
- end
285
-
286
- def parse_queue(queue, weight = nil)
287
- [weight.to_i, 1].max.times { Shoryuken.queues << queue }
288
- end
289
-
290
- def patch_deprecated_workers!
291
- Shoryuken.worker_registry.queues.each do |queue|
292
- Shoryuken.worker_registry.workers(queue).each do |worker_class|
293
- if worker_class.instance_method(:perform).arity == 1
294
- logger.warn "[DEPRECATION] #{worker_class.name}#perform(sqs_msg) is deprecated. Please use #{worker_class.name}#perform(sqs_msg, body)"
295
-
296
- worker_class.class_eval do
297
- alias_method :deprecated_perform, :perform
298
-
299
- def perform(sqs_msg, body = nil)
300
- deprecated_perform(sqs_msg)
301
- end
302
- end
303
- end
304
- end
305
- end
306
- end
307
199
  end
308
200
  end
@@ -1,29 +1,43 @@
1
1
  module Shoryuken
2
2
  class Client
3
3
  @@queues = {}
4
- @@visibility_timeouts = {}
4
+ @@topics = {}
5
5
 
6
6
  class << self
7
- def queues(queue)
8
- @@queues[queue.to_s] ||= sqs.queues.named(queue)
7
+ def queues(name)
8
+ @@queues[name.to_s] ||= sqs_resource.get_queue_by_name(queue_name: name)
9
9
  end
10
10
 
11
- def visibility_timeout(queue)
12
- @@visibility_timeouts[queue.to_s] ||= queues(queue).visibility_timeout
11
+ def sns
12
+ @sns ||= Aws::SNS::Client.new(aws_client_options(:sns_endpoint))
13
13
  end
14
14
 
15
- def receive_message(queue, options = {})
16
- queues(queue).receive_message(Hash(options))
15
+ def sns_arn
16
+ @sns_arn ||= SnsArn
17
17
  end
18
18
 
19
- def send_message(queue, body, options = {})
20
- body = JSON.dump(body) if body.is_a?(Hash)
19
+ def sqs
20
+ @sqs ||= Aws::SQS::Client.new(aws_client_options(:sqs_endpoint))
21
+ end
21
22
 
22
- queues(queue).send_message(body, options)
23
+ def sqs_resource
24
+ @sqs_resource ||= Aws::SQS::Resource.new(client: sqs)
23
25
  end
24
26
 
25
- def sqs
26
- @sqs ||= AWS::SQS.new
27
+ def topics(name)
28
+ @@topics[name.to_s] ||= Topic.new(name, sns)
29
+ end
30
+
31
+ attr_accessor :account_id
32
+ attr_writer :sns, :sqs, :sqs_resource, :sns_arn
33
+
34
+ private
35
+
36
+ def aws_client_options service_endpoint_key
37
+ explicit_endpoint = Shoryuken.options[:aws][service_endpoint_key]
38
+ options = {}
39
+ options[:endpoint] = explicit_endpoint unless explicit_endpoint.to_s.empty?
40
+ options
27
41
  end
28
42
  end
29
43
  end