neetodeploy-autoscale 1.0.6 → 2.0.0
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 +4 -4
- data/lib/neetodeploy/autoscale/config.rb +19 -0
- data/lib/neetodeploy/autoscale/logger.rb +15 -0
- data/lib/neetodeploy/autoscale/metrics.rb +26 -0
- data/lib/neetodeploy/autoscale/metrics_collector.rb +53 -0
- data/lib/neetodeploy/autoscale/metrics_store.rb +27 -0
- data/lib/neetodeploy/autoscale/middleware.rb +11 -28
- data/lib/neetodeploy/autoscale/railtie.rb +8 -6
- data/lib/neetodeploy/autoscale/reporter.rb +27 -0
- data/lib/neetodeploy/autoscale/version.rb +1 -1
- data/lib/neetodeploy-autoscale.rb +13 -9
- metadata +8 -4
- data/lib/neetodeploy/autoscale/exporter.rb +0 -21
- data/lib/neetodeploy/autoscale/sidekiq_middleware.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37bcdd5f87684bd29d215c8ed744b8ff6963a6a57d1af07c5ddadc407f4462d5
|
4
|
+
data.tar.gz: 05fac864a8cca54ac8773e6ec96773d4918961c32fdf1b27ff61049d34241c7f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf13f8872227a9e0685460a13cbddeab3d10f5da67c55f96bd533faddb85cb153ded05cebd523d29d83690d09317999dda2fc094aa06b9bcc9e77306bb322603
|
7
|
+
data.tar.gz: a8809427527fe868c571ee014b79e242379f1ee9ff432e323dfaed5b8ede759f0fd75ca67dccd610bd8c7eadfe358e3415a3f1c4c66ed2d074e1e3073f8de353
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Neetodeploy
|
2
|
+
class Config
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
attr_accessor :disable_auto_scale_gem, :app_name, :metrics_server_url, :metrics_server_auth_token, :report_interval_seconds
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@disable_auto_scale_gem = ENV["DISABLE_NEETO_DEPLOY_AUTOSCALE"]
|
9
|
+
@app_name = ENV["NEETODEPLOY_APP_NAME"]
|
10
|
+
@metrics_server_url = "http://nd-queue-time-exporter-web-deployment:3000/metrics"
|
11
|
+
@metrics_server_auth_token = "K0An3O3MSyEEMTCnRd1IHgGjdGQkzy"
|
12
|
+
@report_interval_seconds = 10
|
13
|
+
end
|
14
|
+
|
15
|
+
def gem_disabled?
|
16
|
+
disable_auto_scale_gem == "true"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module NeetoDeploy
|
4
|
+
module Logger
|
5
|
+
def self.logger
|
6
|
+
@logger ||= ::Logger.new(STDOUT).tap do |log|
|
7
|
+
log.formatter = proc { |severity, _datetime, _progname, msg| "[neetodeploy-autoscale] #{severity}: #{msg}\n" }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def logger
|
12
|
+
NeetoDeploy::Logger.logger
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "neetodeploy/autoscale/config"
|
2
|
+
|
3
|
+
module Neetodeploy
|
4
|
+
class Metrics
|
5
|
+
def initialize(env, config = Config.instance)
|
6
|
+
@config = config
|
7
|
+
@request_start_header = env["HTTP_X_REQUEST_START"].to_i
|
8
|
+
@network_time = env["puma.request_body_wait"].to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
def ignore?
|
12
|
+
@config.gem_disabled?
|
13
|
+
end
|
14
|
+
|
15
|
+
def queue_time
|
16
|
+
return if @request_start_header.zero?
|
17
|
+
|
18
|
+
time_now = Time.now.to_f * 1000
|
19
|
+
|
20
|
+
queue_time = (time_now - @request_start_header).round
|
21
|
+
queue_time -= @network_time
|
22
|
+
|
23
|
+
queue_time.positive? ? queue_time : nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
require "neetodeploy/autoscale/metrics_store"
|
5
|
+
require "neetodeploy/autoscale/reporter"
|
6
|
+
require "neetodeploy/autoscale/logger"
|
7
|
+
|
8
|
+
module Neetodeploy
|
9
|
+
class MetricsCollector
|
10
|
+
include Singleton
|
11
|
+
include NeetoDeploy::Logger
|
12
|
+
|
13
|
+
def self.start
|
14
|
+
instance.start! unless instance.running?
|
15
|
+
end
|
16
|
+
|
17
|
+
def start!
|
18
|
+
logger.info("Starting background worker to collect metrics")
|
19
|
+
@pid = Process.pid
|
20
|
+
start_thread_with_collector_loop
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop!
|
24
|
+
@thread&.terminate
|
25
|
+
@thread = nil
|
26
|
+
@pid = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def running?
|
30
|
+
@pid == Process.pid and @thread.alive?
|
31
|
+
end
|
32
|
+
|
33
|
+
def start_thread_with_collector_loop(config = Config.instance)
|
34
|
+
@thread = Thread.new do
|
35
|
+
metrics_store = MetricsStore.instance
|
36
|
+
loop do
|
37
|
+
run_metrics_collection(metrics_store)
|
38
|
+
multiplier = 1 - (rand / 4)
|
39
|
+
sleep config.report_interval_seconds * multiplier
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def run_metrics_collection(metrics_store)
|
45
|
+
data = metrics_store.flush
|
46
|
+
return if data.empty?
|
47
|
+
|
48
|
+
average_queue_time = data.sum / data.size
|
49
|
+
logger.info("Reporting average queue time of #{data.size} metrics: #{average_queue_time}")
|
50
|
+
Reporter.new(average_queue_time).report
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
require "judoscale/metric"
|
5
|
+
require "judoscale/report"
|
6
|
+
|
7
|
+
module Neetodeploy
|
8
|
+
class MetricsStore
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
attr_reader :metrics
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@metrics = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def push(queue_time)
|
18
|
+
@metrics << queue_time
|
19
|
+
end
|
20
|
+
|
21
|
+
def flush
|
22
|
+
result = @metrics
|
23
|
+
@metrics = []
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -2,44 +2,27 @@
|
|
2
2
|
|
3
3
|
require "net/http"
|
4
4
|
require "time"
|
5
|
-
require "neetodeploy/autoscale/
|
5
|
+
require "neetodeploy/autoscale/metrics"
|
6
|
+
require "neetodeploy/autoscale/metrics_store"
|
7
|
+
require "neetodeploy/autoscale/metrics_collector"
|
6
8
|
|
7
9
|
module Neetodeploy
|
8
10
|
class Middleware
|
9
|
-
|
10
11
|
def initialize(app)
|
11
12
|
@app = app
|
12
13
|
end
|
13
14
|
|
14
15
|
def call(env)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
queue_time: queue_time(env)
|
19
|
-
)
|
20
|
-
Neetodeploy::Exporter.export(payload)
|
21
|
-
@app.call(env)
|
22
|
-
end
|
16
|
+
metrics = Metrics.new(env)
|
17
|
+
queue_time = metrics.queue_time unless metrics.ignore?
|
18
|
+
MetricsCollector.start
|
23
19
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
env["HTTP_X_REQUEST_START"].to_i
|
30
|
-
end
|
20
|
+
if queue_time
|
21
|
+
store = MetricsStore.instance
|
22
|
+
store.push queue_time
|
23
|
+
end
|
31
24
|
|
32
|
-
|
33
|
-
env["puma.request_body_wait"].to_i
|
34
|
-
end
|
35
|
-
|
36
|
-
def queue_time(now = Time.now.to_f * 1000, env)
|
37
|
-
return if started_at(env).nil?
|
38
|
-
|
39
|
-
queue_time = (now - started_at(env)).round
|
40
|
-
queue_time -= network_time(env)
|
41
|
-
|
42
|
-
queue_time.positive? ? queue_time : 0
|
25
|
+
@app.call(env)
|
43
26
|
end
|
44
27
|
end
|
45
28
|
end
|
@@ -1,17 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "neetodeploy/autoscale/middleware"
|
3
4
|
module Neetodeploy
|
4
5
|
class Railtie < Rails::Railtie
|
5
|
-
initializer "neetodeploy.
|
6
|
-
|
6
|
+
initializer "neetodeploy.Neetodeploy.middleware" do |app|
|
7
|
+
app.middleware.insert_before Rack::Runtime, Neetodeploy::Middleware
|
7
8
|
end
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
config.after_initialize do
|
11
|
+
MetricsCollector.start unless in_rails_console_or_runner?
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
-
|
14
|
+
def in_rails_console_or_runner?
|
15
|
+
# This is gross, but we can't find a more reliable way to detect if we're in a Rails console/runner.
|
16
|
+
caller.any? { |call| call.include?("console_command.rb") || call.include?("runner_command.rb") }
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "time"
|
5
|
+
require "neetodeploy/autoscale/config"
|
6
|
+
|
7
|
+
module Neetodeploy
|
8
|
+
class Reporter
|
9
|
+
|
10
|
+
def initialize(queue_time, config = Config.instance )
|
11
|
+
@queue_time = queue_time.to_i
|
12
|
+
@config = config
|
13
|
+
end
|
14
|
+
|
15
|
+
def report
|
16
|
+
url = URI.parse(@config.metrics_server_url)
|
17
|
+
url.query = URI.encode_www_form(app_name: @config.app_name, process_type: "web", queue_time: @queue_time)
|
18
|
+
|
19
|
+
post = Net::HTTP::Post.new(url)
|
20
|
+
post["AuthToken"] = @config.metrics_server_auth_token
|
21
|
+
|
22
|
+
Net::HTTP.start(url.host, url.port, use_ssl: false) do |http|
|
23
|
+
http.request(post)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,33 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "neetodeploy/autoscale/version"
|
4
|
+
require "neetodeploy/autoscale/logger"
|
4
5
|
|
5
6
|
module Neetodeploy
|
6
7
|
module Autoscale
|
7
8
|
class Error < StandardError; end
|
8
9
|
|
9
10
|
class << self
|
11
|
+
include NeetoDeploy::Logger
|
12
|
+
|
10
13
|
def setup_middleware
|
11
|
-
require "neetodeploy/autoscale/middleware"
|
12
14
|
Neetodeploy::Autoscale.setup_rails if defined? Rails
|
13
|
-
Neetodeploy::Autoscale.setup_sidekiq if defined?(Sidekiq)
|
14
15
|
end
|
15
16
|
|
16
17
|
def setup_rails
|
17
18
|
require "neetodeploy/autoscale/railtie"
|
18
19
|
end
|
19
20
|
|
20
|
-
def
|
21
|
-
|
21
|
+
def enable_middleware?
|
22
|
+
ENV["NEETODEPLOY_APP_NAME"].present? && ENV["DISABLE_NEETO_DEPLOY_AUTOSCALE"] != "true"
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
def initialize_if_enabled
|
26
|
+
if enable_middleware?
|
27
|
+
logger.info("Setting up Neetodeploy autoscale middleware")
|
28
|
+
setup_middleware
|
29
|
+
else
|
30
|
+
logger.info("Conditions not met skipping NeetoDeploy autoscale middleware setup")
|
27
31
|
end
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
|
-
|
37
|
+
Neetodeploy::Autoscale.initialize_if_enabled
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neetodeploy-autoscale
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sreeram Venkitesh
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: For automatically scaling your Rails application based on network metrics
|
14
14
|
email:
|
@@ -23,10 +23,14 @@ files:
|
|
23
23
|
- README.md
|
24
24
|
- Rakefile
|
25
25
|
- lib/neetodeploy-autoscale.rb
|
26
|
-
- lib/neetodeploy/autoscale/
|
26
|
+
- lib/neetodeploy/autoscale/config.rb
|
27
|
+
- lib/neetodeploy/autoscale/logger.rb
|
28
|
+
- lib/neetodeploy/autoscale/metrics.rb
|
29
|
+
- lib/neetodeploy/autoscale/metrics_collector.rb
|
30
|
+
- lib/neetodeploy/autoscale/metrics_store.rb
|
27
31
|
- lib/neetodeploy/autoscale/middleware.rb
|
28
32
|
- lib/neetodeploy/autoscale/railtie.rb
|
29
|
-
- lib/neetodeploy/autoscale/
|
33
|
+
- lib/neetodeploy/autoscale/reporter.rb
|
30
34
|
- lib/neetodeploy/autoscale/version.rb
|
31
35
|
homepage: https://neetodeploy.com
|
32
36
|
licenses: []
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "net/http"
|
4
|
-
require "time"
|
5
|
-
|
6
|
-
module Neetodeploy
|
7
|
-
class Exporter
|
8
|
-
def self.export(payload)
|
9
|
-
Thread.new do
|
10
|
-
url = URI.parse("http://nd-queue-time-exporter-web-deployment:3000/metrics")
|
11
|
-
url.query = payload
|
12
|
-
post = Net::HTTP::Post.new(url)
|
13
|
-
post["AuthToken"] = "K0An3O3MSyEEMTCnRd1IHgGjdGQkzy"
|
14
|
-
|
15
|
-
Net::HTTP.start(url.host, url.port, use_ssl: false) do |http|
|
16
|
-
http.request(post)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "net/http"
|
4
|
-
require "time"
|
5
|
-
|
6
|
-
module Neetodeploy
|
7
|
-
class SidekiqMiddleware
|
8
|
-
def call(worker, job, queue)
|
9
|
-
url = URI.parse("http://nd-queue-time-exporter-web-deployment:3000/metrics")
|
10
|
-
|
11
|
-
queues_by_name = ::Sidekiq::Queue.all.each_with_object({}) do |queue_name, obj|
|
12
|
-
obj[queue_name.name] = queue_name
|
13
|
-
end
|
14
|
-
|
15
|
-
queues = queues_by_name.keys
|
16
|
-
|
17
|
-
queues.each do |queue_obj|
|
18
|
-
queue = queues_by_name.fetch(queue_obj) { |name| ::Sidekiq::Queue.new(name) }
|
19
|
-
|
20
|
-
query_params = URI.encode_www_form(app_name: ENV.to_h["NEETODEPLOY_APP_NAME"], process_type: "worker", queue_name: queue_obj, queue_time: queue_time(queue))
|
21
|
-
url.query = query_params
|
22
|
-
post = Net::HTTP::Post.new(url)
|
23
|
-
post["AuthToken"] = "K0An3O3MSyEEMTCnRd1IHgGjdGQkzy"
|
24
|
-
|
25
|
-
begin
|
26
|
-
Net::HTTP.start(url.host, url.port, use_ssl: false) do |http|
|
27
|
-
http.request(post)
|
28
|
-
end
|
29
|
-
rescue => e
|
30
|
-
puts "Exception in sending post request to exporter from Sidekiq process: #{e.message}"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
yield
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def queues
|
40
|
-
::Sidekiq::Queue.all
|
41
|
-
end
|
42
|
-
|
43
|
-
def queue_time(queue_obj)
|
44
|
-
queue = ::Sidekiq::Queue.new(queue_obj.name)
|
45
|
-
|
46
|
-
(queue.latency * 1000).ceil
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|