Commit 87d0c9c3 authored by Matthias Kaeppler's avatar Matthias Kaeppler

Run GC after each metrics request

This cuts down on long-term memory use with
a small increase in request latency.
parent 909295c8
......@@ -11,10 +11,11 @@ module Gitlab
attr_accessor :readiness_checks
def initialize(settings, log_enabled:, log_file:, **options)
def initialize(settings, log_enabled:, log_file:, gc_requests: false, **options)
super(**options)
@settings = settings
@gc_requests = gc_requests
# log_enabled does not exist for all exporters
log_sink = log_enabled ? File.join(Rails.root, 'log', log_file) : File::NULL
......@@ -71,11 +72,13 @@ module Gitlab
readiness = readiness_probe
liveness = liveness_probe
pid = thread_name
gc_requests = @gc_requests
Rack::Builder.app do
use Rack::Deflater
use Gitlab::Metrics::Exporter::MetricsMiddleware, pid
use Gitlab::Metrics::Exporter::HealthChecksMiddleware, readiness, liveness
use Gitlab::Metrics::Exporter::GcRequestMiddleware if gc_requests
use ::Prometheus::Client::Rack::Exporter if ::Gitlab::Metrics.metrics_folder_present?
run -> (env) { [404, {}, ['']] }
end
......
# frozen_string_literal: true
module Gitlab
module Metrics
module Exporter
class GcRequestMiddleware
def initialize(app)
@app = app
end
def call(env)
@app.call(env).tap do
GC.start
end
end
end
end
end
end
......@@ -26,6 +26,7 @@ require_relative '../lib/gitlab/metrics/exporter/base_exporter'
require_relative '../lib/gitlab/metrics/exporter/sidekiq_exporter'
require_relative '../lib/gitlab/metrics/exporter/metrics_middleware'
require_relative '../lib/gitlab/metrics/exporter/health_checks_middleware'
require_relative '../lib/gitlab/metrics/exporter/gc_request_middleware'
require_relative '../lib/gitlab/health_checks/probes/collection'
require_relative '../lib/gitlab/health_checks/probes/status'
require_relative '../lib/gitlab/process_management'
......
......@@ -54,7 +54,7 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
exporter_class = "Gitlab::Metrics::Exporter::#{@target.camelize}Exporter".constantize
settings = Settings.new(Settings.monitoring[name])
server = exporter_class.instance(settings, synchronous: true)
server = exporter_class.instance(settings, gc_requests: true, synchronous: true)
server.start
end
......
# frozen_string_literal: true
require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Exporter::GcRequestMiddleware do
let(:app) { double(:app) }
let(:env) { {} }
subject(:middleware) { described_class.new(app) }
describe '#call' do
it 'runs a major GC after the next middleware is called' do
expect(app).to receive(:call).with(env).ordered.and_return([200, {}, []])
expect(GC).to receive(:start).ordered
response = middleware.call(env)
expect(response).to eq([200, {}, []])
end
end
end
......@@ -70,7 +70,9 @@ RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
before do
stub_const('Gitlab::Metrics::Exporter::FakeExporter', exporter_class)
expect(exporter_class).to receive(:instance).with(settings['fake_exporter'], synchronous: true).and_return(exporter_double)
expect(exporter_class).to receive(:instance).with(
settings['fake_exporter'], gc_requests: true, synchronous: true
).and_return(exporter_double)
expect(Settings).to receive(:monitoring).and_return(settings)
allow(Gitlab::Metrics::Samplers::RubySampler).to receive(:initialize_instance).and_return(ruby_sampler_double)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment