rails-autoscale-core 1.3.1 → 1.4.1

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.
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: []