app/vmselect/graphite: properly handle case when /metrics/find finds both leaf and node for the given query=prefix.*

In this case only node must be returned with stripped dot in the end of id as carbonapi does
This commit is contained in:
Aliaksandr Valialkin 2020-09-29 11:00:41 +03:00
parent ffa6581c46
commit e66f7edfc9
3 changed files with 135 additions and 98 deletions

View File

@ -83,10 +83,10 @@ func MetricsFindHandler(startTime time.Time, at *auth.Token, w http.ResponseWrit
if isPartial && searchutils.GetDenyPartialResponse(r) { if isPartial && searchutils.GetDenyPartialResponse(r) {
return fmt.Errorf("cannot return full response, since some of vmstorage nodes are unavailable") return fmt.Errorf("cannot return full response, since some of vmstorage nodes are unavailable")
} }
paths = deduplicatePaths(paths)
if leavesOnly { if leavesOnly {
paths = filterLeaves(paths, delimiter) paths = filterLeaves(paths, delimiter)
} }
paths = deduplicatePaths(paths, delimiter)
sortPaths(paths, delimiter) sortPaths(paths, delimiter)
contentType := "application/json" contentType := "application/json"
if jsonp != "" { if jsonp != "" {
@ -103,13 +103,38 @@ func MetricsFindHandler(startTime time.Time, at *auth.Token, w http.ResponseWrit
return nil return nil
} }
func deduplicatePaths(paths []string) []string { func deduplicatePaths(paths []string, delimiter string) []string {
m := make(map[string]struct{}, len(paths)) if len(paths) == 0 {
for _, path := range paths { return nil
m[path] = struct{}{}
} }
dst := make([]string, 0, len(m))
for path := range m { sort.Strings(paths)
// remove duplicates
dst := paths[:1]
for _, path := range paths[1:] {
prevPath := dst[len(dst)-1]
if path == prevPath {
// Skip duplicate path.
continue
}
dst = append(dst, path)
}
paths = dst
// substitute `path` and `path<delimiter>` with `path<delimiter><delimiter>` like carbonapi does.
// Such path is treated specially during rendering - see metrics_find_response.qtpl for details.
dst = paths[:1]
for _, path := range paths[1:] {
prevPath := dst[len(dst)-1]
if len(path) == len(prevPath)+1 && strings.HasSuffix(path, delimiter) && strings.HasPrefix(path, prevPath) {
// The path is equivalent to <prevPath> + <delimiter>
// Overwrite the prevPath with <path> + <delimiter> as carbonapi does.
// I.e. the resulting path ends with double delimiter.
// Such path is treated specially during rendering - see metrics_find_response.qtpl for details.
dst[len(dst)-1] = path + delimiter
continue
}
dst = append(dst, path) dst = append(dst, path)
} }
return dst return dst

View File

@ -46,14 +46,20 @@ See https://graphite-api.readthedocs.io/en/latest/api.html#metrics-find
{% for i, path := range paths %} {% for i, path := range paths %}
{ {
{% code {% code
id := path
allowChildren := "0" allowChildren := "0"
isLeaf := "1" isLeaf := "1"
if strings.HasSuffix(path, delimiter) { if strings.HasSuffix(id, delimiter) {
if strings.HasSuffix(id[:len(id)-1], delimiter) {
// Special case when id ends with double delimiter.
// See deduplicatePaths() code for details.
id = id[:len(id)-2]
}
allowChildren = "1" allowChildren = "1"
isLeaf = "0" isLeaf = "0"
} }
%} %}
"id": {%q= path %}, "id": {%q= id %},
"text": {%= metricPathName(path, delimiter) %}, "text": {%= metricPathName(path, delimiter) %},
"allowChildren": {%s= allowChildren %}, "allowChildren": {%s= allowChildren %},
"expandable": {%s= allowChildren %}, "expandable": {%s= allowChildren %},

View File

@ -170,48 +170,54 @@ func streammetricsFindResponseTreeJSON(qw422016 *qt422016.Writer, paths []string
//line app/vmselect/graphite/metrics_find_response.qtpl:46 //line app/vmselect/graphite/metrics_find_response.qtpl:46
qw422016.N().S(`{`) qw422016.N().S(`{`)
//line app/vmselect/graphite/metrics_find_response.qtpl:49 //line app/vmselect/graphite/metrics_find_response.qtpl:49
id := path
allowChildren := "0" allowChildren := "0"
isLeaf := "1" isLeaf := "1"
if strings.HasSuffix(path, delimiter) { if strings.HasSuffix(id, delimiter) {
if strings.HasSuffix(id[:len(id)-1], delimiter) {
// Special case when id ends with double delimiter.
// See deduplicatePaths() code for details.
id = id[:len(id)-2]
}
allowChildren = "1" allowChildren = "1"
isLeaf = "0" isLeaf = "0"
} }
//line app/vmselect/graphite/metrics_find_response.qtpl:55 //line app/vmselect/graphite/metrics_find_response.qtpl:61
qw422016.N().S(`"id":`) qw422016.N().S(`"id":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:56 //line app/vmselect/graphite/metrics_find_response.qtpl:62
qw422016.N().Q(path) qw422016.N().Q(id)
//line app/vmselect/graphite/metrics_find_response.qtpl:56 //line app/vmselect/graphite/metrics_find_response.qtpl:62
qw422016.N().S(`,"text":`) qw422016.N().S(`,"text":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:57
streammetricPathName(qw422016, path, delimiter)
//line app/vmselect/graphite/metrics_find_response.qtpl:57
qw422016.N().S(`,"allowChildren":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:58
qw422016.N().S(allowChildren)
//line app/vmselect/graphite/metrics_find_response.qtpl:58
qw422016.N().S(`,"expandable":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:59
qw422016.N().S(allowChildren)
//line app/vmselect/graphite/metrics_find_response.qtpl:59
qw422016.N().S(`,"leaf":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:60
qw422016.N().S(isLeaf)
//line app/vmselect/graphite/metrics_find_response.qtpl:60
qw422016.N().S(`}`)
//line app/vmselect/graphite/metrics_find_response.qtpl:62
if i+1 < len(paths) {
//line app/vmselect/graphite/metrics_find_response.qtpl:62
qw422016.N().S(`,`)
//line app/vmselect/graphite/metrics_find_response.qtpl:62
}
//line app/vmselect/graphite/metrics_find_response.qtpl:63 //line app/vmselect/graphite/metrics_find_response.qtpl:63
streammetricPathName(qw422016, path, delimiter)
//line app/vmselect/graphite/metrics_find_response.qtpl:63
qw422016.N().S(`,"allowChildren":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:64
qw422016.N().S(allowChildren)
//line app/vmselect/graphite/metrics_find_response.qtpl:64
qw422016.N().S(`,"expandable":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:65
qw422016.N().S(allowChildren)
//line app/vmselect/graphite/metrics_find_response.qtpl:65
qw422016.N().S(`,"leaf":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:66
qw422016.N().S(isLeaf)
//line app/vmselect/graphite/metrics_find_response.qtpl:66
qw422016.N().S(`}`)
//line app/vmselect/graphite/metrics_find_response.qtpl:68
if i+1 < len(paths) {
//line app/vmselect/graphite/metrics_find_response.qtpl:68
qw422016.N().S(`,`)
//line app/vmselect/graphite/metrics_find_response.qtpl:68
}
//line app/vmselect/graphite/metrics_find_response.qtpl:69
} }
//line app/vmselect/graphite/metrics_find_response.qtpl:64 //line app/vmselect/graphite/metrics_find_response.qtpl:70
if addWildcards && len(paths) > 1 { if addWildcards && len(paths) > 1 {
//line app/vmselect/graphite/metrics_find_response.qtpl:64 //line app/vmselect/graphite/metrics_find_response.qtpl:70
qw422016.N().S(`,{`) qw422016.N().S(`,{`)
//line app/vmselect/graphite/metrics_find_response.qtpl:67 //line app/vmselect/graphite/metrics_find_response.qtpl:73
path := paths[0] path := paths[0]
for strings.HasSuffix(path, delimiter) { for strings.HasSuffix(path, delimiter) {
path = path[:len(path)-1] path = path[:len(path)-1]
@ -232,60 +238,60 @@ func streammetricsFindResponseTreeJSON(qw422016 *qt422016.Writer, paths []string
} }
} }
//line app/vmselect/graphite/metrics_find_response.qtpl:86 //line app/vmselect/graphite/metrics_find_response.qtpl:92
qw422016.N().S(`"id":`) qw422016.N().S(`"id":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:87 //line app/vmselect/graphite/metrics_find_response.qtpl:93
qw422016.N().Q(id) qw422016.N().Q(id)
//line app/vmselect/graphite/metrics_find_response.qtpl:87 //line app/vmselect/graphite/metrics_find_response.qtpl:93
qw422016.N().S(`,"text": "*","allowChildren":`) qw422016.N().S(`,"text": "*","allowChildren":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:89 //line app/vmselect/graphite/metrics_find_response.qtpl:95
qw422016.N().S(allowChildren) qw422016.N().S(allowChildren)
//line app/vmselect/graphite/metrics_find_response.qtpl:89 //line app/vmselect/graphite/metrics_find_response.qtpl:95
qw422016.N().S(`,"expandable":`) qw422016.N().S(`,"expandable":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:90 //line app/vmselect/graphite/metrics_find_response.qtpl:96
qw422016.N().S(allowChildren) qw422016.N().S(allowChildren)
//line app/vmselect/graphite/metrics_find_response.qtpl:90 //line app/vmselect/graphite/metrics_find_response.qtpl:96
qw422016.N().S(`,"leaf":`) qw422016.N().S(`,"leaf":`)
//line app/vmselect/graphite/metrics_find_response.qtpl:91
qw422016.N().S(isLeaf)
//line app/vmselect/graphite/metrics_find_response.qtpl:91
qw422016.N().S(`}`)
//line app/vmselect/graphite/metrics_find_response.qtpl:93
}
//line app/vmselect/graphite/metrics_find_response.qtpl:93
qw422016.N().S(`]`)
//line app/vmselect/graphite/metrics_find_response.qtpl:95
}
//line app/vmselect/graphite/metrics_find_response.qtpl:95
func writemetricsFindResponseTreeJSON(qq422016 qtio422016.Writer, paths []string, delimiter string, addWildcards bool) {
//line app/vmselect/graphite/metrics_find_response.qtpl:95
qw422016 := qt422016.AcquireWriter(qq422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:95
streammetricsFindResponseTreeJSON(qw422016, paths, delimiter, addWildcards)
//line app/vmselect/graphite/metrics_find_response.qtpl:95
qt422016.ReleaseWriter(qw422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:95
}
//line app/vmselect/graphite/metrics_find_response.qtpl:95
func metricsFindResponseTreeJSON(paths []string, delimiter string, addWildcards bool) string {
//line app/vmselect/graphite/metrics_find_response.qtpl:95
qb422016 := qt422016.AcquireByteBuffer()
//line app/vmselect/graphite/metrics_find_response.qtpl:95
writemetricsFindResponseTreeJSON(qb422016, paths, delimiter, addWildcards)
//line app/vmselect/graphite/metrics_find_response.qtpl:95
qs422016 := string(qb422016.B)
//line app/vmselect/graphite/metrics_find_response.qtpl:95
qt422016.ReleaseByteBuffer(qb422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:95
return qs422016
//line app/vmselect/graphite/metrics_find_response.qtpl:95
}
//line app/vmselect/graphite/metrics_find_response.qtpl:97 //line app/vmselect/graphite/metrics_find_response.qtpl:97
func streammetricPathName(qw422016 *qt422016.Writer, path, delimiter string) { qw422016.N().S(isLeaf)
//line app/vmselect/graphite/metrics_find_response.qtpl:97
qw422016.N().S(`}`)
//line app/vmselect/graphite/metrics_find_response.qtpl:99 //line app/vmselect/graphite/metrics_find_response.qtpl:99
}
//line app/vmselect/graphite/metrics_find_response.qtpl:99
qw422016.N().S(`]`)
//line app/vmselect/graphite/metrics_find_response.qtpl:101
}
//line app/vmselect/graphite/metrics_find_response.qtpl:101
func writemetricsFindResponseTreeJSON(qq422016 qtio422016.Writer, paths []string, delimiter string, addWildcards bool) {
//line app/vmselect/graphite/metrics_find_response.qtpl:101
qw422016 := qt422016.AcquireWriter(qq422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:101
streammetricsFindResponseTreeJSON(qw422016, paths, delimiter, addWildcards)
//line app/vmselect/graphite/metrics_find_response.qtpl:101
qt422016.ReleaseWriter(qw422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:101
}
//line app/vmselect/graphite/metrics_find_response.qtpl:101
func metricsFindResponseTreeJSON(paths []string, delimiter string, addWildcards bool) string {
//line app/vmselect/graphite/metrics_find_response.qtpl:101
qb422016 := qt422016.AcquireByteBuffer()
//line app/vmselect/graphite/metrics_find_response.qtpl:101
writemetricsFindResponseTreeJSON(qb422016, paths, delimiter, addWildcards)
//line app/vmselect/graphite/metrics_find_response.qtpl:101
qs422016 := string(qb422016.B)
//line app/vmselect/graphite/metrics_find_response.qtpl:101
qt422016.ReleaseByteBuffer(qb422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:101
return qs422016
//line app/vmselect/graphite/metrics_find_response.qtpl:101
}
//line app/vmselect/graphite/metrics_find_response.qtpl:103
func streammetricPathName(qw422016 *qt422016.Writer, path, delimiter string) {
//line app/vmselect/graphite/metrics_find_response.qtpl:105
name := path name := path
for strings.HasSuffix(name, delimiter) { for strings.HasSuffix(name, delimiter) {
name = name[:len(name)-1] name = name[:len(name)-1]
@ -294,33 +300,33 @@ func streammetricPathName(qw422016 *qt422016.Writer, path, delimiter string) {
name = name[n+1:] name = name[n+1:]
} }
//line app/vmselect/graphite/metrics_find_response.qtpl:107 //line app/vmselect/graphite/metrics_find_response.qtpl:113
qw422016.N().Q(name) qw422016.N().Q(name)
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
} }
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
func writemetricPathName(qq422016 qtio422016.Writer, path, delimiter string) { func writemetricPathName(qq422016 qtio422016.Writer, path, delimiter string) {
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
streammetricPathName(qw422016, path, delimiter) streammetricPathName(qw422016, path, delimiter)
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
} }
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
func metricPathName(path, delimiter string) string { func metricPathName(path, delimiter string) string {
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
writemetricPathName(qb422016, path, delimiter) writemetricPathName(qb422016, path, delimiter)
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
return qs422016 return qs422016
//line app/vmselect/graphite/metrics_find_response.qtpl:108 //line app/vmselect/graphite/metrics_find_response.qtpl:114
} }