github.com/grafana/pyroscope@v1.18.0/pkg/scheduler/schedulerdiscovery/ring.go (about)

     1  // SPDX-License-Identifier: AGPL-3.0-only
     2  
     3  package schedulerdiscovery
     4  
     5  import (
     6  	"net"
     7  	"strconv"
     8  
     9  	"github.com/go-kit/log"
    10  	"github.com/grafana/dskit/kv"
    11  	"github.com/grafana/dskit/ring"
    12  	"github.com/pkg/errors"
    13  	"github.com/prometheus/client_golang/prometheus"
    14  
    15  	"github.com/grafana/pyroscope/pkg/util"
    16  )
    17  
    18  const (
    19  	// ringKey is the key under which we store the query-schedulers ring in the KVStore.
    20  	ringKey = "query-scheduler"
    21  
    22  	// ringNumTokens is how many tokens each query-scheduler should have in the ring.
    23  	// Query-schedulers use a ring for service-discovery so just 1 token is enough.
    24  	ringNumTokens = 1
    25  
    26  	// ringAutoForgetUnhealthyPeriods is how many consecutive timeout periods an unhealthy instance
    27  	// in the ring will be automatically removed after.
    28  	ringAutoForgetUnhealthyPeriods = 4
    29  
    30  	// sharedOptionWithRingClient is a message appended to all config options that should be also
    31  	// set on the components running the query-scheduler ring client.
    32  	sharedOptionWithRingClient = " When query-scheduler ring-based service discovery is enabled, this option needs be set on query-schedulers, query-frontends and queriers."
    33  )
    34  
    35  // toBasicLifecyclerConfig returns a ring.BasicLifecyclerConfig based on the query-scheduler ring config.
    36  func toBasicLifecyclerConfig(cfg util.CommonRingConfig, logger log.Logger) (ring.BasicLifecyclerConfig, error) {
    37  	instanceAddr, err := ring.GetInstanceAddr(cfg.InstanceAddr, cfg.InstanceInterfaceNames, logger, cfg.EnableIPv6)
    38  	if err != nil {
    39  		return ring.BasicLifecyclerConfig{}, err
    40  	}
    41  
    42  	instancePort := ring.GetInstancePort(cfg.InstancePort, cfg.ListenPort)
    43  
    44  	return ring.BasicLifecyclerConfig{
    45  		ID:                              cfg.InstanceID,
    46  		Addr:                            net.JoinHostPort(instanceAddr, strconv.Itoa(instancePort)),
    47  		HeartbeatPeriod:                 cfg.HeartbeatPeriod,
    48  		HeartbeatTimeout:                cfg.HeartbeatTimeout,
    49  		TokensObservePeriod:             0,
    50  		NumTokens:                       ringNumTokens,
    51  		KeepInstanceInTheRingOnShutdown: false,
    52  	}, nil
    53  }
    54  
    55  // NewRingLifecycler creates a new query-scheduler ring lifecycler with all required lifecycler delegates.
    56  func NewRingLifecycler(cfg util.CommonRingConfig, logger log.Logger, reg prometheus.Registerer) (*ring.BasicLifecycler, error) {
    57  	reg = prometheus.WrapRegistererWithPrefix("pyroscope_", reg)
    58  	kvStore, err := kv.NewClient(cfg.KVStore, ring.GetCodec(), kv.RegistererWithKVName(reg, "query-scheduler-lifecycler"), logger)
    59  	if err != nil {
    60  		return nil, errors.Wrap(err, "failed to initialize query-schedulers' KV store")
    61  	}
    62  
    63  	lifecyclerCfg, err := toBasicLifecyclerConfig(cfg, logger)
    64  	if err != nil {
    65  		return nil, errors.Wrap(err, "failed to build query-schedulers' lifecycler config")
    66  	}
    67  
    68  	var delegate ring.BasicLifecyclerDelegate
    69  	delegate = ring.NewInstanceRegisterDelegate(ring.ACTIVE, ringNumTokens)
    70  	delegate = ring.NewLeaveOnStoppingDelegate(delegate, logger)
    71  	delegate = ring.NewAutoForgetDelegate(ringAutoForgetUnhealthyPeriods*cfg.HeartbeatTimeout, delegate, logger)
    72  
    73  	lifecycler, err := ring.NewBasicLifecycler(lifecyclerCfg, "query-scheduler", ringKey, kvStore, delegate, logger, reg)
    74  	if err != nil {
    75  		return nil, errors.Wrap(err, "failed to initialize query-schedulers' lifecycler")
    76  	}
    77  
    78  	return lifecycler, nil
    79  }
    80  
    81  // NewRingClient creates a client for the query-schedulers ring.
    82  func NewRingClient(cfg util.CommonRingConfig, component string, logger log.Logger, reg prometheus.Registerer) (*ring.Ring, error) {
    83  	client, err := ring.New(cfg.ToRingConfig(), component, ringKey, logger, prometheus.WrapRegistererWithPrefix("pyroscope_", reg))
    84  	if err != nil {
    85  		return nil, errors.Wrap(err, "failed to initialize query-schedulers' ring client")
    86  	}
    87  
    88  	return client, err
    89  }