Repository: VictoriaMetrics/VictoriaMetrics
Pull request #1085 (2021-02-23 15:02:42)

vmselect: fix vmselect cache partial result when timeout
when response from one vmstorage node is partially processed and then timeout when reading next block, data from the partially processed response should not be cached 


Patch:
diff --git a/app/vmselect/netstorage/netstorage.go b/app/vmselect/netstorage/netstorage.go
index 89af8a3c5..1014fc993 100644
--- a/app/vmselect/netstorage/netstorage.go
+++ b/app/vmselect/netstorage/netstorage.go
@@ -1704,16 +1704,14 @@ func (sn *storageNode) processSearchMetricNames(requestData []byte, deadline sea
 }
 
 func (sn *storageNode) processSearchQuery(requestData []byte, fetchData bool, processBlock func(mb *storage.MetricBlock) error, deadline searchutils.Deadline) error {
-	var blocksRead int
 	f := func(bc *handshake.BufferedConn) error {
-		n, err := sn.processSearchQueryOnConn(bc, requestData, fetchData, processBlock)
+		_, err := sn.processSearchQueryOnConn(bc, requestData, fetchData, processBlock)
 		if err != nil {
 			return err
 		}
-		blocksRead = n
 		return nil
 	}
-	if err := sn.execOnConn("search_v4", f, deadline); err != nil && blocksRead == 0 {
+	if err := sn.execOnConn("search_v4", f, deadline); err != nil {
 		// Try again before giving up if zero blocks read on the previous attempt.
 		if err = sn.execOnConn("search_v4", f, deadline); err != nil {
 			return err
diff --git a/app/vmselect/promql/transform.go b/app/vmselect/promql/transform.go
index 6d1b01cbd..20961e409 100644
--- a/app/vmselect/promql/transform.go
+++ b/app/vmselect/promql/transform.go
@@ -649,14 +649,9 @@ func transformHistogramQuantile(tfa *transformFuncArg) ([]*timeseries, error) {
 	m := groupLeTimeseries(tss)
 
 	// Calculate quantile for each group in m
-
 	lastNonInf := func(i int, xss []leTimeseries) float64 {
 		for len(xss) > 0 {
 			xsLast := xss[len(xss)-1]
-			v := xsLast.ts.Values[i]
-			if v == 0 {
-				return nan
-			}
 			if !math.IsInf(xsLast.le, 0) {
 				return xsLast.le
 			}
@@ -700,8 +695,7 @@ func transformHistogramQuantile(tfa *transformFuncArg) ([]*timeseries, error) {
 				continue
 			}
 			if math.IsInf(le, 0) {
-				vv := lastNonInf(i, xss)
-				return vv, vv, inf
+				break
 			}
 			if v == vPrev {
 				return lePrev, lePrev, v
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index a8ddf8c3e..663f6cbed 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -17,6 +17,7 @@
 
 * BUGFIX: vmagent: properly perform graceful shutdown on `SIGINT` and `SIGTERM` signals. The graceful shutdown has been broken in `v1.54.0`. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1065
 * BUGFIX: reduce the probability of `duplicate time series` errors when querying Kubernetes metrics.
+* BUGFIX: properly calculate `histogram_quantile()` over time series with only a single non-zero bucket with `{le="+Inf"}`. Previously `NaN` was returned, now the value for the last bucket before `{le="+Inf"}` is returned like Prometheus does.
 
 
 # [v1.54.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.54.1)


Test patch:
diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go
index c59343296..24800397e 100644
--- a/app/vmselect/promql/exec_test.go
+++ b/app/vmselect/promql/exec_test.go
@@ -2767,6 +2767,26 @@ func TestExecSuccess(t *testing.T) {
 		resultExpected := []netstorage.Result{}
 		f(q, resultExpected)
 	})
+	t.Run(`histogram_quantile(single-value-inf-le)`, func(t *testing.T) {
+		t.Parallel()
+		q := `histogram_quantile(0.6, label_set(100, "le", "+Inf"))`
+		resultExpected := []netstorage.Result{}
+		f(q, resultExpected)
+	})
+	t.Run(`histogram_quantile(single-value-inf-le)`, func(t *testing.T) {
+		t.Parallel()
+		q := `histogram_quantile(0.6, (
+			label_set(100, "le", "+Inf"),
+			label_set(0, "le", "42"),
+		))`
+		r := netstorage.Result{
+			MetricName: metricNameExpected,
+			Values:     []float64{42, 42, 42, 42, 42, 42},
+			Timestamps: timestampsExpected,
+		}
+		resultExpected := []netstorage.Result{r}
+		f(q, resultExpected)
+	})
 	t.Run(`histogram_quantile(single-value-valid-le)`, func(t *testing.T) {
 		t.Parallel()
 		q := `histogram_quantile(0.6, label_set(100, "le", "200"))`