From 03ab8dcca0040674de5c1a6a9caabdf5ab4c1d4b Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Wed, 12 Oct 2022 14:12:03 +0200 Subject: [PATCH] Add comments on EvalTimestamp Signed-off-by: Dimitar Dimitrov --- rules/manager.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/rules/manager.go b/rules/manager.go index 140fb45b7..e24eee2c0 100644 --- a/rules/manager.go +++ b/rules/manager.go @@ -532,9 +532,28 @@ func (g *Group) setLastEvaluation(ts time.Time) { // EvalTimestamp returns the immediately preceding consistently slotted evaluation time. func (g *Group) EvalTimestamp(startTime int64) time.Time { - offset := (uint64(startTime) - g.hash()) % uint64(g.interval) + var ( + offset = int64(g.hash() % uint64(g.interval)) - return time.Unix(0, startTime-int64(offset)).UTC() + // This group's evaluation times differ from the perfect time intervals by `offset` nanoseconds. + // But we can only use `% interval` to align with the interval. And `% interval` will always + // align with the perfect time intervals, instead of this group's. Because of this we add + // `offset` _after_ aligning with the perfect time interval. + // + // There can be cases where adding `offset` to the perfect evaluation time can yield a + // timestamp in the future, which is not what EvalTimestamp should do. + // So we subtract one `offset` to make sure that `now - (now % interval) + offset` gives an + // evaluation time in the past. + adjNow = startTime - offset + + // Adjust to perfect evaluation intervals. + base = adjNow - (adjNow % int64(g.interval)) + + // Add one offset to randomize the evaluation times of this group. + next = base + offset + ) + + return time.Unix(0, next).UTC() } func nameAndLabels(rule Rule) string {