github.com/netdata/go.d.plugin@v0.58.1/agent/discovery/sd/pipeline/accumulator.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package pipeline 4 5 import ( 6 "context" 7 "sync" 8 "time" 9 10 "github.com/netdata/go.d.plugin/agent/discovery/sd/model" 11 "github.com/netdata/go.d.plugin/logger" 12 ) 13 14 func newAccumulator() *accumulator { 15 return &accumulator{ 16 send: make(chan struct{}, 1), 17 sendEvery: time.Second * 3, 18 mux: &sync.Mutex{}, 19 tggs: make(map[string]model.TargetGroup), 20 } 21 } 22 23 type ( 24 accumulator struct { 25 *logger.Logger 26 discoverers []model.Discoverer 27 send chan struct{} 28 sendEvery time.Duration 29 mux *sync.Mutex 30 tggs map[string]model.TargetGroup 31 } 32 ) 33 34 func (a *accumulator) run(ctx context.Context, in chan []model.TargetGroup) { 35 updates := make(chan []model.TargetGroup) 36 37 var wg sync.WaitGroup 38 for _, d := range a.discoverers { 39 wg.Add(1) 40 d := d 41 go func() { defer wg.Done(); a.runDiscoverer(ctx, d, updates) }() 42 } 43 44 done := make(chan struct{}) 45 go func() { defer close(done); wg.Wait() }() 46 47 tk := time.NewTicker(a.sendEvery) 48 defer tk.Stop() 49 50 for { 51 select { 52 case <-ctx.Done(): 53 select { 54 case <-done: 55 a.Info("all discoverers exited") 56 case <-time.After(time.Second * 5): 57 a.Warning("not all discoverers exited") 58 } 59 return 60 case <-done: 61 a.Info("all discoverers exited") 62 a.trySend(in) 63 return 64 case <-tk.C: 65 select { 66 case <-a.send: 67 a.trySend(in) 68 default: 69 } 70 } 71 } 72 } 73 74 func (a *accumulator) runDiscoverer(ctx context.Context, d model.Discoverer, updates chan []model.TargetGroup) { 75 done := make(chan struct{}) 76 go func() { defer close(done); d.Discover(ctx, updates) }() 77 78 for { 79 select { 80 case <-ctx.Done(): 81 select { 82 case <-done: 83 case <-time.After(time.Second * 5): 84 } 85 return 86 case <-done: 87 return 88 case tggs := <-updates: 89 a.mux.Lock() 90 a.groupsUpdate(tggs) 91 a.mux.Unlock() 92 a.triggerSend() 93 } 94 } 95 } 96 97 func (a *accumulator) trySend(in chan<- []model.TargetGroup) { 98 a.mux.Lock() 99 defer a.mux.Unlock() 100 101 select { 102 case in <- a.groupsList(): 103 a.groupsReset() 104 default: 105 a.triggerSend() 106 } 107 } 108 109 func (a *accumulator) triggerSend() { 110 select { 111 case a.send <- struct{}{}: 112 default: 113 } 114 } 115 116 func (a *accumulator) groupsUpdate(tggs []model.TargetGroup) { 117 for _, tgg := range tggs { 118 a.tggs[tgg.Source()] = tgg 119 } 120 } 121 122 func (a *accumulator) groupsReset() { 123 for key := range a.tggs { 124 delete(a.tggs, key) 125 } 126 } 127 128 func (a *accumulator) groupsList() []model.TargetGroup { 129 tggs := make([]model.TargetGroup, 0, len(a.tggs)) 130 for _, tgg := range a.tggs { 131 if tgg != nil { 132 tggs = append(tggs, tgg) 133 } 134 } 135 return tggs 136 }