Run rule evaluation with timestamps precisely evaluation_interval apart (#4201)
* Run rule evaluation with timestamps precisely evaluation_interval apart from one another. Signed-off-by: Alin Sinpalean <alin.sinpalean@gmail.com>
This commit is contained in:
parent
05cf674279
commit
9dc763cc03
|
@ -192,8 +192,9 @@ func (g *Group) run(ctx context.Context) {
|
||||||
defer close(g.terminated)
|
defer close(g.terminated)
|
||||||
|
|
||||||
// Wait an initial amount to have consistently slotted intervals.
|
// Wait an initial amount to have consistently slotted intervals.
|
||||||
|
evalTimestamp := g.evalTimestamp().Add(g.interval)
|
||||||
select {
|
select {
|
||||||
case <-time.After(g.offset()):
|
case <-time.After(time.Until(evalTimestamp)):
|
||||||
case <-g.done:
|
case <-g.done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -202,17 +203,20 @@ func (g *Group) run(ctx context.Context) {
|
||||||
iterationsScheduled.Inc()
|
iterationsScheduled.Inc()
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
g.Eval(ctx, start)
|
g.Eval(ctx, evalTimestamp)
|
||||||
|
timeSinceStart := time.Since(start)
|
||||||
|
|
||||||
iterationDuration.Observe(time.Since(start).Seconds())
|
iterationDuration.Observe(timeSinceStart.Seconds())
|
||||||
g.SetEvaluationTime(time.Since(start))
|
g.SetEvaluationTime(timeSinceStart)
|
||||||
}
|
}
|
||||||
lastTriggered := time.Now()
|
|
||||||
iter()
|
|
||||||
|
|
||||||
|
// The assumption here is that since the ticker was started after having
|
||||||
|
// waited for `evalTimestamp` to pass, the ticks will trigger soon
|
||||||
|
// after each `evalTimestamp + N * g.interval` occurrence.
|
||||||
tick := time.NewTicker(g.interval)
|
tick := time.NewTicker(g.interval)
|
||||||
defer tick.Stop()
|
defer tick.Stop()
|
||||||
|
|
||||||
|
iter()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-g.done:
|
case <-g.done:
|
||||||
|
@ -222,12 +226,12 @@ func (g *Group) run(ctx context.Context) {
|
||||||
case <-g.done:
|
case <-g.done:
|
||||||
return
|
return
|
||||||
case <-tick.C:
|
case <-tick.C:
|
||||||
missed := (time.Since(lastTriggered).Nanoseconds() / g.interval.Nanoseconds()) - 1
|
missed := (time.Since(evalTimestamp) / g.interval) - 1
|
||||||
if missed > 0 {
|
if missed > 0 {
|
||||||
iterationsMissed.Add(float64(missed))
|
iterationsMissed.Add(float64(missed))
|
||||||
iterationsScheduled.Add(float64(missed))
|
iterationsScheduled.Add(float64(missed))
|
||||||
}
|
}
|
||||||
lastTriggered = time.Now()
|
evalTimestamp = evalTimestamp.Add((missed + 1) * g.interval)
|
||||||
iter()
|
iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,20 +265,16 @@ func (g *Group) SetEvaluationTime(dur time.Duration) {
|
||||||
g.evaluationTime = dur
|
g.evaluationTime = dur
|
||||||
}
|
}
|
||||||
|
|
||||||
// offset returns until the next consistently slotted evaluation interval.
|
// evalTimestamp returns the immediately preceding consistently slotted evaluation time.
|
||||||
func (g *Group) offset() time.Duration {
|
func (g *Group) evalTimestamp() time.Time {
|
||||||
now := time.Now().UnixNano()
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
base = now - (now % int64(g.interval))
|
offset = int64(g.hash() % uint64(g.interval))
|
||||||
offset = g.hash() % uint64(g.interval)
|
now = time.Now().UnixNano()
|
||||||
next = base + int64(offset)
|
adjNow = now - offset
|
||||||
|
base = adjNow - (adjNow % int64(g.interval))
|
||||||
)
|
)
|
||||||
|
|
||||||
if next < now {
|
return time.Unix(0, base+offset)
|
||||||
next += int64(g.interval)
|
|
||||||
}
|
|
||||||
return time.Duration(next - now)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyState copies the alerting rule and staleness related state from the given group.
|
// copyState copies the alerting rule and staleness related state from the given group.
|
||||||
|
|
Loading…
Reference in New Issue