mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 12:31:07 +01:00
app/vmselect: add optional limit
query arg to /api/v1/labels
and /api/v1/label_values
endpoints
This arg allows limiting the number of sample values returned from these APIs
This commit is contained in:
parent
483b402bb2
commit
89b778902b
@ -607,6 +607,8 @@ For example, the following query would return data for the last 30 minutes: `/ap
|
|||||||
|
|
||||||
VictoriaMetrics accepts `round_digits` query arg for `/api/v1/query` and `/api/v1/query_range` handlers. It can be used for rounding response values to the given number of digits after the decimal point. For example, `/api/v1/query?query=avg_over_time(temperature[1h])&round_digits=2` would round response values to up to two digits after the decimal point.
|
VictoriaMetrics accepts `round_digits` query arg for `/api/v1/query` and `/api/v1/query_range` handlers. It can be used for rounding response values to the given number of digits after the decimal point. For example, `/api/v1/query?query=avg_over_time(temperature[1h])&round_digits=2` would round response values to up to two digits after the decimal point.
|
||||||
|
|
||||||
|
VictoriaMetrics accepts `limit` query arg for `/api/v1/labels` and `/api/v1/label/<labelName>/values` handlers for limiting the number of returned entries. For example, the query to `/api/v1/labels?limit=5` returns a sample of up to 5 unique labels, while ignoring the rest of labels. If the provided `limit` value exceeds the corresponding `-search.maxTagKeys` / `-search.maxTagValues` command-line flag values, then limits specified in the command-line flags are used.
|
||||||
|
|
||||||
By default, VictoriaMetrics returns time series for the last 5 minutes from `/api/v1/series`, while the Prometheus API defaults to all time. Use `start` and `end` to select a different time range.
|
By default, VictoriaMetrics returns time series for the last 5 minutes from `/api/v1/series`, while the Prometheus API defaults to all time. Use `start` and `end` to select a different time range.
|
||||||
|
|
||||||
Additionally, VictoriaMetrics provides the following handlers:
|
Additionally, VictoriaMetrics provides the following handlers:
|
||||||
|
@ -197,7 +197,7 @@ func MetricsExpandHandler(startTime time.Time, w http.ResponseWriter, r *http.Re
|
|||||||
func MetricsIndexHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
func MetricsIndexHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
||||||
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
||||||
jsonp := r.FormValue("jsonp")
|
jsonp := r.FormValue("jsonp")
|
||||||
metricNames, err := netstorage.GetLabelValues(nil, "__name__", deadline)
|
metricNames, err := netstorage.GetLabelValues(nil, "__name__", 0, deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot obtain metric names: %w`, err)
|
return fmt.Errorf(`cannot obtain metric names: %w`, err)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -159,7 +158,7 @@ var (
|
|||||||
// See https://graphite.readthedocs.io/en/stable/tags.html#auto-complete-support
|
// See https://graphite.readthedocs.io/en/stable/tags.html#auto-complete-support
|
||||||
func TagsAutoCompleteValuesHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
func TagsAutoCompleteValuesHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
||||||
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
||||||
limit, err := getInt(r, "limit")
|
limit, err := searchutils.GetInt(r, "limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -245,7 +244,7 @@ var tagsAutoCompleteValuesDuration = metrics.NewSummary(`vm_request_duration_sec
|
|||||||
// See https://graphite.readthedocs.io/en/stable/tags.html#auto-complete-support
|
// See https://graphite.readthedocs.io/en/stable/tags.html#auto-complete-support
|
||||||
func TagsAutoCompleteTagsHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
func TagsAutoCompleteTagsHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
||||||
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
||||||
limit, err := getInt(r, "limit")
|
limit, err := searchutils.GetInt(r, "limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -324,7 +323,7 @@ var tagsAutoCompleteTagsDuration = metrics.NewSummary(`vm_request_duration_secon
|
|||||||
// See https://graphite.readthedocs.io/en/stable/tags.html#exploring-tags
|
// See https://graphite.readthedocs.io/en/stable/tags.html#exploring-tags
|
||||||
func TagsFindSeriesHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
func TagsFindSeriesHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
||||||
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
||||||
limit, err := getInt(r, "limit")
|
limit, err := searchutils.GetInt(r, "limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -392,7 +391,7 @@ var tagsFindSeriesDuration = metrics.NewSummary(`vm_request_duration_seconds{pat
|
|||||||
// See https://graphite.readthedocs.io/en/stable/tags.html#exploring-tags
|
// See https://graphite.readthedocs.io/en/stable/tags.html#exploring-tags
|
||||||
func TagValuesHandler(startTime time.Time, tagName string, w http.ResponseWriter, r *http.Request) error {
|
func TagValuesHandler(startTime time.Time, tagName string, w http.ResponseWriter, r *http.Request) error {
|
||||||
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
||||||
limit, err := getInt(r, "limit")
|
limit, err := searchutils.GetInt(r, "limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -420,7 +419,7 @@ var tagValuesDuration = metrics.NewSummary(`vm_request_duration_seconds{path="/t
|
|||||||
// See https://graphite.readthedocs.io/en/stable/tags.html#exploring-tags
|
// See https://graphite.readthedocs.io/en/stable/tags.html#exploring-tags
|
||||||
func TagsHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
func TagsHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) error {
|
||||||
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
deadline := searchutils.GetDeadlineForQuery(r, startTime)
|
||||||
limit, err := getInt(r, "limit")
|
limit, err := searchutils.GetInt(r, "limit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -443,18 +442,6 @@ func TagsHandler(startTime time.Time, w http.ResponseWriter, r *http.Request) er
|
|||||||
|
|
||||||
var tagsDuration = metrics.NewSummary(`vm_request_duration_seconds{path="/tags"}`)
|
var tagsDuration = metrics.NewSummary(`vm_request_duration_seconds{path="/tags"}`)
|
||||||
|
|
||||||
func getInt(r *http.Request, argName string) (int, error) {
|
|
||||||
argValue := r.FormValue(argName)
|
|
||||||
if len(argValue) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
n, err := strconv.Atoi(argValue)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("cannot parse %q=%q: %w", argName, argValue, err)
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSearchQueryForExprs(startTime time.Time, etfs [][]storage.TagFilter, exprs []string, maxMetrics int) (*storage.SearchQuery, error) {
|
func getSearchQueryForExprs(startTime time.Time, etfs [][]storage.TagFilter, exprs []string, maxMetrics int) (*storage.SearchQuery, error) {
|
||||||
tfs, err := exprsToTagFilters(exprs)
|
tfs, err := exprsToTagFilters(exprs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -612,13 +612,16 @@ func DeleteSeries(qt *querytracer.Tracer, sq *storage.SearchQuery, deadline sear
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelsOnTimeRange returns labels for the given tr until the given deadline.
|
// GetLabelsOnTimeRange returns labels for the given tr until the given deadline.
|
||||||
func GetLabelsOnTimeRange(qt *querytracer.Tracer, tr storage.TimeRange, deadline searchutils.Deadline) ([]string, error) {
|
func GetLabelsOnTimeRange(qt *querytracer.Tracer, tr storage.TimeRange, limit int, deadline searchutils.Deadline) ([]string, error) {
|
||||||
qt = qt.NewChild("get labels on timeRange=%s", &tr)
|
qt = qt.NewChild("get labels on timeRange=%s", &tr)
|
||||||
defer qt.Done()
|
defer qt.Done()
|
||||||
if deadline.Exceeded() {
|
if deadline.Exceeded() {
|
||||||
return nil, fmt.Errorf("timeout exceeded before starting the query processing: %s", deadline.String())
|
return nil, fmt.Errorf("timeout exceeded before starting the query processing: %s", deadline.String())
|
||||||
}
|
}
|
||||||
labels, err := vmstorage.SearchTagKeysOnTimeRange(tr, *maxTagKeysPerSearch, deadline.Deadline())
|
if limit > *maxTagKeysPerSearch || limit <= 0 {
|
||||||
|
limit = *maxTagKeysPerSearch
|
||||||
|
}
|
||||||
|
labels, err := vmstorage.SearchTagKeysOnTimeRange(tr, limit, deadline.Deadline())
|
||||||
qt.Printf("get %d labels", len(labels))
|
qt.Printf("get %d labels", len(labels))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error during labels search on time range: %w", err)
|
return nil, fmt.Errorf("error during labels search on time range: %w", err)
|
||||||
@ -642,7 +645,7 @@ func GetGraphiteTags(qt *querytracer.Tracer, filter string, limit int, deadline
|
|||||||
if deadline.Exceeded() {
|
if deadline.Exceeded() {
|
||||||
return nil, fmt.Errorf("timeout exceeded before starting the query processing: %s", deadline.String())
|
return nil, fmt.Errorf("timeout exceeded before starting the query processing: %s", deadline.String())
|
||||||
}
|
}
|
||||||
labels, err := GetLabels(nil, deadline)
|
labels, err := GetLabels(nil, 0, deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -683,13 +686,16 @@ func hasString(a []string, s string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLabels returns labels until the given deadline.
|
// GetLabels returns labels until the given deadline.
|
||||||
func GetLabels(qt *querytracer.Tracer, deadline searchutils.Deadline) ([]string, error) {
|
func GetLabels(qt *querytracer.Tracer, limit int, deadline searchutils.Deadline) ([]string, error) {
|
||||||
qt = qt.NewChild("get labels")
|
qt = qt.NewChild("get labels")
|
||||||
defer qt.Done()
|
defer qt.Done()
|
||||||
if deadline.Exceeded() {
|
if deadline.Exceeded() {
|
||||||
return nil, fmt.Errorf("timeout exceeded before starting the query processing: %s", deadline.String())
|
return nil, fmt.Errorf("timeout exceeded before starting the query processing: %s", deadline.String())
|
||||||
}
|
}
|
||||||
labels, err := vmstorage.SearchTagKeys(*maxTagKeysPerSearch, deadline.Deadline())
|
if limit > *maxTagKeysPerSearch || limit <= 0 {
|
||||||
|
limit = *maxTagKeysPerSearch
|
||||||
|
}
|
||||||
|
labels, err := vmstorage.SearchTagKeys(limit, deadline.Deadline())
|
||||||
qt.Printf("get %d labels from global index", len(labels))
|
qt.Printf("get %d labels from global index", len(labels))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error during labels search: %w", err)
|
return nil, fmt.Errorf("error during labels search: %w", err)
|
||||||
@ -708,7 +714,7 @@ func GetLabels(qt *querytracer.Tracer, deadline searchutils.Deadline) ([]string,
|
|||||||
|
|
||||||
// GetLabelValuesOnTimeRange returns label values for the given labelName on the given tr
|
// GetLabelValuesOnTimeRange returns label values for the given labelName on the given tr
|
||||||
// until the given deadline.
|
// until the given deadline.
|
||||||
func GetLabelValuesOnTimeRange(qt *querytracer.Tracer, labelName string, tr storage.TimeRange, deadline searchutils.Deadline) ([]string, error) {
|
func GetLabelValuesOnTimeRange(qt *querytracer.Tracer, labelName string, tr storage.TimeRange, limit int, deadline searchutils.Deadline) ([]string, error) {
|
||||||
qt = qt.NewChild("get values for label %s on a timeRange %s", labelName, &tr)
|
qt = qt.NewChild("get values for label %s on a timeRange %s", labelName, &tr)
|
||||||
defer qt.Done()
|
defer qt.Done()
|
||||||
if deadline.Exceeded() {
|
if deadline.Exceeded() {
|
||||||
@ -718,7 +724,10 @@ func GetLabelValuesOnTimeRange(qt *querytracer.Tracer, labelName string, tr stor
|
|||||||
labelName = ""
|
labelName = ""
|
||||||
}
|
}
|
||||||
// Search for tag values
|
// Search for tag values
|
||||||
labelValues, err := vmstorage.SearchTagValuesOnTimeRange([]byte(labelName), tr, *maxTagValuesPerSearch, deadline.Deadline())
|
if limit > *maxTagValuesPerSearch || limit <= 0 {
|
||||||
|
limit = *maxTagValuesPerSearch
|
||||||
|
}
|
||||||
|
labelValues, err := vmstorage.SearchTagValuesOnTimeRange([]byte(labelName), tr, limit, deadline.Deadline())
|
||||||
qt.Printf("get %d label values", len(labelValues))
|
qt.Printf("get %d label values", len(labelValues))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error during label values search on time range for labelName=%q: %w", labelName, err)
|
return nil, fmt.Errorf("error during label values search on time range for labelName=%q: %w", labelName, err)
|
||||||
@ -739,7 +748,7 @@ func GetGraphiteTagValues(qt *querytracer.Tracer, tagName, filter string, limit
|
|||||||
if tagName == "name" {
|
if tagName == "name" {
|
||||||
tagName = ""
|
tagName = ""
|
||||||
}
|
}
|
||||||
tagValues, err := GetLabelValues(nil, tagName, deadline)
|
tagValues, err := GetLabelValues(nil, tagName, 0, deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -757,7 +766,7 @@ func GetGraphiteTagValues(qt *querytracer.Tracer, tagName, filter string, limit
|
|||||||
|
|
||||||
// GetLabelValues returns label values for the given labelName
|
// GetLabelValues returns label values for the given labelName
|
||||||
// until the given deadline.
|
// until the given deadline.
|
||||||
func GetLabelValues(qt *querytracer.Tracer, labelName string, deadline searchutils.Deadline) ([]string, error) {
|
func GetLabelValues(qt *querytracer.Tracer, labelName string, limit int, deadline searchutils.Deadline) ([]string, error) {
|
||||||
qt = qt.NewChild("get values for label %s", labelName)
|
qt = qt.NewChild("get values for label %s", labelName)
|
||||||
defer qt.Done()
|
defer qt.Done()
|
||||||
if deadline.Exceeded() {
|
if deadline.Exceeded() {
|
||||||
@ -767,7 +776,10 @@ func GetLabelValues(qt *querytracer.Tracer, labelName string, deadline searchuti
|
|||||||
labelName = ""
|
labelName = ""
|
||||||
}
|
}
|
||||||
// Search for tag values
|
// Search for tag values
|
||||||
labelValues, err := vmstorage.SearchTagValues([]byte(labelName), *maxTagValuesPerSearch, deadline.Deadline())
|
if limit > *maxTagValuesPerSearch || limit <= 0 {
|
||||||
|
limit = *maxTagValuesPerSearch
|
||||||
|
}
|
||||||
|
labelValues, err := vmstorage.SearchTagValues([]byte(labelName), limit, deadline.Deadline())
|
||||||
qt.Printf("get %d label values", len(labelValues))
|
qt.Printf("get %d label values", len(labelValues))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error during label values search for labelName=%q: %w", labelName, err)
|
return nil, fmt.Errorf("error during label values search for labelName=%q: %w", labelName, err)
|
||||||
|
@ -450,10 +450,14 @@ func LabelValuesHandler(qt *querytracer.Tracer, startTime time.Time, labelName s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
limit, err := searchutils.GetInt(r, "limit")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var labelValues []string
|
var labelValues []string
|
||||||
if len(cp.filterss) == 0 {
|
if len(cp.filterss) == 0 {
|
||||||
if cp.IsDefaultTimeRange() {
|
if cp.IsDefaultTimeRange() {
|
||||||
labelValues, err = netstorage.GetLabelValues(qt, labelName, cp.deadline)
|
labelValues, err = netstorage.GetLabelValues(qt, labelName, limit, cp.deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot obtain label values for %q: %w`, labelName, err)
|
return fmt.Errorf(`cannot obtain label values for %q: %w`, labelName, err)
|
||||||
}
|
}
|
||||||
@ -465,7 +469,7 @@ func LabelValuesHandler(qt *querytracer.Tracer, startTime time.Time, labelName s
|
|||||||
MinTimestamp: cp.start,
|
MinTimestamp: cp.start,
|
||||||
MaxTimestamp: cp.end,
|
MaxTimestamp: cp.end,
|
||||||
}
|
}
|
||||||
labelValues, err = netstorage.GetLabelValuesOnTimeRange(qt, labelName, tr, cp.deadline)
|
labelValues, err = netstorage.GetLabelValuesOnTimeRange(qt, labelName, tr, limit, cp.deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot obtain label values on time range for %q: %w`, labelName, err)
|
return fmt.Errorf(`cannot obtain label values on time range for %q: %w`, labelName, err)
|
||||||
}
|
}
|
||||||
@ -478,7 +482,7 @@ func LabelValuesHandler(qt *querytracer.Tracer, startTime time.Time, labelName s
|
|||||||
if cp.start == 0 {
|
if cp.start == 0 {
|
||||||
cp.start = cp.end - defaultStep
|
cp.start = cp.end - defaultStep
|
||||||
}
|
}
|
||||||
labelValues, err = labelValuesWithMatches(qt, labelName, cp)
|
labelValues, err = labelValuesWithMatches(qt, labelName, cp, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot obtain label values for %q on time range [%d...%d]: %w", labelName, cp.start, cp.end, err)
|
return fmt.Errorf("cannot obtain label values for %q on time range [%d...%d]: %w", labelName, cp.start, cp.end, err)
|
||||||
}
|
}
|
||||||
@ -494,7 +498,7 @@ func LabelValuesHandler(qt *querytracer.Tracer, startTime time.Time, labelName s
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func labelValuesWithMatches(qt *querytracer.Tracer, labelName string, cp *commonParams) ([]string, error) {
|
func labelValuesWithMatches(qt *querytracer.Tracer, labelName string, cp *commonParams, limit int) ([]string, error) {
|
||||||
// Add `labelName!=''` tag filter in order to filter out series without the labelName.
|
// Add `labelName!=''` tag filter in order to filter out series without the labelName.
|
||||||
// There is no need in adding `__name__!=''` filter, since all the time series should
|
// There is no need in adding `__name__!=''` filter, since all the time series should
|
||||||
// already have non-empty name.
|
// already have non-empty name.
|
||||||
@ -546,6 +550,9 @@ func labelValuesWithMatches(qt *querytracer.Tracer, labelName string, cp *common
|
|||||||
for labelValue := range m {
|
for labelValue := range m {
|
||||||
labelValues = append(labelValues, labelValue)
|
labelValues = append(labelValues, labelValue)
|
||||||
}
|
}
|
||||||
|
if limit > 0 && len(labelValues) > limit {
|
||||||
|
labelValues = labelValues[:limit]
|
||||||
|
}
|
||||||
sort.Strings(labelValues)
|
sort.Strings(labelValues)
|
||||||
qt.Printf("sort %d label values", len(labelValues))
|
qt.Printf("sort %d label values", len(labelValues))
|
||||||
return labelValues, nil
|
return labelValues, nil
|
||||||
@ -659,10 +666,14 @@ func LabelsHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseW
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
limit, err := searchutils.GetInt(r, "limit")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var labels []string
|
var labels []string
|
||||||
if len(cp.filterss) == 0 {
|
if len(cp.filterss) == 0 {
|
||||||
if cp.IsDefaultTimeRange() {
|
if cp.IsDefaultTimeRange() {
|
||||||
labels, err = netstorage.GetLabels(qt, cp.deadline)
|
labels, err = netstorage.GetLabels(qt, limit, cp.deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot obtain labels: %w", err)
|
return fmt.Errorf("cannot obtain labels: %w", err)
|
||||||
}
|
}
|
||||||
@ -674,7 +685,7 @@ func LabelsHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseW
|
|||||||
MinTimestamp: cp.start,
|
MinTimestamp: cp.start,
|
||||||
MaxTimestamp: cp.end,
|
MaxTimestamp: cp.end,
|
||||||
}
|
}
|
||||||
labels, err = netstorage.GetLabelsOnTimeRange(qt, tr, cp.deadline)
|
labels, err = netstorage.GetLabelsOnTimeRange(qt, tr, limit, cp.deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot obtain labels on time range: %w", err)
|
return fmt.Errorf("cannot obtain labels on time range: %w", err)
|
||||||
}
|
}
|
||||||
@ -685,7 +696,7 @@ func LabelsHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseW
|
|||||||
if cp.start == 0 {
|
if cp.start == 0 {
|
||||||
cp.start = cp.end - defaultStep
|
cp.start = cp.end - defaultStep
|
||||||
}
|
}
|
||||||
labels, err = labelsWithMatches(qt, cp)
|
labels, err = labelsWithMatches(qt, cp, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot obtain labels for timeRange=[%d..%d]: %w", cp.start, cp.end, err)
|
return fmt.Errorf("cannot obtain labels for timeRange=[%d..%d]: %w", cp.start, cp.end, err)
|
||||||
}
|
}
|
||||||
@ -701,7 +712,7 @@ func LabelsHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseW
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func labelsWithMatches(qt *querytracer.Tracer, cp *commonParams) ([]string, error) {
|
func labelsWithMatches(qt *querytracer.Tracer, cp *commonParams, limit int) ([]string, error) {
|
||||||
sq := storage.NewSearchQuery(cp.start, cp.end, cp.filterss, *maxSeriesLimit)
|
sq := storage.NewSearchQuery(cp.start, cp.end, cp.filterss, *maxSeriesLimit)
|
||||||
m := make(map[string]struct{})
|
m := make(map[string]struct{})
|
||||||
if cp.end-cp.start > 24*3600*1000 {
|
if cp.end-cp.start > 24*3600*1000 {
|
||||||
@ -741,6 +752,9 @@ func labelsWithMatches(qt *querytracer.Tracer, cp *commonParams) ([]string, erro
|
|||||||
for label := range m {
|
for label := range m {
|
||||||
labels = append(labels, label)
|
labels = append(labels, label)
|
||||||
}
|
}
|
||||||
|
if limit > 0 && limit < len(labels) {
|
||||||
|
labels = labels[:limit]
|
||||||
|
}
|
||||||
sort.Strings(labels)
|
sort.Strings(labels)
|
||||||
qt.Printf("sort %d labels", len(labels))
|
qt.Printf("sort %d labels", len(labels))
|
||||||
return labels, nil
|
return labels, nil
|
||||||
|
@ -25,6 +25,19 @@ func roundToSeconds(ms int64) int64 {
|
|||||||
return ms - ms%1000
|
return ms - ms%1000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetInt returns integer value from the given argKey.
|
||||||
|
func GetInt(r *http.Request, argKey string) (int, error) {
|
||||||
|
argValue := r.FormValue(argKey)
|
||||||
|
if len(argValue) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
n, err := strconv.Atoi(argValue)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("cannot parse integer %q=%q: %w", argKey, argValue, err)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetTime returns time from the given argKey query arg.
|
// GetTime returns time from the given argKey query arg.
|
||||||
//
|
//
|
||||||
// If argKey is missing in r, then defaultMs rounded to seconds is returned.
|
// If argKey is missing in r, then defaultMs rounded to seconds is returned.
|
||||||
|
@ -23,6 +23,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
|||||||
* FEATURE: add support of `lowercase` and `uppercase` relabeling actions in the same way as [Prometheus 2.36.0 does](https://github.com/prometheus/prometheus/releases/tag/v2.36.0). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2664).
|
* FEATURE: add support of `lowercase` and `uppercase` relabeling actions in the same way as [Prometheus 2.36.0 does](https://github.com/prometheus/prometheus/releases/tag/v2.36.0). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2664).
|
||||||
* FEATURE: add ability to change the `indexdb` rotation timezone offset via `-retentionTimezoneOffset` command-line flag. Previously it was performed at 4am UTC time. This could lead to performance degradation in the middle of the day when VictoriaMetrics runs in time zones located too far from UTC. Thanks to @cnych for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2574).
|
* FEATURE: add ability to change the `indexdb` rotation timezone offset via `-retentionTimezoneOffset` command-line flag. Previously it was performed at 4am UTC time. This could lead to performance degradation in the middle of the day when VictoriaMetrics runs in time zones located too far from UTC. Thanks to @cnych for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2574).
|
||||||
* FEATURE: limit the number of background merge threads on systems with big number of CPU cores by default. This increases the max size of parts, which can be created during background merge when `-storageDataPath` directory has limited free disk space. This may improve on-disk data compression efficiency and query performance. The limits can be tuned if needed with `-smallMergeConcurrency` and `-bigMergeConcurrency` command-line flags. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2673).
|
* FEATURE: limit the number of background merge threads on systems with big number of CPU cores by default. This increases the max size of parts, which can be created during background merge when `-storageDataPath` directory has limited free disk space. This may improve on-disk data compression efficiency and query performance. The limits can be tuned if needed with `-smallMergeConcurrency` and `-bigMergeConcurrency` command-line flags. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2673).
|
||||||
|
* FEATURE: accept optional `limit` query arg at [/api/v1/labels](https://prometheus.io/docs/prometheus/latest/querying/api/#getting-label-names) and [/api/v1/label_values](https://prometheus.io/docs/prometheus/latest/querying/api/#querying-label-values) for limiting the numbef of sample entries returned from these endpoints. See [these docs](https://docs.victoriametrics.com/#prometheus-querying-api-enhancements).
|
||||||
* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): support `limit` param per-group for limiting number of produced samples per each rule. Thanks to @Howie59 for [implementation](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2676).
|
* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): support `limit` param per-group for limiting number of produced samples per each rule. Thanks to @Howie59 for [implementation](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2676).
|
||||||
* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): remove dependency on Internet access at [web API pages](https://docs.victoriametrics.com/vmalert.html#web). Previously the functionality and the layout of these pages was broken without Internet access. See [shis issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2594).
|
* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): remove dependency on Internet access at [web API pages](https://docs.victoriametrics.com/vmalert.html#web). Previously the functionality and the layout of these pages was broken without Internet access. See [shis issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2594).
|
||||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): implement the `http://vmagent:8429/service-discovery` page in the same way as Prometheus does. This page shows the original labels for all the discovered targets alongside the resulting labels after the relabeling. This simplifies service discovery debugging.
|
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): implement the `http://vmagent:8429/service-discovery` page in the same way as Prometheus does. This page shows the original labels for all the discovered targets alongside the resulting labels after the relabeling. This simplifies service discovery debugging.
|
||||||
|
@ -607,6 +607,8 @@ For example, the following query would return data for the last 30 minutes: `/ap
|
|||||||
|
|
||||||
VictoriaMetrics accepts `round_digits` query arg for `/api/v1/query` and `/api/v1/query_range` handlers. It can be used for rounding response values to the given number of digits after the decimal point. For example, `/api/v1/query?query=avg_over_time(temperature[1h])&round_digits=2` would round response values to up to two digits after the decimal point.
|
VictoriaMetrics accepts `round_digits` query arg for `/api/v1/query` and `/api/v1/query_range` handlers. It can be used for rounding response values to the given number of digits after the decimal point. For example, `/api/v1/query?query=avg_over_time(temperature[1h])&round_digits=2` would round response values to up to two digits after the decimal point.
|
||||||
|
|
||||||
|
VictoriaMetrics accepts `limit` query arg for `/api/v1/labels` and `/api/v1/label/<labelName>/values` handlers for limiting the number of returned entries. For example, the query to `/api/v1/labels?limit=5` returns a sample of up to 5 unique labels, while ignoring the rest of labels. If the provided `limit` value exceeds the corresponding `-search.maxTagKeys` / `-search.maxTagValues` command-line flag values, then limits specified in the command-line flags are used.
|
||||||
|
|
||||||
By default, VictoriaMetrics returns time series for the last 5 minutes from `/api/v1/series`, while the Prometheus API defaults to all time. Use `start` and `end` to select a different time range.
|
By default, VictoriaMetrics returns time series for the last 5 minutes from `/api/v1/series`, while the Prometheus API defaults to all time. Use `start` and `end` to select a different time range.
|
||||||
|
|
||||||
Additionally, VictoriaMetrics provides the following handlers:
|
Additionally, VictoriaMetrics provides the following handlers:
|
||||||
|
@ -611,6 +611,8 @@ For example, the following query would return data for the last 30 minutes: `/ap
|
|||||||
|
|
||||||
VictoriaMetrics accepts `round_digits` query arg for `/api/v1/query` and `/api/v1/query_range` handlers. It can be used for rounding response values to the given number of digits after the decimal point. For example, `/api/v1/query?query=avg_over_time(temperature[1h])&round_digits=2` would round response values to up to two digits after the decimal point.
|
VictoriaMetrics accepts `round_digits` query arg for `/api/v1/query` and `/api/v1/query_range` handlers. It can be used for rounding response values to the given number of digits after the decimal point. For example, `/api/v1/query?query=avg_over_time(temperature[1h])&round_digits=2` would round response values to up to two digits after the decimal point.
|
||||||
|
|
||||||
|
VictoriaMetrics accepts `limit` query arg for `/api/v1/labels` and `/api/v1/label/<labelName>/values` handlers for limiting the number of returned entries. For example, the query to `/api/v1/labels?limit=5` returns a sample of up to 5 unique labels, while ignoring the rest of labels. If the provided `limit` value exceeds the corresponding `-search.maxTagKeys` / `-search.maxTagValues` command-line flag values, then limits specified in the command-line flags are used.
|
||||||
|
|
||||||
By default, VictoriaMetrics returns time series for the last 5 minutes from `/api/v1/series`, while the Prometheus API defaults to all time. Use `start` and `end` to select a different time range.
|
By default, VictoriaMetrics returns time series for the last 5 minutes from `/api/v1/series`, while the Prometheus API defaults to all time. Use `start` and `end` to select a different time range.
|
||||||
|
|
||||||
Additionally, VictoriaMetrics provides the following handlers:
|
Additionally, VictoriaMetrics provides the following handlers:
|
||||||
|
Loading…
Reference in New Issue
Block a user