fix le buckets when adjacent vmrange is empty (#4021)

There is a bug here where if you have a single bucket like:

foo{vmrange="4.084e+02...4.642e+02"} 2 123

The expected output is three le encoded buckets like:

foo{le="4.084e+02"} 0 123
foo{le="4.642e+02"} 2 123
foo{le="+Inf"} 2 123

This correctly encodes the start and end of the vmrange.
If however, the input contains the previous bucket, and that bucket is
empty then you only get the end le and +Inf out currently, i.e:

foo{vmrange="7.743e+05...8.799e+05"} 5 123
foo{vmrange="6.813e+05...7.743e+05"} 0 123

results in:

foo{le="8.799e+05"} 5 123
foo{le="+Inf"} 5 123

This causes issues when you go to compute a quantile because this means
that the assumed lower bound of the buckets is 0 and this we interpolate
between 0->end rather than the vmrange start->end as expected.
This commit is contained in:
Ze'ev Klapow 2023-03-27 20:54:19 -04:00 committed by Aliaksandr Valialkin
parent 87769b36d1
commit b3ee33eb8e
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
2 changed files with 27 additions and 1 deletions

View file

@ -548,14 +548,25 @@ func vmrangeBucketsToLE(tss []*timeseries) []*timeseries {
sort.Slice(xss, func(i, j int) bool { return xss[i].end < xss[j].end }) sort.Slice(xss, func(i, j int) bool { return xss[i].end < xss[j].end })
xssNew := make([]x, 0, len(xss)+2) xssNew := make([]x, 0, len(xss)+2)
var xsPrev x var xsPrev x
hasNonEmpty := false
uniqTs := make(map[string]*timeseries, len(xss)) uniqTs := make(map[string]*timeseries, len(xss))
for _, xs := range xss { for _, xs := range xss {
ts := xs.ts ts := xs.ts
if isZeroTS(ts) { if isZeroTS(ts) {
// Skip time series with zeros. They are substituted by xssNew below.
xsPrev = xs xsPrev = xs
if uniqTs[xs.endStr] == nil {
uniqTs[xs.endStr] = xs.ts
xssNew = append(xssNew, x{
endStr: xs.endStr,
end: xs.end,
ts: copyTS(ts, xs.endStr),
})
}
continue continue
} }
hasNonEmpty = true
if xs.start != xsPrev.end && uniqTs[xs.startStr] == nil { if xs.start != xsPrev.end && uniqTs[xs.startStr] == nil {
uniqTs[xs.startStr] = xs.ts uniqTs[xs.startStr] = xs.ts
xssNew = append(xssNew, x{ xssNew = append(xssNew, x{
@ -575,6 +586,12 @@ func vmrangeBucketsToLE(tss []*timeseries) []*timeseries {
} }
xsPrev = xs xsPrev = xs
} }
if !hasNonEmpty {
xssNew = []x{}
continue
}
if !math.IsInf(xsPrev.end, 1) && !isZeroTS(xsPrev.ts) { if !math.IsInf(xsPrev.end, 1) && !isZeroTS(xsPrev.ts) {
xssNew = append(xssNew, x{ xssNew = append(xssNew, x{
endStr: "+Inf", endStr: "+Inf",

View file

@ -78,6 +78,15 @@ foo{le="+Inf"} 1.23 456`,
foo{le="+Inf"} 5.3 0`, foo{le="+Inf"} 5.3 0`,
) )
// Adjacent empty vmrange bucket
f(
`foo{vmrange="7.743e+05...8.799e+05"} 5 123
foo{vmrange="6.813e+05...7.743e+05"} 0 123`,
`foo{le="7.743e+05"} 0 123
foo{le="8.799e+05"} 5 123
foo{le="+Inf"} 5 123`,
)
// Multiple non-empty vmrange buckets // Multiple non-empty vmrange buckets
f( f(
`foo{vmrange="4.084e+02...4.642e+02"} 2 123 `foo{vmrange="4.084e+02...4.642e+02"} 2 123