Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions app/vtstorage/netselect/netselect.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,10 +807,14 @@ func getFirstError(errs []error, allowPartialResponse bool) error {

// allowPartialResponse == true. Return the error only if all the backends are unavailable
// or if some of the backends are improperly configured.
//
// Check all the errors, since nil may come from a backend with a canceled query,
// because another backend returned an error. See https://github.com/VictoriaMetrics/VictoriaLogs/issues/1935
hasSuccess := false
for _, err := range errs {
if err == nil {
// At least a single vtstorage returned full response.
return nil
hasSuccess = true
Comment thread
cuongleqq marked this conversation as resolved.
Resolved
continue
}
if !isUnavailableBackendError(err) {
// Return the first error, which isn't related to the backend unavailability, to the client,
Expand All @@ -821,6 +825,11 @@ func getFirstError(errs []error, allowPartialResponse bool) error {
}
}

if hasSuccess {
// At least a single vtstorage returned full response.
return nil
}

err := fmt.Errorf("all the vtstorage nodes are unavailable for querying; a sample error: %w", errs[0])
return newStatusBadGatewayError(err)
}
Expand Down
51 changes: 51 additions & 0 deletions app/vtstorage/netselect/netselect_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package netselect

import (
"errors"
"net/http"
"testing"

"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
)

func TestGetFirstError(t *testing.T) {
errUnavailable := &errUnavailableBackend{
err: errors.New("cannot connect to storage node"),
}
errResponse := errors.New("unexpected response status code")

f := func(errs []error, allowPartialResponse bool, errExpected error) {
t.Helper()

err := getFirstError(errs, allowPartialResponse)
if errExpected == nil {
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
return
}

if !errors.Is(err, errExpected) {
t.Fatalf("unexpected error\ngot\n%v\nwant the error wrapping\n%v", err, errExpected)
}
esc, ok := errors.AsType[*httpserver.ErrorWithStatusCode](err)
if !ok || esc.StatusCode != http.StatusBadGateway {
t.Fatalf("expecting %d status code for the error %v", http.StatusBadGateway, err)
}
}

// partial responses are disallowed
f([]error{nil, nil}, false, nil)
f([]error{nil, errUnavailable}, false, errUnavailable)

// partial responses are allowed
f([]error{nil, nil}, true, nil)
f([]error{nil, errUnavailable}, true, nil)
f([]error{errUnavailable, errUnavailable}, true, errUnavailable)
f([]error{errUnavailable, errResponse}, true, errResponse)
f([]error{errResponse, nil}, true, errResponse)

// handleError() returns nil for the query canceled because of an error at another backend.
// See https://github.com/VictoriaMetrics/VictoriaLogs/issues/1935
f([]error{nil, errResponse}, true, errResponse)
}
2 changes: 2 additions & 0 deletions docs/victoriatraces/changelog/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ The following `tip` changes can be tested by building VictoriaTraces components

## tip

* BUGFIX: [VictoriaTraces cluster](https://docs.victoriametrics.com/victoriatraces/cluster/): properly return `502 Bad Gateway` error when one of `vtstorage` nodes returns non-200 status code and partial responses are allowed via `-search.allowPartialResponse`. Previously `vtselect` could return an empty or truncated response with `200 OK` status code depending on the order of nodes in `-storageNode`. See [VictoriaLogs issue #1935](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1935).

## [v0.13.0](https://github.com/VictoriaMetrics/VictoriaTraces/releases/tag/v0.13.0)

Released at 2026-11-05
Expand Down
Loading