| 618 | } |
| 619 | |
| 620 | func (api *API) queryRange(r *http.Request) (result apiFuncResult) { |
| 621 | limit, err := parseLimitParam(r.FormValue("limit")) |
| 622 | if err != nil { |
| 623 | return invalidParamError(err, "limit") |
| 624 | } |
| 625 | start, err := parseTime(r.FormValue("start")) |
| 626 | if err != nil { |
| 627 | return invalidParamError(err, "start") |
| 628 | } |
| 629 | end, err := parseTime(r.FormValue("end")) |
| 630 | if err != nil { |
| 631 | return invalidParamError(err, "end") |
| 632 | } |
| 633 | if end.Before(start) { |
| 634 | return invalidParamError(errors.New("end timestamp must not be before start time"), "end") |
| 635 | } |
| 636 | |
| 637 | step, err := parseDuration(r.FormValue("step")) |
| 638 | if err != nil { |
| 639 | return invalidParamError(err, "step") |
| 640 | } |
| 641 | |
| 642 | if step <= 0 { |
| 643 | return invalidParamError(errors.New("zero or negative query resolution step widths are not accepted. Try a positive integer"), "step") |
| 644 | } |
| 645 | |
| 646 | // For safety, limit the number of returned points per timeseries. |
| 647 | // This is sufficient for 60s resolution for a week or 1h resolution for a year. |
| 648 | if end.Sub(start)/step > 11000 { |
| 649 | err := errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)") |
| 650 | return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
| 651 | } |
| 652 | |
| 653 | ctx := r.Context() |
| 654 | if to := r.FormValue("timeout"); to != "" { |
| 655 | var cancel context.CancelFunc |
| 656 | timeout, err := parseDuration(to) |
| 657 | if err != nil { |
| 658 | return invalidParamError(err, "timeout") |
| 659 | } |
| 660 | |
| 661 | ctx, cancel = context.WithTimeout(ctx, timeout) |
| 662 | defer cancel() |
| 663 | } |
| 664 | |
| 665 | opts, err := extractQueryOpts(r) |
| 666 | if err != nil { |
| 667 | return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} |
| 668 | } |
| 669 | qry, err := api.QueryEngine.NewRangeQuery(ctx, api.Queryable, opts, r.FormValue("query"), start, end, step) |
| 670 | if err != nil { |
| 671 | return invalidParamError(err, "query") |
| 672 | } |
| 673 | // From now on, we must only return with a finalizer in the result (to |
| 674 | // be called by the caller) or call qry.Close ourselves (which is |
| 675 | // required in the case of a panic). |
| 676 | defer func() { |
| 677 | if result.finalizer == nil { |