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

Commit 46b9904

Browse files
authored
Merge pull request #1580 from bloomberg/find_lastupdate
tags/findSeries - add lastts-json format
2 parents 7ca637a + 1a134a2 commit 46b9904

File tree

5 files changed

+150
-14
lines changed

5 files changed

+150
-14
lines changed

api/graphite.go

+64-10
Original file line numberDiff line numberDiff line change
@@ -686,8 +686,7 @@ func (s *Server) executePlan(ctx context.Context, orgId uint32, plan expr.Plan)
686686
if err != nil {
687687
return nil, meta, err
688688
}
689-
690-
series, err = s.clusterFindByTag(ctx, orgId, exprs, int64(r.From), maxSeriesPerReq-len(reqs))
689+
series, err = s.clusterFindByTag(ctx, orgId, exprs, int64(r.From), maxSeriesPerReq-len(reqs), false)
691690
} else {
692691
series, err = s.findSeries(ctx, orgId, []string{r.Query}, int64(r.From))
693692
}
@@ -1012,7 +1011,16 @@ func (s *Server) graphiteTagFindSeries(ctx *middleware.Context, request models.G
10121011
return
10131012
}
10141013

1015-
series, err := s.clusterFindByTag(reqCtx, ctx.OrgId, expressions, request.From, maxSeriesPerReq)
1014+
// Out of the provided soft limit and the global `maxSeriesPerReq` hard limit
1015+
// (either of which may be 0 aka disabled), pick the only one that matters: the most strict one.
1016+
limit := request.Limit
1017+
isSoftLimit := limit > 0
1018+
if maxSeriesPerReq > 0 && (limit == 0 || limit > maxSeriesPerReq) {
1019+
limit = maxSeriesPerReq
1020+
isSoftLimit = false
1021+
}
1022+
1023+
series, err := s.clusterFindByTag(reqCtx, ctx.OrgId, expressions, request.From, limit, isSoftLimit)
10161024
if err != nil {
10171025
response.Write(ctx, response.WrapError(err))
10181026
return
@@ -1025,14 +1033,47 @@ func (s *Server) graphiteTagFindSeries(ctx *middleware.Context, request models.G
10251033
return
10261034
default:
10271035
}
1028-
seriesNames := make([]string, 0, len(series))
1029-
for _, serie := range series {
1030-
seriesNames = append(seriesNames, serie.Pattern)
1036+
1037+
var warnings []string
1038+
if len(series) == limit {
1039+
warnings = append(warnings, "Result set truncated due to limit")
1040+
}
1041+
1042+
switch request.Format {
1043+
case "lastts-json":
1044+
retval := models.GraphiteTagFindSeriesLastTsResp{Warnings: warnings}
1045+
retval.Series = make([]models.SeriesLastTs, 0, len(series))
1046+
for _, serie := range series {
1047+
var lastUpdate int64
1048+
for _, node := range serie.Series {
1049+
for _, ndef := range node.Defs {
1050+
if ndef.LastUpdate > lastUpdate {
1051+
lastUpdate = ndef.LastUpdate
1052+
}
1053+
}
1054+
}
1055+
retval.Series = append(retval.Series, models.SeriesLastTs{Series: serie.Pattern, Ts: lastUpdate})
1056+
}
1057+
1058+
response.Write(ctx, response.NewJson(200, retval, ""))
1059+
case "series-json":
1060+
seriesNames := make([]string, 0, len(series))
1061+
for _, serie := range series {
1062+
seriesNames = append(seriesNames, serie.Pattern)
1063+
}
1064+
1065+
if request.Meta == true {
1066+
retval := models.GraphiteTagFindSeriesMetaResp{Series: seriesNames, Warnings: warnings}
1067+
response.Write(ctx, response.NewJson(200, retval, ""))
1068+
} else {
1069+
response.Write(ctx, response.NewJson(200, seriesNames, ""))
1070+
}
10311071
}
1032-
response.Write(ctx, response.NewJson(200, seriesNames, ""))
10331072
}
10341073

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

1050-
// 0 disables the check, so only check if maxSeriesPerReq > 0
1051-
if maxSeriesPerReq > 0 && len(resp.Metrics)+len(allSeries) > maxSeries {
1091+
// Only check if maxSeriesPerReq > 0 (meaning enabled) or soft-limited
1092+
checkSeriesLimit := maxSeriesPerReq > 0 || softLimit
1093+
if checkSeriesLimit && len(resp.Metrics)+len(allSeries) > maxSeries {
1094+
if softLimit {
1095+
remainingSpace := maxSeries - len(allSeries)
1096+
// Fill in up to maxSeries
1097+
for _, series := range resp.Metrics[:remainingSpace] {
1098+
allSeries = append(allSeries, Series{
1099+
Pattern: series.Path,
1100+
Node: r.peer,
1101+
Series: []idx.Node{series},
1102+
})
1103+
}
1104+
return allSeries, nil
1105+
}
10521106
return nil,
10531107
response.NewError(
10541108
http.StatusRequestEntityTooLarge,

api/models/graphite.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ import (
2626
//msgp:ignore GraphiteTagDetailsValueResp
2727
//msgp:ignore GraphiteTagFindSeries
2828
//msgp:ignore GraphiteTagFindSeriesResp
29+
//msgp:ignore GraphiteTagFindSeriesLastTsResp
30+
//msgp:ignore GraphiteTagFindSeriesMetaResp
2931
//msgp:ignore GraphiteTagResp
3032
//msgp:ignore GraphiteTags
3133
//msgp:ignore GraphiteTagsResp
3234
//msgp:ignore MetricNames
3335
//msgp:ignore MetricsDelete
3436
//msgp:ignore SeriesCompleter
3537
//msgp:ignore SeriesCompleterItem
38+
//msgp:ignore SeriesLastTs
3639
//msgp:ignore SeriesTree
3740
//msgp:ignore SeriesTreeItem
3841

@@ -117,14 +120,32 @@ type GraphiteTagDetailsValueResp struct {
117120
}
118121

119122
type GraphiteTagFindSeries struct {
120-
Expr []string `json:"expr" form:"expr"`
121-
From int64 `json:"from" form:"from"`
123+
Expr []string `json:"expr" form:"expr"`
124+
From int64 `json:"from" form:"from"`
125+
Format string `json:"format" form:"format" binding:"In(,series-json,lastts-json);Default(series-json)"`
126+
Limit int `json:"limit" binding:"Default(0)"`
127+
Meta bool `json:"meta" binding:"Default(false)"`
122128
}
123129

124130
type GraphiteTagFindSeriesResp struct {
125131
Series []string `json:"series"`
126132
}
127133

134+
type SeriesLastTs struct {
135+
Series string `json:"val"`
136+
Ts int64 `json:"lastTs"`
137+
}
138+
139+
type GraphiteTagFindSeriesLastTsResp struct {
140+
Series []SeriesLastTs `json:"series"`
141+
Warnings []string `json:"warnings,omitempty"`
142+
}
143+
144+
type GraphiteTagFindSeriesMetaResp struct {
145+
Series []string `json:"series"`
146+
Warnings []string `json:"warnings,omitempty"`
147+
}
148+
128149
type GraphiteTagDelSeries struct {
129150
Paths []string `json:"path" form:"path"`
130151
Propagate bool `json:"propagate" form:"propagate" binding:"Default(true)"`

api/prometheus_querier.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func (q *querier) Select(matchers ...*labels.Matcher) (storage.SeriesSet, error)
7070
return nil, err
7171
}
7272

73-
series, err := q.clusterFindByTag(q.ctx, q.OrgID, parsedExpressions, 0, maxSeriesPerReq)
73+
series, err := q.clusterFindByTag(q.ctx, q.OrgID, parsedExpressions, 0, maxSeriesPerReq, false)
7474
if err != nil {
7575
return nil, err
7676
}

docs/cloud/http-api.md

+18
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,11 @@ that duplicate entries will be returned.
279279

280280
* expr (required): a list of [tag expressions](#tag-expressions)
281281
* from: Graphite [from time specification](#fromto) (optional. defaults to now-24hours)
282+
* format: series-json, lastts-json. (defaults to series-json)
283+
* limit: max number to return. (default: 0)
284+
Note: the resultset is also subjected to the cluster configuration.
285+
if the result set is larger than the cluster configuration, an error is returned. If it breaches the provided limit, the result is truncated.
286+
* 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)
282287

283288
##### Example
284289

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

298+
```sh
299+
curl -H "Authorization: Bearer $key" "$out/tags/findSeries?expr=datacenter=dc1&expr=server=web01&format=lastts-json"
300+
301+
{
302+
"series": [
303+
{
304+
"lastTs": 1576683990,
305+
"val": "disk.used;datacenter=dc1;rack=a1;server=web01"
306+
}
307+
]
308+
}
309+
```
310+
293311
### Render `/render` (return data for a given query)
294312

295313
Graphite-web-like api. It can return JSON, pickle or messagepack output

docs/http-api.md

+44-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,50 @@ json and treejson are the same.
6666
curl -H "X-Org-Id: 12345" "http://localhost:6060/metrics/find?query=statsd.fakesite.counters.session_start.*.count"
6767
```
6868

69+
## Find tagged metrics
70+
71+
```
72+
GET /tags/findSeries
73+
POST /tags/findSeries
74+
```
75+
76+
Returns metrics which match tag queries and have received an update since `from`.
77+
Note: the returned results are not deduplicated and in certain cases it is possible
78+
that duplicate entries will be returned.
79+
80+
##### Parameters
81+
82+
* expr (required): a list of [tag expressions](#tag-expressions)
83+
* from: Graphite [from time specification](#fromto) (optional. defaults to now-24hours)
84+
* format: series-json, lastts-json. (defaults to series-json)
85+
* limit: max number to return. (default: 0)
86+
Note: the resultset is also subjected to the `http.max-series-per-req` config setting.
87+
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.
88+
* 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)
89+
90+
##### Example
91+
92+
```sh
93+
curl "http://localhost:6060/tags/findSeries?expr=datacenter=dc1&expr=server=web01"
94+
95+
[
96+
"disk.used;datacenter=dc1;rack=a1;server=web01"
97+
]
98+
```
99+
100+
```sh
101+
curl "http://localhost:6060/tags/findSeries?expr=datacenter=dc1&expr=server=web01&format=lastts-json"
102+
103+
{
104+
"series": [
105+
{
106+
"lastTs": 1576683990,
107+
"val": "disk.used;datacenter=dc1;rack=a1;server=web01"
108+
}
109+
]
110+
}
111+
```
112+
69113
## Deleting metrics
70114

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

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

0 commit comments

Comments
 (0)