// SPDX-License-Identifier: AGPL-3.0-only
// Provenance-includes-location: https://github.com/cortexproject/cortex/blob/master/pkg/distributor/ingester_client_pool.go
// Provenance-includes-license: Apache-2.0
// Provenance-includes-copyright: The Cortex Authors.

package distributor

import (
	"flag"
	"time"

	"github.com/go-kit/log"
	"github.com/grafana/dskit/ring"
	ring_client "github.com/grafana/dskit/ring/client"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

//lint:ignore faillint It's non-trivial to remove this global variable.
var clients = promauto.NewGauge(prometheus.GaugeOpts{
	Name: "cortex_distributor_ingester_clients",
	Help: "The current number of ingester clients.",
})

// PoolConfig is config for creating a Pool.
type PoolConfig struct {
	ClientCleanupPeriod    time.Duration `yaml:"client_cleanup_period" category:"advanced"`
	HealthCheckIngesters   bool          `yaml:"health_check_ingesters" category:"advanced"`
	RemoteTimeout          time.Duration `yaml:"-"`
	HealthCheckGracePeriod time.Duration `yaml:"ingester_health_check_grace_period" category:"experimental"`
}

// RegisterFlags adds the flags required to config this to the given FlagSet.
func (cfg *PoolConfig) RegisterFlags(f *flag.FlagSet) {
	f.DurationVar(&cfg.ClientCleanupPeriod, "distributor.client-cleanup-period", 15*time.Second, "How frequently to clean up clients for ingesters that have gone away.")
	f.BoolVar(&cfg.HealthCheckIngesters, "distributor.health-check-ingesters", true, "Run a health check on each ingester client during periodic cleanup.")
	f.DurationVar(&cfg.HealthCheckGracePeriod, "distributor.ingester-health-check-grace-period", 0, "The grace period for ingester health checks. If an ingester connection consistently fails health checks for this period, any open connections are closed. The distributor or querier will attempt to reconnect to the ingester if a subsequent request is made to the ingester. Set to 0 to immediately remove ingester connections on the first health check failure.")
}

func NewPool(cfg PoolConfig, ring ring.ReadRing, factory ring_client.PoolFactory, logger log.Logger) *ring_client.Pool {
	poolCfg := ring_client.PoolConfig{
		CheckInterval:          cfg.ClientCleanupPeriod,
		HealthCheckEnabled:     cfg.HealthCheckIngesters,
		HealthCheckTimeout:     cfg.RemoteTimeout,
		HealthCheckGracePeriod: cfg.HealthCheckGracePeriod,
	}

	return ring_client.NewPool("ingester", poolCfg, ring_client.NewRingServiceDiscovery(ring), factory, clients, logger)
}
