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 }