master
go 129 lines 3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package jobruntime
4
5 import (
6 "sync"
7 "sync/atomic"
8 "time"
9
10 "github.com/netdata/netdata/go/plugins/logger"
11 "github.com/netdata/netdata/go/plugins/plugin/framework/tickstate"
12 )
13
14 const (
15 penaltyStep = 5
16 maxPenalty = 600
17 infTries = -1
18 )
19
20 // stopController coordinates stop request and completion acknowledgement.
21 // It is safe for repeated Stop calls and pre-start stop requests.
22 type stopController struct {
23 stopCh chan struct{}
24 stoppedCh chan struct{}
25
26 started atomic.Bool
27
28 stopOnce sync.Once
29 stoppedOnce sync.Once
30 }
31
32 func newStopController() stopController {
33 return stopController{
34 stopCh: make(chan struct{}),
35 stoppedCh: make(chan struct{}),
36 }
37 }
38
39 func (c *stopController) markStarted() {
40 c.started.Store(true)
41 }
42
43 func (c *stopController) markStopped() {
44 c.stoppedOnce.Do(func() { close(c.stoppedCh) })
45 }
46
47 func (c *stopController) requestStop() {
48 c.stopOnce.Do(func() { close(c.stopCh) })
49 }
50
51 func (c *stopController) stopAndWait() {
52 c.requestStop()
53 if !c.started.Load() {
54 return
55 }
56 <-c.stoppedCh
57 }
58
59 func retryAutoDetection(autoDetectEvery, autoDetectTries int) bool {
60 return autoDetectEvery > 0 && (autoDetectTries == infTries || autoDetectTries > 0)
61 }
62
63 func disableAutoDetection(autoDetectEvery *int) {
64 *autoDetectEvery = 0
65 }
66
67 func consumeAutoDetectTry(autoDetectTries *int) {
68 if *autoDetectTries != infTries {
69 *autoDetectTries--
70 }
71 }
72
73 func shouldCollectWithPenalty(clock, updateEvery, retries int) bool {
74 return clock%(updateEvery+penaltyFromRetries(retries, updateEvery)) == 0
75 }
76
77 func penaltyFromRetries(retries, updateEvery int) int {
78 v := retries / penaltyStep * penaltyStep * updateEvery / 2
79 if v > maxPenalty {
80 return maxPenalty
81 }
82 return v
83 }
84
85 func enqueueTickWithSkipLog(
86 tick chan int,
87 clock int,
88 functionOnly bool,
89 updateEvery int,
90 retries int,
91 skipTracker *tickstate.SkipTracker,
92 log *logger.Logger,
93 ) {
94 select {
95 case tick <- clock:
96 default:
97 if functionOnly || !shouldCollectWithPenalty(clock, updateEvery, retries) {
98 return
99 }
100
101 skip := skipTracker.MarkSkipped()
102 if skip.RunStarted.IsZero() {
103 log.Infof("skipping data collection: waiting for first collection to start (interval %ds)", updateEvery)
104 return
105 }
106 if skip.Count >= 2 {
107 log.Warningf(
108 "skipping data collection: previous run is still in progress for %s (skipped %d times in a row, interval %ds)",
109 time.Since(skip.RunStarted),
110 skip.Count,
111 updateEvery,
112 )
113 return
114 }
115 log.Infof("skipping data collection: previous run is still in progress for %s (interval %ds)", time.Since(skip.RunStarted), updateEvery)
116 }
117 }
118
119 func markRunStartWithResumeLog(skipTracker *tickstate.SkipTracker, log *logger.Logger) {
120 resume := skipTracker.MarkRunStart(time.Now())
121 if resume.Skipped == 0 {
122 return
123 }
124 if resume.RunStopped.IsZero() || resume.RunStarted.IsZero() {
125 log.Infof("data collection resumed (skipped %d times)", resume.Skipped)
126 return
127 }
128 log.Infof("data collection resumed after %s (skipped %d times)", resume.RunStopped.Sub(resume.RunStarted), resume.Skipped)
129 }