rails_autoscale_agent 0.8.2 → 0.9.0.beta.5

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
  SHA256:
3
- metadata.gz: e8dbbc3694c07403043f08525c27933723f1110e7b6a035bf0ffcd47f5ba6cb8
4
- data.tar.gz: fbad110d671bfb32dcfdfb31263b1a0bfd137e2074ed6cd23b2ffd734a2b9402
3
+ metadata.gz: f151c5012b2d2a5bb76f17ed4b4ee7f8f751ef9657c7f4b9d42d4d86fda7c654
4
+ data.tar.gz: 4773c138b97d723aa44f803ccb504facdf4c619995122296561933b5363d8910
5
5
  SHA512:
6
- metadata.gz: 6c57fc917a84ebca827f1c8bebe43425f04bd70d19448f041151b40e9c9efb882cfbe3e1568c142d12fc335de348a29edfc4c859198fa7de773e302cad56916f
7
- data.tar.gz: 3f1ee0279f3513cf3048658668e4950240029b2f48a3d12d34573fa5990783ebe3f0f958e76cfa0e260d3761c54ffe2d6fdb6562e8ee3011b6b009222723c17b
6
+ metadata.gz: 432f9ea93f5c722f286a0bec54f22c477583f0b0501cff64498f2d710fdd5e3f674753443d017ab43cefc04d0b8b2263227554992e84f023b13074392d004e15
7
+ data.tar.gz: 1ba59eb800d04044e78e2c5462fe373dc4d81e1b14de72d4a41f3454acf4a4a0739067f8371ad2d31372badf2c222dffc0b13c2ef0c01e542d12d218a6c1981f
@@ -6,7 +6,8 @@
6
6
  {
7
7
  "label": "test: all",
8
8
  "group": "test",
9
- "command": "rspec",
9
+ "command": "bundle",
10
+ "args": ["exec", "rspec"],
10
11
  "runOptions": {
11
12
  "reevaluateOnRerun": false
12
13
  },
@@ -31,8 +32,8 @@
31
32
  {
32
33
  "label": "test: file",
33
34
  "group": "test",
34
- "command": "rspec",
35
- "args": ["${relativeFile}"],
35
+ "command": "bundle",
36
+ "args": ["exec", "rspec", "${relativeFile}"],
36
37
  "runOptions": {
37
38
  "reevaluateOnRerun": false
38
39
  },
@@ -57,8 +58,8 @@
57
58
  {
58
59
  "label": "test: line",
59
60
  "group": "test",
60
- "command": "rspec",
61
- "args": ["${relativeFile}:${lineNumber}"],
61
+ "command": "bundle",
62
+ "args": ["exec", "rspec", "${relativeFile}:${lineNumber}"],
62
63
  "runOptions": {
63
64
  "reevaluateOnRerun": false
64
65
  },
data/Gemfile CHANGED
@@ -1,4 +1,16 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in rails_autoscale_agent.gemspec
4
3
  gemspec
4
+
5
+ gem "rake", ">= 12.3.3"
6
+ gem "rspec", ">= 3.0"
7
+ gem "vcr", ">= 3.0"
8
+ gem "webmock"
9
+ gem "pry-byebug"
10
+ gem "sidekiq", ">= 5.0"
11
+ gem "delayed_job"
12
+ gem "delayed_job_active_record"
13
+ gem "que"
14
+ gem "resque"
15
+ gem "activesupport"
16
+ gem "sqlite3", platforms: :ruby
data/README.md CHANGED
@@ -74,7 +74,7 @@ Reach out to help@railsautoscale.com if you run into any other problems.
74
74
 
75
75
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
76
76
 
77
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
77
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, commit it, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
78
78
 
79
79
  ## Contributing
80
80
 
@@ -11,8 +11,8 @@ module RailsAutoscaleAgent
11
11
 
12
12
  SUCCESS = 'success'
13
13
 
14
- def initialize(api_url_base)
15
- @api_url_base = api_url_base
14
+ def initialize(config)
15
+ @config = config
16
16
  end
17
17
 
18
18
  def report_metrics!(report_params, timings_csv)
@@ -24,6 +24,10 @@ module RailsAutoscaleAgent
24
24
  post_json '/registrations', registration: registration_params
25
25
  end
26
26
 
27
+ def report_exception!(ex)
28
+ post_json '/exceptions', message: ex.inspect, backtrace: ex.backtrace.join("\n")
29
+ end
30
+
27
31
  private
28
32
 
29
33
  def post_json(path, data)
@@ -37,9 +41,14 @@ module RailsAutoscaleAgent
37
41
  end
38
42
 
39
43
  def post_raw(options)
40
- uri = URI.parse("#{@api_url_base}#{options.fetch(:path)}")
44
+ uri = URI.parse("#{@config.api_base_url}#{options.fetch(:path)}")
41
45
  ssl = uri.scheme == 'https'
42
46
 
47
+ if @config.dev_mode
48
+ logger.debug "[DEV_MODE] Skipping request to #{uri}"
49
+ return SuccessResponse.new('{}')
50
+ end
51
+
43
52
  response = Net::HTTP.start(uri.host, uri.port, use_ssl: ssl) do |http|
44
53
  request = Net::HTTP::Post.new(uri.request_uri, options[:headers] || {})
45
54
  request.body = options.fetch(:body)
@@ -7,26 +7,29 @@ module RailsAutoscaleAgent
7
7
  include Singleton
8
8
 
9
9
  attr_accessor :report_interval, :logger, :api_base_url, :max_request_size,
10
- :dyno, :pid, :addon_name, :worker_adapters
10
+ :dyno, :pid, :addon_name, :worker_adapters, :dev_mode
11
11
 
12
12
  def initialize
13
13
  require 'rails_autoscale_agent/worker_adapters/sidekiq'
14
14
  require 'rails_autoscale_agent/worker_adapters/delayed_job'
15
15
  require 'rails_autoscale_agent/worker_adapters/que'
16
+ require 'rails_autoscale_agent/worker_adapters/resque'
16
17
  @worker_adapters = [
17
18
  WorkerAdapters::Sidekiq.instance,
18
19
  WorkerAdapters::DelayedJob.instance,
19
20
  WorkerAdapters::Que.instance,
21
+ WorkerAdapters::Resque.instance,
20
22
  ]
21
23
 
22
24
  # Allow the add-on name to be configured - needed for testing
23
25
  @addon_name = ENV['RAILS_AUTOSCALE_ADDON'] || 'RAILS_AUTOSCALE'
24
26
  @api_base_url = ENV["#{@addon_name}_URL"]
27
+ @dev_mode = ENV['RAILS_AUTOSCALE_DEV'] == 'true'
25
28
  @pid = Process.pid
26
29
  @max_request_size = 100_000 # ignore request payloads over 100k since they skew the queue times
27
- @report_interval = 60 # this default will be overwritten during Reporter#register!
30
+ @report_interval = 10 # this default will be overwritten during Reporter#register!
28
31
  @logger ||= defined?(Rails) ? Rails.logger : ::Logger.new(STDOUT)
29
- @dyno = ENV['DYNO']
32
+ @dyno = @dev_mode ? 'dev.1' : ENV['DYNO']
30
33
  end
31
34
 
32
35
  def to_s
@@ -37,5 +40,6 @@ module RailsAutoscaleAgent
37
40
  @max_request_size
38
41
  end
39
42
 
43
+ alias_method :dev_mode?, :dev_mode
40
44
  end
41
45
  end
@@ -24,7 +24,7 @@ module RailsAutoscaleAgent
24
24
  # to DEBUG level in production).
25
25
  # This uses a separate logger so that RAILS_AUTOSCALE_DEBUG
26
26
  # shows debug logs regardless of Rails log level.
27
- debug_logger.debug tag(msg) if ENV['RAILS_AUTOSCALE_DEBUG'] == 'true'
27
+ debug_logger.debug tag(msg) if ENV['RAILS_AUTOSCALE_DEBUG'] == 'true' || Config.instance.dev_mode?
28
28
  end
29
29
 
30
30
  private
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsAutoscaleAgent
4
- class Measurement < Struct.new(:time, :value, :queue_name)
5
- def initialize(time, value, queue_name = nil)
6
- super time.utc, value.to_i, queue_name
4
+ class Measurement < Struct.new(:time, :value, :queue_name, :metric)
5
+ # No queue_name is assumed to be a web request measurement
6
+ # Metrics: qt = queue time (default), qd = queue depth (needed for Resque support)
7
+ def initialize(time, value, queue_name = nil, metric = nil)
8
+ super time.utc, value.to_i, queue_name, metric
7
9
  end
8
10
  end
9
11
  end
@@ -3,7 +3,7 @@
3
3
  require 'rails_autoscale_agent/version'
4
4
 
5
5
  module RailsAutoscaleAgent
6
- class Registration < Struct.new(:config)
6
+ class Registration < Struct.new(:config, :worker_adapters)
7
7
 
8
8
  def to_params
9
9
  {
@@ -12,6 +12,8 @@ module RailsAutoscaleAgent
12
12
  ruby_version: RUBY_VERSION,
13
13
  rails_version: defined?(Rails) && Rails.version,
14
14
  gem_version: VERSION,
15
+ # example: { worker_adapters: 'Sidekiq,Que' }
16
+ worker_adapters: worker_adapters.map { |o| o.class.name.split('::').last }.join(','),
15
17
  }
16
18
  end
17
19
  end
@@ -19,14 +19,12 @@ module RailsAutoscaleAgent
19
19
  def to_csv
20
20
  String.new.tap do |result|
21
21
  @measurements.each do |measurement|
22
- result << measurement.time.to_i.to_s
23
- result << ','
24
- result << measurement.value.to_s
25
-
26
- if measurement.queue_name
27
- result << ','
28
- result << measurement.queue_name
29
- end
22
+ result << [
23
+ measurement.time.to_i,
24
+ measurement.value,
25
+ measurement.queue_name,
26
+ measurement.metric,
27
+ ].join(',')
30
28
 
31
29
  result << "\n"
32
30
  end
@@ -21,14 +21,14 @@ module RailsAutoscaleAgent
21
21
  @started = true
22
22
  @worker_adapters = config.worker_adapters.select(&:enabled?)
23
23
 
24
- if !config.api_base_url
24
+ if !config.api_base_url && !config.dev_mode?
25
25
  logger.info "Reporter not started: #{config.addon_name}_URL is not set"
26
26
  return
27
27
  end
28
28
 
29
29
  Thread.new do
30
30
  loop do
31
- register!(config) unless @registered
31
+ register!(config, @worker_adapters) unless @registered
32
32
 
33
33
  # Stagger reporting to spread out reports from many processes
34
34
  multiplier = 1 - (rand / 4) # between 0.75 and 1.0
@@ -41,7 +41,7 @@ module RailsAutoscaleAgent
41
41
  # Exceptions in threads other than the main thread will fail silently
42
42
  # https://ruby-doc.org/core-2.2.0/Thread.html#class-Thread-label-Exception+handling
43
43
  logger.error "Reporter error: #{ex.inspect}"
44
- logger.error ex.backtrace.join("\n")
44
+ AutoscaleApi.new(config.api_base_url).report_exception!(ex)
45
45
  end
46
46
  end
47
47
  end
@@ -58,7 +58,7 @@ module RailsAutoscaleAgent
58
58
  logger.info "Reporting #{report.measurements.size} measurements"
59
59
 
60
60
  params = report.to_params(config)
61
- result = AutoscaleApi.new(config.api_base_url).report_metrics!(params, report.to_csv)
61
+ result = AutoscaleApi.new(config).report_metrics!(params, report.to_csv)
62
62
 
63
63
  case result
64
64
  when AutoscaleApi::SuccessResponse
@@ -71,16 +71,16 @@ module RailsAutoscaleAgent
71
71
  end
72
72
  end
73
73
 
74
- def register!(config)
75
- params = Registration.new(config).to_params
76
- result = AutoscaleApi.new(config.api_base_url).register_reporter!(params)
74
+ def register!(config, worker_adapters)
75
+ params = Registration.new(config, worker_adapters).to_params
76
+ result = AutoscaleApi.new(config).register_reporter!(params)
77
77
 
78
78
  case result
79
79
  when AutoscaleApi::SuccessResponse
80
80
  @registered = true
81
81
  config.report_interval = result.data['report_interval'] if result.data['report_interval']
82
82
  config.max_request_size = result.data['max_request_size'] if result.data['max_request_size']
83
- worker_adapters_msg = @worker_adapters.map { |a| a.class.name }.join(', ')
83
+ worker_adapters_msg = worker_adapters.map { |a| a.class.name }.join(', ')
84
84
  logger.info "Reporter starting, will report every #{config.report_interval} seconds or so. Worker adapters: [#{worker_adapters_msg}]"
85
85
  when AutoscaleApi::FailureResponse
86
86
  logger.error "Reporter failed to register: #{result.failure_message}"
@@ -4,17 +4,17 @@ module RailsAutoscaleAgent
4
4
  class Request
5
5
  include Logger
6
6
 
7
- attr_reader :id, :entered_queue_at, :path, :method, :size
8
-
9
7
  def initialize(env, config)
10
8
  @config = config
11
9
  @id = env['HTTP_X_REQUEST_ID']
12
- @path = env['PATH_INFO']
13
- @method = env['REQUEST_METHOD'].downcase
14
10
  @size = env['rack.input'].respond_to?(:size) ? env['rack.input'].size : 0
11
+ @request_body_wait = env['puma.request_body_wait'].to_i
15
12
 
16
- if unix_millis = env['HTTP_X_REQUEST_START']
17
- @entered_queue_at = Time.at(unix_millis.to_f / 1000)
13
+ @entered_queue_at = if unix_millis = env['HTTP_X_REQUEST_START']
14
+ Time.at(unix_millis.to_f / 1000)
15
+ elsif config.dev_mode?
16
+ # In dev mode, fake a queue time of 0-1000ms
17
+ Time.now - rand + @request_body_wait
18
18
  end
19
19
  end
20
20
 
@@ -23,12 +23,17 @@ module RailsAutoscaleAgent
23
23
  end
24
24
 
25
25
  def queue_time
26
- if entered_queue_at
27
- queue_time = ((Time.now - entered_queue_at) * 1000).to_i
28
- queue_time = 0 if queue_time < 0
29
- logger.debug "Collected queue_time=#{queue_time}ms request_id=#{id} request_size=#{size}"
26
+ if @entered_queue_at
27
+ queue_time = ((Time.now - @entered_queue_at) * 1000).to_i
28
+
29
+ # Subtract the time Puma spent waiting on the request body. It's irrelevant to capacity-related queue time.
30
+ # Without this, slow clients and large request payloads will skew queue time.
31
+ queue_time -= @request_body_wait
32
+
33
+ logger.debug "Request queue_time=#{queue_time}ms body_wait=#{@request_body_wait}ms request_id=#{@id} size=#{@size}"
30
34
 
31
- queue_time
35
+ # Safeguard against negative queue times (should not happen in practice)
36
+ queue_time > 0 ? queue_time : 0
32
37
  end
33
38
  end
34
39
  end
@@ -15,8 +15,8 @@ module RailsAutoscaleAgent
15
15
  @measurements = []
16
16
  end
17
17
 
18
- def push(value, time = Time.now, queue_name = nil)
19
- @measurements << Measurement.new(time, value, queue_name)
18
+ def push(value, time = Time.now, queue_name = nil, metric = nil)
19
+ @measurements << Measurement.new(time, value, queue_name, metric)
20
20
  end
21
21
 
22
22
  def pop_report
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsAutoscaleAgent
4
- VERSION = "0.8.2"
4
+ VERSION = "0.9.0.beta.5"
5
5
  end
@@ -2,62 +2,56 @@
2
2
 
3
3
  require 'rails_autoscale_agent/logger'
4
4
 
5
- module WorkerAdapters
6
- class DelayedJob
7
- include RailsAutoscaleAgent::Logger
8
- include Singleton
9
-
10
- class << self
11
- attr_accessor :queues
12
- end
13
-
14
- def initialize
15
- # Track the known queues so we can continue reporting on queues that don't
16
- # currently have enqueued jobs.
17
- self.class.queues = Set.new
18
-
19
- install if enabled?
20
- end
21
-
22
- def enabled?
23
- defined? ::Delayed
24
- end
25
-
26
- def collect!(store)
27
- log_msg = String.new('DelayedJob latency ')
28
- t = Time.now
29
-
30
- sql = 'SELECT queue, min(run_at) FROM delayed_jobs GROUP BY queue'
31
- run_at_by_queue = Hash[ActiveRecord::Base.connection.select_rows(sql)]
32
- queues = self.class.queues | run_at_by_queue.keys
33
-
34
- queues.each do |queue|
35
- next if queue.nil? || queue.empty?
36
- run_at = run_at_by_queue[queue]
37
- run_at = Time.parse(run_at) if run_at.is_a?(String)
38
- latency_ms = run_at ? ((t - run_at)*1000).ceil : 0
39
- store.push latency_ms, t, queue
40
- log_msg << "#{queue}=#{latency_ms} "
5
+ module RailsAutoscaleAgent
6
+ module WorkerAdapters
7
+ class DelayedJob
8
+ include RailsAutoscaleAgent::Logger
9
+ include Singleton
10
+
11
+ attr_writer :queues
12
+
13
+ def queues
14
+ # Track the known queues so we can continue reporting on queues that don't
15
+ # have enqueued jobs at the time of reporting.
16
+ # Assume a "default" queue so we always report *something*, even when nothing
17
+ # is enqueued.
18
+ @queues ||= Set.new(['default'])
41
19
  end
42
20
 
43
- logger.debug log_msg
44
- end
45
-
46
- private
47
-
48
- def install
49
- plugin = Class.new(Delayed::Plugin) do
50
- require 'delayed_job'
51
-
52
- callbacks do |lifecycle|
53
- lifecycle.before(:enqueue) do |job, &block|
54
- queue = job.queue || 'default'
55
- WorkerAdapters::DelayedJob.queues.add queue
56
- end
21
+ def enabled?
22
+ if defined?(::Delayed::Job) && defined?(::Delayed::Backend::ActiveRecord)
23
+ logger.info "DelayedJob enabled (#{::ActiveRecord::Base.default_timezone})"
24
+ true
57
25
  end
58
26
  end
59
27
 
60
- Delayed::Worker.plugins << plugin
28
+ def collect!(store)
29
+ log_msg = String.new
30
+ t = Time.now.utc
31
+ sql = <<~SQL
32
+ SELECT COALESCE(queue, 'default'), min(run_at)
33
+ FROM delayed_jobs
34
+ WHERE locked_at IS NULL
35
+ AND failed_at IS NULL
36
+ GROUP BY queue
37
+ SQL
38
+
39
+ run_at_by_queue = Hash[ActiveRecord::Base.connection.select_rows(sql)]
40
+ self.queues |= run_at_by_queue.keys
41
+
42
+ queues.each do |queue|
43
+ run_at = run_at_by_queue[queue]
44
+ # DateTime.parse assumes a UTC string
45
+ run_at = DateTime.parse(run_at) if run_at.is_a?(String)
46
+ latency_ms = run_at ? ((t - run_at)*1000).ceil : 0
47
+ latency_ms = 0 if latency_ms < 0
48
+
49
+ store.push latency_ms, t, queue
50
+ log_msg << "dj.#{queue}=#{latency_ms} "
51
+ end
52
+
53
+ logger.debug log_msg unless log_msg.empty?
54
+ end
61
55
  end
62
56
  end
63
57
  end
@@ -1,46 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails_autoscale_agent/logger'
4
- require 'time'
5
4
 
6
- module WorkerAdapters
7
- class Que
8
- include RailsAutoscaleAgent::Logger
9
- include Singleton
5
+ module RailsAutoscaleAgent
6
+ module WorkerAdapters
7
+ class Que
8
+ include RailsAutoscaleAgent::Logger
9
+ include Singleton
10
10
 
11
- DEFAULT_QUEUES = ['default']
11
+ attr_writer :queues
12
12
 
13
- class << self
14
- attr_accessor :queues
15
- end
16
-
17
- def initialize
18
- self.class.queues = DEFAULT_QUEUES
19
- end
20
-
21
- def enabled?
22
- defined? ::Que
23
- end
13
+ def queues
14
+ # Track the known queues so we can continue reporting on queues that don't
15
+ # have enqueued jobs at the time of reporting.
16
+ # Assume a "default" queue so we always report *something*, even when nothing
17
+ # is enqueued.
18
+ @queues ||= Set.new(['default'])
19
+ end
24
20
 
25
- def collect!(store)
26
- log_msg = String.new('Que latency ')
27
- t = Time.now
28
-
29
- # Ignore failed jobs (they skew latency measurement due to the original run_at)
30
- sql = 'SELECT queue, min(run_at) FROM que_jobs WHERE error_count = 0 GROUP BY queue'
31
- run_at_by_queue = Hash[ActiveRecord::Base.connection.select_rows(sql)]
32
- self.class.queues |= run_at_by_queue.keys
33
-
34
- self.class.queues.each do |queue|
35
- next if queue.nil? || queue.empty?
36
- run_at = run_at_by_queue[queue]
37
- run_at = Time.parse(run_at) if run_at.is_a?(String)
38
- latency_ms = run_at ? ((t - run_at)*1000).ceil : 0
39
- store.push latency_ms, t, queue
40
- log_msg << "#{queue}=#{latency_ms} "
21
+ def enabled?
22
+ if defined?(::Que)
23
+ logger.info "Que enabled (#{::ActiveRecord::Base.default_timezone})"
24
+ true
25
+ end
41
26
  end
42
27
 
43
- logger.debug log_msg
28
+ def collect!(store)
29
+ log_msg = String.new
30
+ t = Time.now.utc
31
+ sql = <<~SQL
32
+ SELECT queue, min(run_at)
33
+ FROM que_jobs
34
+ WHERE finished_at IS NULL
35
+ AND expired_at IS NULL
36
+ AND error_count = 0
37
+ GROUP BY 1
38
+ SQL
39
+
40
+ run_at_by_queue = Hash[ActiveRecord::Base.connection.select_rows(sql)]
41
+ self.queues |= run_at_by_queue.keys
42
+
43
+ queues.each do |queue|
44
+ run_at = run_at_by_queue[queue]
45
+ run_at = DateTime.parse(run_at) if run_at.is_a?(String)
46
+ latency_ms = run_at ? ((t - run_at)*1000).ceil : 0
47
+ latency_ms = 0 if latency_ms < 0
48
+
49
+ store.push latency_ms, t, queue
50
+ log_msg << "que.#{queue}=#{latency_ms} "
51
+ end
52
+
53
+ logger.debug log_msg unless log_msg.empty?
54
+ end
44
55
  end
45
56
  end
46
57
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_autoscale_agent/logger'
4
+
5
+ module RailsAutoscaleAgent
6
+ module WorkerAdapters
7
+ class Resque
8
+ include RailsAutoscaleAgent::Logger
9
+ include Singleton
10
+
11
+ attr_writer :queues
12
+
13
+ def queues
14
+ @queues ||= ['default']
15
+ end
16
+
17
+ def enabled?
18
+ require 'resque'
19
+ logger.info "Resque enabled"
20
+ true
21
+ rescue LoadError
22
+ false
23
+ end
24
+
25
+ def collect!(store)
26
+ log_msg = String.new
27
+
28
+ # Ensure we continue to collect metrics for known queue names, even when nothing is
29
+ # enqueued at the time. Without this, it will appears that the agent is no longer reporting.
30
+ self.queues |= ::Resque.queues
31
+
32
+ queues.each do |queue|
33
+ next if queue.nil? || queue.empty?
34
+ depth = ::Resque.size(queue)
35
+ store.push depth, Time.now, queue, :qd
36
+ log_msg << "resque-qd.#{queue}=#{depth} "
37
+ end
38
+
39
+ logger.debug log_msg
40
+ end
41
+ end
42
+ end
43
+ end
@@ -2,28 +2,49 @@
2
2
 
3
3
  require 'rails_autoscale_agent/logger'
4
4
 
5
- module WorkerAdapters
6
- class Sidekiq
7
- include RailsAutoscaleAgent::Logger
8
- include Singleton
9
-
10
- def enabled?
11
- require 'sidekiq/api'
12
- true
13
- rescue LoadError
14
- false
15
- end
5
+ module RailsAutoscaleAgent
6
+ module WorkerAdapters
7
+ class Sidekiq
8
+ include RailsAutoscaleAgent::Logger
9
+ include Singleton
10
+
11
+ attr_writer :known_queue_names
16
12
 
17
- def collect!(store)
18
- log_msg = String.new('Sidekiq latency ')
13
+ def known_queue_names
14
+ @known_queue_names ||= ['default']
15
+ end
19
16
 
20
- ::Sidekiq::Queue.all.each do |queue|
21
- latency_ms = (queue.latency * 1000).ceil
22
- store.push latency_ms, Time.now, queue.name
23
- log_msg << "#{queue.name}=#{latency_ms} "
17
+ def enabled?
18
+ require 'sidekiq/api'
19
+ logger.info "Sidekiq enabled"
20
+ true
21
+ rescue LoadError
22
+ false
24
23
  end
25
24
 
26
- logger.debug log_msg
25
+ def collect!(store)
26
+ log_msg = String.new
27
+ queues_by_name = ::Sidekiq::Queue.all.each_with_object({}) do |queue, obj|
28
+ obj[queue.name] = queue
29
+ end
30
+
31
+ # Ensure we continue to collect metrics for known queue names, even when nothing is
32
+ # enqueued at the time. Without this, it will appears that the agent is no longer reporting.
33
+ known_queue_names.each do |queue_name|
34
+ queues_by_name[queue_name] ||= ::Sidekiq::Queue.new(queue_name)
35
+ end
36
+ self.known_queue_names = queues_by_name.keys
37
+
38
+ queues_by_name.each do |queue_name, queue|
39
+ latency_ms = (queue.latency * 1000).ceil
40
+ depth = queue.size
41
+ store.push latency_ms, Time.now, queue.name, :qt
42
+ store.push depth, Time.now, queue.name, :qd
43
+ log_msg << "sidekiq-qt.#{queue.name}=#{latency_ms} sidekiq-qd.#{queue.name}=#{depth} "
44
+ end
45
+
46
+ logger.debug log_msg
47
+ end
27
48
  end
28
49
  end
29
50
  end
@@ -15,16 +15,4 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
17
  spec.require_paths = ["lib"]
18
-
19
- spec.add_development_dependency "bundler", "~> 2.0"
20
- spec.add_development_dependency "rake", "~> 12.3.3"
21
- spec.add_development_dependency "rspec", "~> 3.0"
22
- spec.add_development_dependency "vcr", "~> 3.0"
23
- spec.add_development_dependency "webmock"
24
- spec.add_development_dependency "pry"
25
- spec.add_development_dependency "pry-byebug"
26
- spec.add_development_dependency "sidekiq", "~> 5.0"
27
- spec.add_development_dependency "delayed_job"
28
- spec.add_development_dependency "que"
29
- spec.add_development_dependency "activesupport"
30
18
  end
metadata CHANGED
@@ -1,169 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_autoscale_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0.beta.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam McCrea
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-22 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '2.0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.0'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 12.3.3
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 12.3.3
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '3.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '3.0'
55
- - !ruby/object:Gem::Dependency
56
- name: vcr
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '3.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: webmock
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: pry
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: pry-byebug
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: sidekiq
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '5.0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '5.0'
125
- - !ruby/object:Gem::Dependency
126
- name: delayed_job
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: que
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: activesupport
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
11
+ date: 2020-06-28 00:00:00.000000000 Z
12
+ dependencies: []
167
13
  description:
168
14
  email:
169
15
  - adam@adamlogic.com
@@ -198,6 +44,7 @@ files:
198
44
  - lib/rails_autoscale_agent/version.rb
199
45
  - lib/rails_autoscale_agent/worker_adapters/delayed_job.rb
200
46
  - lib/rails_autoscale_agent/worker_adapters/que.rb
47
+ - lib/rails_autoscale_agent/worker_adapters/resque.rb
201
48
  - lib/rails_autoscale_agent/worker_adapters/sidekiq.rb
202
49
  - log/.gitkeep
203
50
  - rails_autoscale_agent.gemspec
@@ -216,11 +63,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
216
63
  version: '0'
217
64
  required_rubygems_version: !ruby/object:Gem::Requirement
218
65
  requirements:
219
- - - ">="
66
+ - - ">"
220
67
  - !ruby/object:Gem::Version
221
- version: '0'
68
+ version: 1.3.1
222
69
  requirements: []
223
- rubygems_version: 3.0.3
70
+ rubygems_version: 3.1.4
224
71
  signing_key:
225
72
  specification_version: 4
226
73
  summary: This gem works with the Rails Autoscale Heroku add-on to automatically scale