gitlab-org--gitlab-foss/lib/gitlab/metrics/samplers/puma_sampler.rb
Jan Provaznik d4a83ce5a3 Ignore Puma empty worker stats
In some cases (during worker start) it's possible that
Puma.stats returns an empty hash for worker's last status. In
that case we just skip sampling of the worker until these
stats are available.
2019-06-04 09:11:55 +00:00

93 lines
3.6 KiB
Ruby

# frozen_string_literal: true
require 'puma/state_file'
module Gitlab
module Metrics
module Samplers
class PumaSampler < BaseSampler
def metrics
@metrics ||= init_metrics
end
def init_metrics
{
puma_workers: ::Gitlab::Metrics.gauge(:puma_workers, 'Total number of workers'),
puma_running_workers: ::Gitlab::Metrics.gauge(:puma_running_workers, 'Number of active workers'),
puma_stale_workers: ::Gitlab::Metrics.gauge(:puma_stale_workers, 'Number of stale workers'),
puma_phase: ::Gitlab::Metrics.gauge(:puma_phase, 'Phase number (increased during phased restarts)'),
puma_running: ::Gitlab::Metrics.gauge(:puma_running, 'Number of running threads'),
puma_queued_connections: ::Gitlab::Metrics.gauge(:puma_queued_connections, 'Number of connections in that worker\'s "todo" set waiting for a worker thread'),
puma_active_connections: ::Gitlab::Metrics.gauge(:puma_active_connections, 'Number of threads processing a request'),
puma_pool_capacity: ::Gitlab::Metrics.gauge(:puma_pool_capacity, 'Number of requests the worker is capable of taking right now'),
puma_max_threads: ::Gitlab::Metrics.gauge(:puma_max_threads, 'Maximum number of worker threads'),
puma_idle_threads: ::Gitlab::Metrics.gauge(:puma_idle_threads, 'Number of spawned threads which are not processing a request')
}
end
def sample
json_stats = puma_stats
return unless json_stats
stats = JSON.parse(json_stats)
if cluster?(stats)
sample_cluster(stats)
else
sample_single_worker(stats)
end
end
private
def puma_stats
Puma.stats
rescue NoMethodError
Rails.logger.info "PumaSampler: stats are not available yet, waiting for Puma to boot"
nil
end
def sample_cluster(stats)
set_master_metrics(stats)
stats['worker_status'].each do |worker|
last_status = worker['last_status']
labels = { worker: "worker_#{worker['index']}" }
metrics[:puma_phase].set(labels, worker['phase'])
set_worker_metrics(last_status, labels) if last_status.present?
end
end
def sample_single_worker(stats)
metrics[:puma_workers].set({}, 1)
metrics[:puma_running_workers].set({}, 1)
set_worker_metrics(stats)
end
def cluster?(stats)
stats['worker_status'].present?
end
def set_master_metrics(stats)
labels = { worker: "master" }
metrics[:puma_workers].set(labels, stats['workers'])
metrics[:puma_running_workers].set(labels, stats['booted_workers'])
metrics[:puma_stale_workers].set(labels, stats['old_workers'])
metrics[:puma_phase].set(labels, stats['phase'])
end
def set_worker_metrics(stats, labels = {})
metrics[:puma_running].set(labels, stats['running'])
metrics[:puma_queued_connections].set(labels, stats['backlog'])
metrics[:puma_active_connections].set(labels, stats['max_threads'] - stats['pool_capacity'])
metrics[:puma_pool_capacity].set(labels, stats['pool_capacity'])
metrics[:puma_max_threads].set(labels, stats['max_threads'])
metrics[:puma_idle_threads].set(labels, stats['running'] + stats['pool_capacity'] - stats['max_threads'])
end
end
end
end
end