github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/util/ring_watcher.go (about)

     1  package util
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/go-kit/log"
     9  	"github.com/go-kit/log/level"
    10  	"github.com/grafana/dskit/ring"
    11  	"github.com/grafana/dskit/services"
    12  )
    13  
    14  const (
    15  	RingKeyOfLeader = 0
    16  )
    17  
    18  type ringWatcher struct {
    19  	log           log.Logger
    20  	ring          ring.ReadRing
    21  	notifications DNSNotifications
    22  	lookupPeriod  time.Duration
    23  	addresses     []string
    24  }
    25  
    26  // NewRingWatcher creates a new Ring watcher and returns a service that is wrapping it.
    27  func NewRingWatcher(log log.Logger, ring ring.ReadRing, lookupPeriod time.Duration, notifications DNSNotifications) (services.Service, error) {
    28  	w := &ringWatcher{
    29  		log:           log,
    30  		ring:          ring,
    31  		notifications: notifications,
    32  		lookupPeriod:  lookupPeriod,
    33  	}
    34  	return services.NewBasicService(nil, w.watchLoop, nil), nil
    35  }
    36  
    37  // watchLoop watches for changes in DNS and sends notifications.
    38  func (w *ringWatcher) watchLoop(servCtx context.Context) error {
    39  
    40  	syncTicker := time.NewTicker(w.lookupPeriod)
    41  	defer syncTicker.Stop()
    42  
    43  	for {
    44  		select {
    45  		case <-servCtx.Done():
    46  			return nil
    47  		case <-syncTicker.C:
    48  			w.lookupAddresses()
    49  		}
    50  	}
    51  }
    52  
    53  func (w *ringWatcher) lookupAddresses() {
    54  	addrs, err := w.getAddresses()
    55  	if err != nil {
    56  		level.Error(w.log).Log("msg", "error getting addresses from ring", "err", err)
    57  	}
    58  
    59  	if len(addrs) == 0 {
    60  		return
    61  	}
    62  	toAdd := make([]string, 0, len(addrs))
    63  	for i, newAddr := range addrs {
    64  		alreadyExists := false
    65  		for _, currAddr := range w.addresses {
    66  			if currAddr == newAddr {
    67  				alreadyExists = true
    68  			}
    69  		}
    70  		if !alreadyExists {
    71  			toAdd = append(toAdd, addrs[i])
    72  		}
    73  	}
    74  	toRemove := make([]string, 0, len(w.addresses))
    75  	for i, existingAddr := range w.addresses {
    76  		stillExists := false
    77  		for _, newAddr := range addrs {
    78  			if newAddr == existingAddr {
    79  				stillExists = true
    80  			}
    81  		}
    82  		if !stillExists {
    83  			toRemove = append(toRemove, w.addresses[i])
    84  		}
    85  	}
    86  
    87  	for _, ta := range toAdd {
    88  		level.Debug(w.log).Log("msg", fmt.Sprintf("adding connection to address: %s", ta))
    89  		w.notifications.AddressAdded(ta)
    90  	}
    91  
    92  	for _, tr := range toRemove {
    93  		level.Debug(w.log).Log("msg", fmt.Sprintf("removing connection to address: %s", tr))
    94  		w.notifications.AddressRemoved(tr)
    95  	}
    96  
    97  	w.addresses = addrs
    98  
    99  }
   100  
   101  func (w *ringWatcher) getAddresses() ([]string, error) {
   102  
   103  	// We use ring.Write combined with the `ring.NewIgnoreUnhealthyInstancesReplicationStrategy`
   104  	// during ring creation to fetch at least 1 and ideally $REPLICATION_FACTOR nodes from the ring.
   105  	// If the ideal nodes for the desired token position are unhealthy, ring.Write ensures
   106  	// we continue to traverse the ring looking for more, until we've acquired $REPLICATION_FACTOR
   107  	// nodes or the ring is exhausted.
   108  	op := ring.Write
   109  
   110  	bufDescs, bufHosts, bufZones := ring.MakeBuffersForGet()
   111  	rs, err := w.ring.Get(RingKeyOfLeader, op, bufDescs, bufHosts, bufZones)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	return rs.GetAddresses(), nil
   117  }