Skip to content
This repository has been archived by the owner on Aug 23, 2023. It is now read-only.

tags/findSeries - add lastts-json format #1580

Merged
merged 11 commits into from
Jan 14, 2020
74 changes: 64 additions & 10 deletions api/graphite.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,8 +686,7 @@ func (s *Server) executePlan(ctx context.Context, orgId uint32, plan expr.Plan)
if err != nil {
return nil, meta, err
}

series, err = s.clusterFindByTag(ctx, orgId, exprs, int64(r.From), maxSeriesPerReq-len(reqs))
series, err = s.clusterFindByTag(ctx, orgId, exprs, int64(r.From), maxSeriesPerReq-len(reqs), false)
} else {
series, err = s.findSeries(ctx, orgId, []string{r.Query}, int64(r.From))
}
Expand Down Expand Up @@ -1012,7 +1011,16 @@ func (s *Server) graphiteTagFindSeries(ctx *middleware.Context, request models.G
return
}

series, err := s.clusterFindByTag(reqCtx, ctx.OrgId, expressions, request.From, maxSeriesPerReq)
// Out of the provided soft limit and the global `maxSeriesPerReq` hard limit
// (either of which may be 0 aka disabled), pick the only one that matters: the most strict one.
limit := request.Limit
isSoftLimit := limit > 0
if maxSeriesPerReq > 0 && (limit == 0 || limit > maxSeriesPerReq) {
limit = maxSeriesPerReq
isSoftLimit = false
}

series, err := s.clusterFindByTag(reqCtx, ctx.OrgId, expressions, request.From, limit, isSoftLimit)
if err != nil {
response.Write(ctx, response.WrapError(err))
return
Expand All @@ -1025,14 +1033,47 @@ func (s *Server) graphiteTagFindSeries(ctx *middleware.Context, request models.G
return
default:
}
seriesNames := make([]string, 0, len(series))
for _, serie := range series {
seriesNames = append(seriesNames, serie.Pattern)

var warnings []string
if len(series) == limit {
warnings = append(warnings, "Result set truncated due to limit")
Dieterbe marked this conversation as resolved.
Show resolved Hide resolved
}

switch request.Format {
case "lastts-json":
retval := models.GraphiteTagFindSeriesLastTsResp{Warnings: warnings}
retval.Series = make([]models.SeriesLastTs, 0, len(series))
for _, serie := range series {
var lastUpdate int64
for _, node := range serie.Series {
for _, ndef := range node.Defs {
if ndef.LastUpdate > lastUpdate {
lastUpdate = ndef.LastUpdate
}
}
}
retval.Series = append(retval.Series, models.SeriesLastTs{Series: serie.Pattern, Ts: lastUpdate})
}

response.Write(ctx, response.NewJson(200, retval, ""))
case "series-json":
seriesNames := make([]string, 0, len(series))
for _, serie := range series {
seriesNames = append(seriesNames, serie.Pattern)
}

if request.Meta == true {
retval := models.GraphiteTagFindSeriesMetaResp{Series: seriesNames, Warnings: warnings}
response.Write(ctx, response.NewJson(200, retval, ""))
} else {
response.Write(ctx, response.NewJson(200, seriesNames, ""))
}
}
response.Write(ctx, response.NewJson(200, seriesNames, ""))
}

func (s *Server) clusterFindByTag(ctx context.Context, orgId uint32, expressions tagquery.Expressions, from int64, maxSeries int) ([]Series, error) {
// clusterFindByTag returns the Series matching the given expressions.
// If maxSeries is > 0, it specifies a limit which will truncate the resultset (if softLimit is true) or return an error otherwise.
func (s *Server) clusterFindByTag(ctx context.Context, orgId uint32, expressions tagquery.Expressions, from int64, maxSeries int, softLimit bool) ([]Series, error) {
data := models.IndexFindByTag{OrgId: orgId, Expr: expressions.Strings(), From: from}
newCtx, cancel := context.WithCancel(ctx)
defer cancel()
Expand All @@ -1047,8 +1088,21 @@ func (s *Server) clusterFindByTag(ctx context.Context, orgId uint32, expressions
return nil, err
}

// 0 disables the check, so only check if maxSeriesPerReq > 0
if maxSeriesPerReq > 0 && len(resp.Metrics)+len(allSeries) > maxSeries {
// Only check if maxSeriesPerReq > 0 (meaning enabled) or soft-limited
checkSeriesLimit := maxSeriesPerReq > 0 || softLimit
if checkSeriesLimit && len(resp.Metrics)+len(allSeries) > maxSeries {
if softLimit {
remainingSpace := maxSeries - len(allSeries)
// Fill in up to maxSeries
for _, series := range resp.Metrics[:remainingSpace] {
allSeries = append(allSeries, Series{
Pattern: series.Path,
Node: r.peer,
Series: []idx.Node{series},
})
}
return allSeries, nil
}
return nil,
response.NewError(
http.StatusRequestEntityTooLarge,
Expand Down
25 changes: 23 additions & 2 deletions api/models/graphite.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ import (
//msgp:ignore GraphiteTagDetailsValueResp
//msgp:ignore GraphiteTagFindSeries
//msgp:ignore GraphiteTagFindSeriesResp
//msgp:ignore GraphiteTagFindSeriesLastTsResp
//msgp:ignore GraphiteTagFindSeriesMetaResp
//msgp:ignore GraphiteTagResp
//msgp:ignore GraphiteTags
//msgp:ignore GraphiteTagsResp
//msgp:ignore MetricNames
//msgp:ignore MetricsDelete
//msgp:ignore SeriesCompleter
//msgp:ignore SeriesCompleterItem
//msgp:ignore SeriesLastTs
//msgp:ignore SeriesTree
//msgp:ignore SeriesTreeItem

Expand Down Expand Up @@ -117,14 +120,32 @@ type GraphiteTagDetailsValueResp struct {
}

type GraphiteTagFindSeries struct {
Expr []string `json:"expr" form:"expr"`
From int64 `json:"from" form:"from"`
Expr []string `json:"expr" form:"expr"`
From int64 `json:"from" form:"from"`
Format string `json:"format" form:"format" binding:"In(,series-json,lastts-json);Default(series-json)"`
Limit int `json:"limit" binding:"Default(0)"`
Meta bool `json:"meta" binding:"Default(false)"`
}

type GraphiteTagFindSeriesResp struct {
Series []string `json:"series"`
}

type SeriesLastTs struct {
Series string `json:"val"`
Ts int64 `json:"lastTs"`
}

type GraphiteTagFindSeriesLastTsResp struct {
Series []SeriesLastTs `json:"series"`
Warnings []string `json:"warnings,omitempty"`
}

type GraphiteTagFindSeriesMetaResp struct {
Series []string `json:"series"`
Warnings []string `json:"warnings,omitempty"`
}

type GraphiteTagDelSeries struct {
Paths []string `json:"path" form:"path"`
Propagate bool `json:"propagate" form:"propagate" binding:"Default(true)"`
Expand Down
2 changes: 1 addition & 1 deletion api/prometheus_querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (q *querier) Select(matchers ...*labels.Matcher) (storage.SeriesSet, error)
return nil, err
}

series, err := q.clusterFindByTag(q.ctx, q.OrgID, parsedExpressions, 0, maxSeriesPerReq)
series, err := q.clusterFindByTag(q.ctx, q.OrgID, parsedExpressions, 0, maxSeriesPerReq, false)
if err != nil {
return nil, err
}
Expand Down
18 changes: 18 additions & 0 deletions docs/cloud/http-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ that duplicate entries will be returned.

* expr (required): a list of [tag expressions](#tag-expressions)
* from: Graphite [from time specification](#fromto) (optional. defaults to now-24hours)
* format: series-json, lastts-json. (defaults to series-json)
* limit: max number to return. (default: 0)
Note: the resultset is also subjected to the cluster configuration.
if the result set is larger than the cluster configuration, an error is returned. If it breaches the provided limit, the result is truncated.
* meta: If false and format is `series-json` then return series names as array (graphite compatibility). If true, include meta information like warnings. (defaults to false)

##### Example

Expand All @@ -290,6 +295,19 @@ curl -H "Authorization: Bearer $key" "$out/tags/findSeries?expr=datacenter=dc1&e
]
```

```sh
curl -H "Authorization: Bearer $key" "$out/tags/findSeries?expr=datacenter=dc1&expr=server=web01&format=lastts-json"

{
"series": [
{
"lastTs": 1576683990,
"val": "disk.used;datacenter=dc1;rack=a1;server=web01"
}
]
}
```

### Render `/render` (return data for a given query)

Graphite-web-like api. It can return JSON, pickle or messagepack output
Expand Down
45 changes: 44 additions & 1 deletion docs/http-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,50 @@ json and treejson are the same.
curl -H "X-Org-Id: 12345" "http://localhost:6060/metrics/find?query=statsd.fakesite.counters.session_start.*.count"
```

## Find tagged metrics

```
GET /tags/findSeries
POST /tags/findSeries
```

Returns metrics which match tag queries and have received an update since `from`.
Note: the returned results are not deduplicated and in certain cases it is possible
that duplicate entries will be returned.

##### Parameters

* expr (required): a list of [tag expressions](#tag-expressions)
* from: Graphite [from time specification](#fromto) (optional. defaults to now-24hours)
* format: series-json, lastts-json. (defaults to series-json)
* limit: max number to return. (default: 0)
Note: the resultset is also subjected to the `http.max-series-per-req` config setting.
if the result set is larger than `http.max-series-per-req`, an error is returned. If it breaches the provided limit, the result is truncated.
* meta: If false and format is `series-json` then return series names as array (graphite compatibility). If true, include meta information like warnings. (defaults to false)

##### Example

```sh
curl "http://localhost:6060/tags/findSeries?expr=datacenter=dc1&expr=server=web01"

[
"disk.used;datacenter=dc1;rack=a1;server=web01"
]
```

```sh
curl "http://localhost:6060/tags/findSeries?expr=datacenter=dc1&expr=server=web01&format=lastts-json"

{
"series": [
{
"lastTs": 1576683990,
"val": "disk.used;datacenter=dc1;rack=a1;server=web01"
}
]
}
```

## Deleting metrics

This will delete any metrics (technically metricdefinitions) matching the query from the index.
Expand Down Expand Up @@ -475,4 +519,3 @@ The time specification is used throughout the http api and it can be any of thes
- `y`, `year`, `years`

* datetime in any of the following formats: `15:04 20060102`, `20060102`, `01/02/06`