rails_autoscale_agent 0.8.3 → 0.9.0.beta.1

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: 3924fc840320be8327c183e1cb54055d8516d2fefee1cffbfa7092da39b05985
4
- data.tar.gz: abd81e6c4c2dc3a65be7c426dda8742ec92b7c29c47a71eeb2566731436ce4e6
3
+ metadata.gz: 37ecb709c866b3d831f79d8a15bd54cfcacfd00dc45ebf84ad5129c0d1206a3d
4
+ data.tar.gz: 373474c8d84bb5cbd026f792aef4eef4028d77b666d74ac4407cd7d4c603703d
5
5
  SHA512:
6
- metadata.gz: 4867868c9e9e707d9e4afe46f20d19f600a87b26aabf5a96f4ad8ff91698d00b08f8a0566ef20fe8f3f1c036c9220b1034a92c70280a4cf29a6d5b5a728b4105
7
- data.tar.gz: e2fbbc4afa90c0c636dddb3172d1164108e984f4c550739df552cd185a8a51d7255ff354debc658ecfa893fb4f87f53e6c298bfe4dddf2aa35615f7636e64420
6
+ metadata.gz: cbe7b97077c907d45deba6cfa0dce814e8d5fff2339cdf8c81c7e96101d0e3cd8a1dcbcdd3474bcb8fe798ec7591f59b040a8e40d367fa11b32c4dbd6ead52f1
7
+ data.tar.gz: 2677393fb16bb0fb776c2325838074ed3c0c9c4d990d296e950ad83576456ac66b15765a96e84a1fefe26ff6696ca89e419e23db78a644e01054378c7dbf3cd1
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)
@@ -37,9 +37,14 @@ module RailsAutoscaleAgent
37
37
  end
38
38
 
39
39
  def post_raw(options)
40
- uri = URI.parse("#{@api_url_base}#{options.fetch(:path)}")
40
+ uri = URI.parse("#{@config.api_base_url}#{options.fetch(:path)}")
41
41
  ssl = uri.scheme == 'https'
42
42
 
43
+ if @config.dev_mode
44
+ logger.debug "[DEV_MODE] Skipping request to #{uri}"
45
+ return SuccessResponse.new('{}')
46
+ end
47
+
43
48
  response = Net::HTTP.start(uri.host, uri.port, use_ssl: ssl) do |http|
44
49
  request = Net::HTTP::Post.new(uri.request_uri, options[:headers] || {})
45
50
  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
@@ -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}"
@@ -13,8 +13,11 @@ module RailsAutoscaleAgent
13
13
  @method = env['REQUEST_METHOD'].downcase
14
14
  @size = env['rack.input'].respond_to?(:size) ? env['rack.input'].size : 0
15
15
 
16
- if unix_millis = env['HTTP_X_REQUEST_START']
17
- @entered_queue_at = Time.at(unix_millis.to_f / 1000)
16
+ @entered_queue_at = if unix_millis = env['HTTP_X_REQUEST_START']
17
+ Time.at(unix_millis.to_f / 1000)
18
+ elsif config.dev_mode?
19
+ # In dev mode, fake a queue time of 0-1000ms
20
+ Time.now - rand
18
21
  end
19
22
  end
20
23
 
@@ -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.3"
4
+ VERSION = "0.9.0.beta.1"
5
5
  end
@@ -2,63 +2,64 @@
2
2
 
3
3
  require 'rails_autoscale_agent/logger'
4
4
 
5
- module WorkerAdapters
6
- class DelayedJob
7
- include RailsAutoscaleAgent::Logger
8
- include Singleton
5
+ module RailsAutoscaleAgent
6
+ module WorkerAdapters
7
+ class DelayedJob
8
+ include RailsAutoscaleAgent::Logger
9
+ include Singleton
9
10
 
10
- class << self
11
- attr_accessor :queues
12
- end
11
+ class << self
12
+ attr_accessor :queues
13
+ end
13
14
 
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
15
+ def initialize
16
+ # Track the known queues so we can continue reporting on queues that don't
17
+ # currently have enqueued jobs.
18
+ self.class.queues = Set.new
18
19
 
19
- install if enabled?
20
- end
20
+ install if enabled?
21
+ end
21
22
 
22
- def enabled?
23
- defined? ::Delayed
24
- end
23
+ def enabled?
24
+ defined? ::Delayed
25
+ end
25
26
 
26
- def collect!(store)
27
- log_msg = String.new('DelayedJob latency ')
28
- t = Time.now
27
+ def collect!(store)
28
+ log_msg = String.new
29
+ t = Time.now
29
30
 
30
- # Ignore failed jobs (they skew latency measurement due to the original run_at)
31
- sql = 'SELECT queue, min(run_at) FROM delayed_jobs WHERE attempts = 0 GROUP BY queue'
32
- run_at_by_queue = Hash[ActiveRecord::Base.connection.select_rows(sql)]
33
- queues = self.class.queues | run_at_by_queue.keys
31
+ sql = 'SELECT queue, min(run_at) FROM delayed_jobs GROUP BY queue'
32
+ run_at_by_queue = Hash[ActiveRecord::Base.connection.select_rows(sql)]
33
+ queues = self.class.queues | run_at_by_queue.keys
34
34
 
35
- queues.each do |queue|
36
- next if queue.nil? || queue.empty?
37
- run_at = run_at_by_queue[queue]
38
- run_at = Time.parse(run_at) if run_at.is_a?(String)
39
- latency_ms = run_at ? ((t - run_at)*1000).ceil : 0
40
- store.push latency_ms, t, queue
41
- log_msg << "#{queue}=#{latency_ms} "
42
- end
35
+ queues.each do |queue|
36
+ next if queue.nil? || queue.empty?
37
+ run_at = run_at_by_queue[queue]
38
+ run_at = Time.parse(run_at) if run_at.is_a?(String)
39
+ latency_ms = run_at ? ((t - run_at)*1000).ceil : 0
40
+ store.push latency_ms, t, queue
41
+ log_msg << "dj.#{queue}=#{latency_ms} "
42
+ end
43
43
 
44
- logger.debug log_msg
45
- end
44
+ logger.debug log_msg unless log_msg.empty?
45
+ end
46
46
 
47
- private
47
+ private
48
48
 
49
- def install
50
- plugin = Class.new(Delayed::Plugin) do
51
- require 'delayed_job'
49
+ def install
50
+ plugin = Class.new(Delayed::Plugin) do
51
+ require 'delayed_job'
52
52
 
53
- callbacks do |lifecycle|
54
- lifecycle.before(:enqueue) do |job, &block|
55
- queue = job.queue || 'default'
56
- WorkerAdapters::DelayedJob.queues.add queue
53
+ callbacks do |lifecycle|
54
+ lifecycle.before(:enqueue) do |job, &block|
55
+ queue = job.queue || 'default'
56
+ WorkerAdapters::DelayedJob.queues.add queue
57
+ end
57
58
  end
58
59
  end
59
- end
60
60
 
61
- Delayed::Worker.plugins << plugin
61
+ Delayed::Worker.plugins << plugin
62
+ end
62
63
  end
63
64
  end
64
65
  end
@@ -3,44 +3,46 @@
3
3
  require 'rails_autoscale_agent/logger'
4
4
  require 'time'
5
5
 
6
- module WorkerAdapters
7
- class Que
8
- include RailsAutoscaleAgent::Logger
9
- include Singleton
6
+ module RailsAutoscaleAgent
7
+ module WorkerAdapters
8
+ class Que
9
+ include RailsAutoscaleAgent::Logger
10
+ include Singleton
10
11
 
11
- DEFAULT_QUEUES = ['default']
12
+ DEFAULT_QUEUES = ['default']
12
13
 
13
- class << self
14
- attr_accessor :queues
15
- end
16
-
17
- def initialize
18
- self.class.queues = DEFAULT_QUEUES
19
- end
14
+ class << self
15
+ attr_accessor :queues
16
+ end
20
17
 
21
- def enabled?
22
- defined? ::Que
23
- end
18
+ def initialize
19
+ self.class.queues = DEFAULT_QUEUES
20
+ end
24
21
 
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} "
22
+ def enabled?
23
+ defined? ::Que
41
24
  end
42
25
 
43
- logger.debug log_msg
26
+ def collect!(store)
27
+ log_msg = String.new
28
+ t = Time.now
29
+
30
+ # Ignore failed jobs (they skew latency measurement due to the original run_at)
31
+ sql = 'SELECT queue, min(run_at) FROM que_jobs WHERE error_count = 0 GROUP BY queue'
32
+ run_at_by_queue = Hash[ActiveRecord::Base.connection.select_rows(sql)]
33
+ self.class.queues |= run_at_by_queue.keys
34
+
35
+ self.class.queues.each do |queue|
36
+ next if queue.nil? || queue.empty?
37
+ run_at = run_at_by_queue[queue]
38
+ run_at = Time.parse(run_at) if run_at.is_a?(String)
39
+ latency_ms = run_at ? ((t - run_at)*1000).ceil : 0
40
+ store.push latency_ms, t, queue
41
+ log_msg << "que.#{queue}=#{latency_ms} "
42
+ end
43
+
44
+ logger.debug log_msg unless log_msg.empty?
45
+ end
44
46
  end
45
47
  end
46
48
  end
@@ -0,0 +1,32 @@
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
+ def enabled?
12
+ require 'resque'
13
+ true
14
+ rescue LoadError
15
+ false
16
+ end
17
+
18
+ def collect!(store)
19
+ log_msg = String.new
20
+
21
+ ::Resque.queues.each do |queue|
22
+ next if queue.nil? || queue.empty?
23
+ depth = ::Resque.size(queue)
24
+ store.push depth, Time.now, queue, :qd
25
+ log_msg << "resque-qd.#{queue}=#{depth} "
26
+ end
27
+
28
+ logger.debug log_msg
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,28 +2,32 @@
2
2
 
3
3
  require 'rails_autoscale_agent/logger'
4
4
 
5
- module WorkerAdapters
6
- class Sidekiq
7
- include RailsAutoscaleAgent::Logger
8
- include Singleton
5
+ module RailsAutoscaleAgent
6
+ module WorkerAdapters
7
+ class Sidekiq
8
+ include RailsAutoscaleAgent::Logger
9
+ include Singleton
9
10
 
10
- def enabled?
11
- require 'sidekiq/api'
12
- true
13
- rescue LoadError
14
- false
15
- end
11
+ def enabled?
12
+ require 'sidekiq/api'
13
+ true
14
+ rescue LoadError
15
+ false
16
+ end
16
17
 
17
- def collect!(store)
18
- log_msg = String.new('Sidekiq latency ')
18
+ def collect!(store)
19
+ log_msg = String.new
19
20
 
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} "
24
- end
21
+ ::Sidekiq::Queue.all.each do |queue|
22
+ latency_ms = (queue.latency * 1000).ceil
23
+ depth = queue.size
24
+ store.push latency_ms, Time.now, queue.name, :qt
25
+ store.push depth, Time.now, queue.name, :qd
26
+ log_msg << "sidekiq-qt.#{queue.name}=#{latency_ms} sidekiq-qd.#{queue.name}=#{depth} "
27
+ end
25
28
 
26
- logger.debug log_msg
29
+ logger.debug log_msg
30
+ end
27
31
  end
28
32
  end
29
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_autoscale_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.9.0.beta.1
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-26 00:00:00.000000000 Z
11
+ date: 2020-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -198,6 +198,7 @@ files:
198
198
  - lib/rails_autoscale_agent/version.rb
199
199
  - lib/rails_autoscale_agent/worker_adapters/delayed_job.rb
200
200
  - lib/rails_autoscale_agent/worker_adapters/que.rb
201
+ - lib/rails_autoscale_agent/worker_adapters/resque.rb
201
202
  - lib/rails_autoscale_agent/worker_adapters/sidekiq.rb
202
203
  - log/.gitkeep
203
204
  - rails_autoscale_agent.gemspec
@@ -216,9 +217,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
216
217
  version: '0'
217
218
  required_rubygems_version: !ruby/object:Gem::Requirement
218
219
  requirements:
219
- - - ">="
220
+ - - ">"
220
221
  - !ruby/object:Gem::Version
221
- version: '0'
222
+ version: 1.3.1
222
223
  requirements: []
223
224
  rubygems_version: 3.0.3
224
225
  signing_key: