master
go 179 lines 3.28 KB
Raw
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/netdata/go/plugins/logger"
11 "github.com/netdata/netdata/go/plugins/plugin/agent/discovery/sd/model"
12 )
13
14 func newAccumulator() *accumulator {
15 return &accumulator{
16 send: make(chan struct{}, 1),
17 sendEvery: time.Second * 2,
18 mux: &sync.Mutex{},
19 tggs: make(map[string]model.TargetGroup),
20 }
21 }
22
23 type accumulator struct {
24 *logger.Logger
25 discoverers []model.Discoverer
26 send chan struct{}
27 sendEvery time.Duration
28 mux *sync.Mutex
29 tggs map[string]model.TargetGroup
30 }
31
32 func (a *accumulator) run(ctx context.Context, in chan []model.TargetGroup) {
33 updates := make(chan []model.TargetGroup)
34
35 var wg sync.WaitGroup
36 for _, d := range a.discoverers {
37 wg.Add(1)
38 d := d
39 go func() { defer wg.Done(); a.runDiscoverer(ctx, d, updates) }()
40 }
41
42 done := make(chan struct{})
43 go func() { defer close(done); wg.Wait() }()
44
45 tk := time.NewTicker(a.sendEvery)
46 defer tk.Stop()
47
48 for {
49 select {
50 case <-ctx.Done():
51 select {
52 case <-done:
53 a.Info("all discoverers exited")
54 case <-time.After(time.Second * 10):
55 a.Warning("not all discoverers exited")
56 }
57 a.finalSend(ctx, in)
58 return
59 case <-done:
60 if !isDone(ctx) {
61 a.Info("all discoverers exited before ctx done")
62 } else {
63 a.Info("all discoverers exited")
64 }
65 a.finalSend(ctx, in)
66 return
67 case <-tk.C:
68 select {
69 case <-a.send:
70 a.trySend(in)
71 default:
72 }
73 }
74 }
75 }
76
77 func (a *accumulator) runDiscoverer(ctx context.Context, d model.Discoverer, updates chan []model.TargetGroup) {
78 done := make(chan struct{})
79 go func() { defer close(done); d.Discover(ctx, updates) }()
80
81 for {
82 select {
83 case <-ctx.Done():
84 select {
85 case <-done:
86 case <-time.After(time.Second * 10):
87 a.Warningf("discoverer '%v' didn't exit on ctx done", d)
88 }
89 return
90 case <-done:
91 if !isDone(ctx) {
92 a.Infof("discoverer '%v' exited before ctx done", d)
93 }
94 return
95 case tggs := <-updates:
96 a.mux.Lock()
97 a.groupsUpdate(tggs)
98 a.mux.Unlock()
99 a.triggerSend()
100 }
101 }
102 }
103
104 func (a *accumulator) trySend(in chan<- []model.TargetGroup) {
105 a.mux.Lock()
106 defer a.mux.Unlock()
107
108 select {
109 case in <- a.groupsList():
110 a.groupsReset()
111 default:
112 a.triggerSend()
113 }
114 }
115
116 func (a *accumulator) finalSend(ctx context.Context, in chan<- []model.TargetGroup) {
117 a.mux.Lock()
118 tggs := a.groupsList()
119 if len(tggs) == 0 {
120 a.mux.Unlock()
121 return
122 }
123 a.mux.Unlock()
124
125 select {
126 case in <- tggs:
127 a.mux.Lock()
128 a.groupsReset()
129 a.mux.Unlock()
130 return
131 default:
132 }
133
134 select {
135 case <-ctx.Done():
136 case in <- tggs:
137 a.mux.Lock()
138 a.groupsReset()
139 a.mux.Unlock()
140 }
141 }
142
143 func (a *accumulator) triggerSend() {
144 select {
145 case a.send <- struct{}{}:
146 default:
147 }
148 }
149
150 func (a *accumulator) groupsUpdate(tggs []model.TargetGroup) {
151 for _, tgg := range tggs {
152 a.tggs[tgg.Source()] = tgg
153 }
154 }
155
156 func (a *accumulator) groupsReset() {
157 for key := range a.tggs {
158 delete(a.tggs, key)
159 }
160 }
161
162 func (a *accumulator) groupsList() []model.TargetGroup {
163 tggs := make([]model.TargetGroup, 0, len(a.tggs))
164 for _, tgg := range a.tggs {
165 if tgg != nil {
166 tggs = append(tggs, tgg)
167 }
168 }
169 return tggs
170 }
171
172 func isDone(ctx context.Context) bool {
173 select {
174 case <-ctx.Done():
175 return true
176 default:
177 return false
178 }
179 }