Hardcode rate() for sparse histograms
@@ -497,6 +497,27 @@ func (api *API) queryRange(r *http.Request) (result apiFuncResult) {
return apiFuncResult{nil, &apiError{errorBadData, errors.New("need exactly 1 selector")}, nil, nil}
}
+ hasRate, rateDuration := false, time.Duration(0)
+ parser.Inspect(expr, func(node parser.Node, path []parser.Node) error {
+ switch n := node.(type) {
+ case *parser.Call:
+ if n.Func.Name == "rate" {
+ hasRate = true
+ rateDuration = n.Args[0].(*parser.MatrixSelector).Range
+ return errors.New("stop it here")
+ }
+ }
+ return nil
+ })
+ var numRateSamples int
+ if hasRate {
+ numRateSamples = int(end.Sub(start)/step + 1)
+ if start.Add(time.Duration(numRateSamples-1) * step).After(end) {
+ numRateSamples--
+ }
+ start = start.Add(-rateDuration) // Adjusting for the first point lookback.
+ }
+
q, err := api.Queryable.Querier(ctx, timestamp.FromTime(start), timestamp.FromTime(end))
if err != nil {
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil}
@@ -544,6 +565,55 @@ func (api *API) queryRange(r *http.Request) (result apiFuncResult) {
}
}
+ if hasRate {
+ newRes := make(promql.Matrix, len(res))
+ for i := range newRes {
+ newRes[i].Metric = res[i].Metric
+ points := make([]promql.Point, numRateSamples)
+
+ rawPoints := res[i].Points
+
+ startIdx, endIdx := 0, 0
+ for idx := range points {
+ pointTime := start.Add(time.Duration(idx) * step)
+ lookbackTime := pointTime.Add(-rateDuration)
+ points[idx].T = timestamp.FromTime(pointTime)
+ if len(rawPoints) == 0 {
+ continue
+ }
+
+ for startIdx < len(rawPoints) && timestamp.Time(rawPoints[startIdx].T).Before(lookbackTime) {
+ startIdx++
+ }
+ if startIdx >= len(rawPoints) {
+ startIdx = len(rawPoints) - 1
+ }
+
+ for endIdx < len(rawPoints) && timestamp.Time(rawPoints[endIdx].T).Before(pointTime) {
+ endIdx++
+ }
+ if endIdx >= len(rawPoints) {
+ endIdx = len(rawPoints) - 1
+ } else if timestamp.Time(rawPoints[endIdx].T).After(pointTime) && (len(rawPoints) == 1 || endIdx == 0) {
+ continue
+ } else {
+ endIdx--
+ }
+
+ valDiff := rawPoints[endIdx].V - rawPoints[startIdx].V
+ timeDiffSeconds := float64(timestamp.Time(rawPoints[endIdx].T).Sub(timestamp.Time(rawPoints[startIdx].T))) / float64(time.Second)
+
+ if timeDiffSeconds != 0 {
+ points[idx].V = valDiff / timeDiffSeconds
+ }
+ }
+
+ newRes[i].Points = points
+ }
+
+ res = newRes
+ }
+
sort.Sort(res)
return apiFuncResult{&queryData{
