rails-autoscale-core 1.3.1 → 1.4.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: 36c26aeba2eb5413f339a8c1c756632a4299b7c397a05cfbea325c01ee562c28
4
- data.tar.gz: '039d32858527abd7b01b804491ed0296b19c77c91216cd7d5eec35b3ad90edf8'
3
+ metadata.gz: 858deb71031ca2f69317d6117a9deb1336851a671aa5c2de5067ce52f9bdaa81
4
+ data.tar.gz: f057b846ef8a46e770f4cb4a2718e2569fc99f28a1a8c971366ea9d9d418dfd2
5
5
  SHA512:
6
- metadata.gz: dbefb2b2c8f48a7ffd4e6b4b2a51cf4b54c545fae1c5db55d9dff45b3e33338ee34507f2950d7ab7e1a60ba0606957f6b87ec1ccffeaae812a723a2fd9603d98
7
- data.tar.gz: f2e0ae3ffd1af3b4afd841b73e3dc17128ee7937a3b5e56a6a20264c1b63b62bf5ea31593458f8595a083990f313402c524c8017b49695ffa3a21bfd4bc70bad
6
+ metadata.gz: be79605f4d962debdfc3027b634d320ff4ad9f7b0d17d8bd77d296f5ecd5b9fad834cc7356e8cefbf1d4fd4697fd9cc6ddcfc41ef51209681de2d9c21dd393b8
7
+ data.tar.gz: 6a61c37727c32d6902e1ac604af7dcd8169d24eb8950413874e55d170852e30f706bbf7d37d4ec32948eb1cdc9f945229bc1f36ba1f04b3a105ca03ab515ac52
data/Gemfile CHANGED
@@ -3,7 +3,6 @@ source "https://rubygems.org"
3
3
  gemspec name: "judoscale-ruby"
4
4
 
5
5
  gem "rake", ">= 12.3.3"
6
- gem "rake-release"
7
6
  gem "minitest"
8
7
  gem "webmock"
9
8
  gem "debug"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- judoscale-ruby (1.3.1)
4
+ judoscale-ruby (1.4.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -20,8 +20,6 @@ GEM
20
20
  minitest (5.17.0)
21
21
  public_suffix (5.0.1)
22
22
  rake (13.0.6)
23
- rake-release (1.3.0)
24
- bundler (>= 1.11, < 3)
25
23
  reline (0.3.2)
26
24
  io-console (~> 0.5)
27
25
  rexml (3.2.5)
@@ -41,7 +39,6 @@ DEPENDENCIES
41
39
  judoscale-ruby!
42
40
  minitest
43
41
  rake (>= 12.3.3)
44
- rake-release
45
42
  webmock
46
43
 
47
44
  BUNDLED WITH
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rake/release"
4
3
  require "rake/testtask"
5
4
 
6
5
  Rake::TestTask.new(:test) do |t|
@@ -5,8 +5,8 @@ require "judoscale/version"
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "judoscale-ruby"
7
7
  spec.version = Judoscale::VERSION
8
- spec.authors = ["Adam McCrea", "Carlos Antonio da Silva"]
9
- spec.email = ["adam@adamlogic.com"]
8
+ spec.authors = ["Adam McCrea", "Carlos Antonio da Silva", "Jon Sullivan"]
9
+ spec.email = ["hello@judoscale.com"]
10
10
 
11
11
  spec.summary = "This gem works with the Judoscale Heroku add-on to automatically scale your web and worker dynos."
12
12
  spec.homepage = "https://judoscale.com"
@@ -5,16 +5,35 @@ require "logger"
5
5
 
6
6
  module Judoscale
7
7
  class Config
8
- class Dyno
9
- attr_reader :name, :num
10
-
11
- def initialize(dyno_string)
12
- @name, @num = dyno_string.to_s.split(".")
13
- @num = @num.to_i
8
+ class RuntimeContainer
9
+ # E.g.:
10
+ # (Heroku) => "worker_fast", "3"
11
+ # (Render) => "srv-cfa1es5a49987h4vcvfg", "5497f74465-m5wwr", "web" (or "worker", "pserv", "cron", "static")
12
+ def initialize(service_name = nil, instance = nil, service_type = nil)
13
+ @service_name = service_name
14
+ @instance = instance
15
+ @service_type = service_type
14
16
  end
15
17
 
16
18
  def to_s
17
- "#{name}.#{num}"
19
+ # heroku: 'worker_fast.5'
20
+ # render: 'srv-cfa1es5a49987h4vcvfg.5497f74465-m5wwr'
21
+ "#{@service_name}.#{@instance}"
22
+ end
23
+
24
+ def web?
25
+ # NOTE: Heroku isolates 'web' as the required _name_ for its web process
26
+ # type, Render exposes the actual service type more explicitly
27
+ @service_name == "web" || @service_type == "web"
28
+ end
29
+
30
+ # Since Heroku exposes ordinal dyno 'numbers', we can tell if the current
31
+ # instance is redundant (and thus skip collecting some metrics sometimes)
32
+ # We don't have a means of determining that on Render though — so every
33
+ # instance must be considered non-redundant
34
+ def redundant_instance?
35
+ instance_is_number = Integer(@instance, exception: false)
36
+ instance_is_number && instance_is_number != 1
18
37
  end
19
38
  end
20
39
 
@@ -66,28 +85,36 @@ module Judoscale
66
85
  end
67
86
  end
68
87
 
69
- attr_accessor :api_base_url, :report_interval_seconds, :max_request_size_bytes, :logger, :log_tag
70
- attr_reader :dyno, :log_level
88
+ attr_accessor :api_base_url, :report_interval_seconds,
89
+ :max_request_size_bytes, :logger, :log_tag, :current_runtime_container
90
+ attr_reader :log_level
71
91
 
72
92
  def initialize
73
93
  reset
74
94
  end
75
95
 
76
96
  def reset
77
- # Allow the API URL to be configured - needed for testing.
78
97
  @api_base_url = ENV["JUDOSCALE_URL"] || ENV["RAILS_AUTOSCALE_URL"]
79
98
  @log_tag = "Judoscale"
80
- self.dyno = ENV["DYNO"]
81
99
  @max_request_size_bytes = 100_000 # ignore request payloads over 100k since they skew the queue times
82
100
  @report_interval_seconds = 10
101
+
83
102
  self.log_level = ENV["JUDOSCALE_LOG_LEVEL"] || ENV["RAILS_AUTOSCALE_LOG_LEVEL"]
84
103
  @logger = ::Logger.new($stdout)
85
104
 
86
105
  self.class.adapter_configs.each(&:reset)
87
- end
88
106
 
89
- def dyno=(dyno_string)
90
- @dyno = Dyno.new(dyno_string)
107
+ if ENV["RENDER_INSTANCE_ID"]
108
+ instance = ENV["RENDER_INSTANCE_ID"].delete_prefix(ENV["RENDER_SERVICE_ID"]).delete_prefix("-")
109
+ @current_runtime_container = RuntimeContainer.new ENV["RENDER_SERVICE_ID"], instance, ENV["RENDER_SERVICE_TYPE"]
110
+ @api_base_url ||= "https://adapter.judoscale.com/api/#{ENV["RENDER_SERVICE_ID"]}"
111
+ elsif ENV["DYNO"]
112
+ service_name, instance = ENV["DYNO"].split "."
113
+ @current_runtime_container = RuntimeContainer.new service_name, instance
114
+ else
115
+ # unsupported platform? Don't want to leave @current_runtime_container nil though
116
+ @current_runtime_container = RuntimeContainer.new
117
+ end
91
118
  end
92
119
 
93
120
  def log_level=(new_level)
@@ -105,10 +132,6 @@ module Judoscale
105
132
  }.merge!(adapter_configs_json)
106
133
  end
107
134
 
108
- def to_s
109
- "#{@dyno}##{Process.pid}"
110
- end
111
-
112
135
  def ignore_large_requests?
113
136
  @max_request_size_bytes
114
137
  end
@@ -8,9 +8,8 @@ module Judoscale
8
8
  class JobMetricsCollector < MetricsCollector
9
9
  include Judoscale::Logger
10
10
 
11
- # It's redundant to report these metrics from every dyno, so only report from the first one.
12
11
  def self.collect?(config)
13
- config.dyno.num == 1 && adapter_config.enabled
12
+ !config.current_runtime_container.redundant_instance? && adapter_config.enabled
14
13
  end
15
14
 
16
15
  def self.adapter_name
@@ -12,7 +12,7 @@ module Judoscale
12
12
 
13
13
  def as_json
14
14
  {
15
- dyno: config.dyno,
15
+ container: config.current_runtime_container,
16
16
  pid: Process.pid,
17
17
  config: config.as_json,
18
18
  adapters: adapters.reduce({}) { |hash, adapter| hash.merge!(adapter.as_json) },
@@ -20,7 +20,7 @@ module Judoscale
20
20
  @pid = Process.pid
21
21
 
22
22
  if !config.api_base_url
23
- logger.info "Reporter not started: JUDOSCALE_URL is not set"
23
+ logger.debug "Reporter not started: JUDOSCALE_URL is not set"
24
24
  return
25
25
  end
26
26
 
@@ -31,7 +31,7 @@ module Judoscale
31
31
  metrics_collectors_classes.compact!
32
32
 
33
33
  if metrics_collectors_classes.empty?
34
- logger.info "Reporter not started: no metrics need to be collected on this dyno"
34
+ logger.debug "Reporter not started: no metrics need to be collected in this process"
35
35
  return
36
36
  end
37
37
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Judoscale
4
4
  class RequestMetrics
5
+ MILLISECONDS_CUTOFF = Time.new(2000, 1, 1).to_i * 1000
6
+ MICROSECONDS_CUTOFF = MILLISECONDS_CUTOFF * 1000
7
+ NANOSECONDS_CUTOFF = MICROSECONDS_CUTOFF * 1000
8
+
5
9
  attr_reader :request_id, :size, :network_time
6
10
 
7
11
  def initialize(env, config = Config.instance)
@@ -20,15 +24,23 @@ module Judoscale
20
24
  if @request_start_header
21
25
  # There are several variants of this header. We handle these:
22
26
  # - whole milliseconds (Heroku)
27
+ # - whole microseconds (???)
23
28
  # - whole nanoseconds (Render)
24
29
  # - fractional seconds (NGINX)
25
30
  # - preceeding "t=" (NGINX)
26
31
  value = @request_start_header.gsub(/[^0-9.]/, "").to_f
27
32
 
28
- case value
29
- when 0..100_000_000_000 then Time.at(value)
30
- when 100_000_000_000..100_000_000_000_000 then Time.at(value / 1000.0)
31
- else Time.at(value / 1_000_000.0)
33
+ # `value` could be seconds, milliseconds, microseconds or nanoseconds.
34
+ # We use some arbitrary cutoffs to determine which one it is.
35
+
36
+ if value > NANOSECONDS_CUTOFF
37
+ Time.at(value / 1_000_000_000.0)
38
+ elsif value > MICROSECONDS_CUTOFF
39
+ Time.at(value / 1_000_000.0)
40
+ elsif value > MILLISECONDS_CUTOFF
41
+ Time.at(value / 1000.0)
42
+ else
43
+ Time.at(value)
32
44
  end
33
45
  end
34
46
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Judoscale
4
- VERSION = "1.3.1"
4
+ VERSION = "1.4.1"
5
5
  end
@@ -5,8 +5,10 @@ require "judoscale/metrics_store"
5
5
 
6
6
  module Judoscale
7
7
  class WebMetricsCollector < MetricsCollector
8
+ # NOTE: We collect metrics on all running web processes since they
9
+ # all receive and handle requests independently
8
10
  def self.collect?(config)
9
- config.dyno.name == "web"
11
+ config.current_runtime_container.web?
10
12
  end
11
13
 
12
14
  def collect
@@ -5,8 +5,8 @@ require "judoscale/version"
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "rails-autoscale-core"
7
7
  spec.version = Judoscale::VERSION
8
- spec.authors = ["Adam McCrea", "Carlos Antonio da Silva"]
9
- spec.email = ["adam@adamlogic.com"]
8
+ spec.authors = ["Adam McCrea", "Carlos Antonio da Silva", "Jon Sullivan"]
9
+ spec.email = ["hello@judoscale.com"]
10
10
 
11
11
  spec.summary = "This gem works with the Judoscale Heroku add-on to automatically scale your web and worker dynos."
12
12
  spec.homepage = "https://judoscale.com"
metadata CHANGED
@@ -1,19 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-autoscale-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam McCrea
8
8
  - Carlos Antonio da Silva
9
+ - Jon Sullivan
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2023-03-11 00:00:00.000000000 Z
13
+ date: 2023-05-04 00:00:00.000000000 Z
13
14
  dependencies: []
14
15
  description:
15
16
  email:
16
- - adam@adamlogic.com
17
+ - hello@judoscale.com
17
18
  executables: []
18
19
  extensions: []
19
20
  extra_rdoc_files: []