FindNonOverlapQueryLength iterates through all the vector selectors in the statement and finds the time interval each selector will try to process. It merges intervals to be non overlapping and calculates the total duration as the query length. This takes into account offsets, @ modifiers, and range
(expr parser.Expr, start, end int64, lookbackDelta time.Duration)
| 15 | // the query length. This takes into account offsets, @ modifiers, and range selectors. |
| 16 | // If the statement does not select series, then duration 0 will be returned. |
| 17 | func FindNonOverlapQueryLength(expr parser.Expr, start, end int64, lookbackDelta time.Duration) time.Duration { |
| 18 | type minMaxTime struct { |
| 19 | minTime, maxTime int64 |
| 20 | } |
| 21 | intervals := make([]minMaxTime, 0) |
| 22 | |
| 23 | // Whenever a MatrixSelector is evaluated, evalRange is set to the corresponding range. |
| 24 | // The evaluation of the VectorSelector inside then evaluates the given range and unsets |
| 25 | // the variable. |
| 26 | var evalRange time.Duration |
| 27 | parser.Inspect(expr, func(node parser.Node, path []parser.Node) error { |
| 28 | switch n := node.(type) { |
| 29 | case *parser.VectorSelector: |
| 30 | start, end := getTimeRangesForSelector(start, end, durationMilliseconds(lookbackDelta), n, path, evalRange) |
| 31 | intervals = append(intervals, minMaxTime{start, end}) |
| 32 | evalRange = 0 |
| 33 | case *parser.MatrixSelector: |
| 34 | evalRange = n.Range |
| 35 | } |
| 36 | return nil |
| 37 | }) |
| 38 | |
| 39 | if len(intervals) == 0 { |
| 40 | return 0 |
| 41 | } |
| 42 | |
| 43 | sort.Slice(intervals, func(i, j int) bool { |
| 44 | return intervals[i].minTime < intervals[j].minTime |
| 45 | }) |
| 46 | |
| 47 | prev := intervals[0] |
| 48 | length := time.Duration(0) |
| 49 | for i := 1; i < len(intervals); i++ { |
| 50 | if intervals[i].minTime <= prev.maxTime { |
| 51 | prev.maxTime = max(prev.maxTime, intervals[i].maxTime) |
| 52 | } else { |
| 53 | length += util.TimeFromMillis(prev.maxTime).Sub(util.TimeFromMillis(prev.minTime)) |
| 54 | prev = intervals[i] |
| 55 | } |
| 56 | } |
| 57 | length += util.TimeFromMillis(prev.maxTime).Sub(util.TimeFromMillis(prev.minTime)) |
| 58 | return length |
| 59 | } |
| 60 | |
| 61 | // Copied from https://github.com/prometheus/prometheus/blob/v2.52.0/promql/engine.go#L863. |
| 62 | func getTimeRangesForSelector(start, end, lookbackDelta int64, n *parser.VectorSelector, path []parser.Node, evalRange time.Duration) (int64, int64) { |