# frozen_string_literal: true

module Gitlab
  module Metrics
    module Samplers
      class DatabaseSampler < BaseSampler
        DEFAULT_SAMPLING_INTERVAL_SECONDS = 5

        METRIC_PREFIX = 'gitlab_database_connection_pool_'

        METRIC_DESCRIPTIONS = {
          size: 'Total connection pool capacity',
          connections: 'Current connections in the pool',
          busy: 'Connections in use where the owner is still alive',
          dead: 'Connections in use where the owner is not alive',
          idle: 'Connections not in use',
          waiting: 'Threads currently waiting on this queue'
        }.freeze

        def metrics
          @metrics ||= init_metrics
        end

        def sample
          host_stats.each do |host_stat|
            METRIC_DESCRIPTIONS.each_key do |metric|
              metrics[metric].set(host_stat[:labels], host_stat[:stats][metric])
            end
          end
        end

        private

        def init_metrics
          METRIC_DESCRIPTIONS.to_h do |name, description|
            [name, ::Gitlab::Metrics.gauge(:"#{METRIC_PREFIX}#{name}", description)]
          end
        end

        def host_stats
          connection_class_stats + replica_host_stats
        end

        def connection_class_stats
          Gitlab::Database.database_base_models.each_value.with_object([]) do |base_model, stats|
            next unless base_model.connected?

            stats << { labels: labels_for_class(base_model), stats: base_model.connection_pool.stat }
          end
        end

        def replica_host_stats
          Gitlab::Database::LoadBalancing.each_load_balancer.with_object([]) do |load_balancer, stats|
            next if load_balancer.primary_only?

            load_balancer.host_list.hosts.each do |host|
              stats << { labels: labels_for_replica_host(load_balancer, host), stats: host.connection.pool.stat }
            end
          end
        end

        def labels_for_class(klass)
          {
            host: klass.connection_db_config.host,
            port: klass.connection_db_config.configuration_hash[:port],
            class: klass.to_s,
            db_config_name: klass.connection_db_config.name
          }
        end

        def labels_for_replica_host(load_balancer, host)
          {
            host: host.host,
            port: host.port,
            class: load_balancer.configuration.connection_specification_name,
            db_config_name: Gitlab::Database.db_config_name(host.connection)
          }
        end
      end
    end
  end
end

Gitlab::Metrics::Samplers::DatabaseSampler.prepend_mod_with('Gitlab::Metrics::Samplers::DatabaseSampler')