diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go index 2c7c2e4058..b7efad70cd 100644 --- a/app/vmselect/promql/exec_test.go +++ b/app/vmselect/promql/exec_test.go @@ -2299,6 +2299,45 @@ func TestExecSuccess(t *testing.T) { resultExpected := []netstorage.Result{r} f(q, resultExpected) }) + t.Run(`histogram_quantile(single-value-valid-le-max-phi)`, func(t *testing.T) { + t.Parallel() + q := `histogram_quantile(1, ( + label_set(100, "le", "200"), + label_set(0, "le", "55"), + ))` + r := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{200, 200, 200, 200, 200, 200}, + Timestamps: timestampsExpected, + } + resultExpected := []netstorage.Result{r} + f(q, resultExpected) + }) + t.Run(`histogram_quantile(single-value-valid-le-min-phi)`, func(t *testing.T) { + t.Parallel() + q := `histogram_quantile(0, ( + label_set(100, "le", "200"), + label_set(0, "le", "55"), + ))` + r := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{55, 55, 55, 55, 55, 55}, + Timestamps: timestampsExpected, + } + resultExpected := []netstorage.Result{r} + f(q, resultExpected) + }) + t.Run(`histogram_quantile(single-value-valid-le-min-phi-no-zero-bucket)`, func(t *testing.T) { + t.Parallel() + q := `histogram_quantile(0, label_set(100, "le", "200"))` + r := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{0, 0, 0, 0, 0, 0}, + Timestamps: timestampsExpected, + } + resultExpected := []netstorage.Result{r} + f(q, resultExpected) + }) t.Run(`histogram_quantile(scalar-phi)`, func(t *testing.T) { t.Parallel() q := `histogram_quantile(time() / 2 / 1e3, label_set(100, "le", "200"))` diff --git a/app/vmselect/promql/transform.go b/app/vmselect/promql/transform.go index 445462846f..03a55a9e4a 100644 --- a/app/vmselect/promql/transform.go +++ b/app/vmselect/promql/transform.go @@ -447,7 +447,15 @@ func transformHistogramQuantile(tfa *transformFuncArg) ([]*timeseries, error) { vPrev = v } } - if len(xss) == 0 { + vLast := nan + for len(xss) > 0 { + vLast = xss[len(xss)-1].ts.Values[i] + if !math.IsNaN(vLast) { + break + } + xss = xss[:len(xss)-1] + } + if vLast == 0 || math.IsNaN(vLast) { return nan } if phi < 0 { @@ -456,10 +464,6 @@ func transformHistogramQuantile(tfa *transformFuncArg) ([]*timeseries, error) { if phi > 1 { return inf } - vLast := xss[len(xss)-1].ts.Values[i] - if vLast == 0 { - return nan - } vReq := vLast * phi vPrev = 0 lePrev := float64(0) @@ -471,6 +475,11 @@ func transformHistogramQuantile(tfa *transformFuncArg) ([]*timeseries, error) { continue } le := xs.le + if v <= 0 { + // Skip zero buckets. + lePrev = le + continue + } if v < vReq { vPrev = v lePrev = le