diff --git a/go/vt/discovery/healthcheck.go b/go/vt/discovery/healthcheck.go index 2f270bd7518..404eb5f86df 100644 --- a/go/vt/discovery/healthcheck.go +++ b/go/vt/discovery/healthcheck.go @@ -872,7 +872,7 @@ func (hc *HealthCheckImpl) TabletConnection(ctx context.Context, alias *topodata hc.mu.Unlock() if thc == nil || thc.Conn == nil { // TODO: test that throws this error - return nil, vterrors.Errorf(vtrpc.Code_NOT_FOUND, "tablet: %v is either down or nonexistent", alias) + return nil, vterrors.VT15001(vtrpc.Code_NOT_FOUND, fmt.Sprintf("tablet: %v is either down or nonexistent", alias)) } return thc.Connection(ctx), nil } diff --git a/go/vt/vterrors/code.go b/go/vt/vterrors/code.go index df19fcbd408..232188a29f8 100644 --- a/go/vt/vterrors/code.go +++ b/go/vt/vterrors/code.go @@ -120,6 +120,8 @@ var ( VT14004 = errorWithoutState("VT14004", vtrpcpb.Code_UNAVAILABLE, "cannot find keyspace for: %s", "The specified keyspace could not be found.") VT14005 = errorWithoutState("VT14005", vtrpcpb.Code_UNAVAILABLE, "cannot lookup sidecar database for keyspace: %s", "Failed to read sidecar database identifier.") + VT15001 = errorWithNoCode("VT15001", "session invalidated: close/reopen connection if applicable: %s", "This error means that the opened transaction should be closed by the application and re-opened.") + // Errors is a list of errors that must match all the variables // defined above to enable auto-documentation of error codes. Errors = []func(args ...any) *VitessError{ @@ -207,6 +209,10 @@ var ( VT14004, VT14005, } + + ErrorsWithNoCode = []func(code vtrpcpb.Code, args ...any) *VitessError{ + VT15001, + } ) type VitessError struct { @@ -258,3 +264,18 @@ func errorWithState(id string, code vtrpcpb.Code, state State, short, long strin } } } + +func errorWithNoCode(id string, short, long string) func(code vtrpcpb.Code, args ...any) *VitessError { + return func(code vtrpcpb.Code, args ...any) *VitessError { + s := short + if len(args) != 0 { + s = fmt.Sprintf(s, args...) + } + + return &VitessError{ + Err: New(code, id+": "+s), + Description: long, + ID: id, + } + } +} diff --git a/go/vt/vterrors/vterrorsgen/main.go b/go/vt/vterrors/vterrorsgen/main.go index 2aafee509e6..cefb0ab6587 100644 --- a/go/vt/vterrors/vterrorsgen/main.go +++ b/go/vt/vterrors/vterrorsgen/main.go @@ -23,6 +23,7 @@ import ( "text/template" "vitess.io/vitess/go/mysql/sqlerror" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" ) @@ -31,10 +32,14 @@ const ( tmpl = ` | ID | Description | Error | MySQL Error Code | SQL State | | --- | --- | --- | --- | --- | -{{- range $err := . }} +{{- range $err := .Errors }} {{- $data := (call $err) }} | {{ $data.ID }} | {{ $data.Description }} | {{ FormatError $data.Err }} | {{ ConvertStateToMySQLErrorCode $data.State }} | {{ ConvertStateToMySQLState $data.State }} | {{- end }} +{{- range $err := .ErrorsWithNoCode }} +{{- $data := (call $err 0) }} +| {{ $data.ID }} | {{ $data.Description }} | {{ FormatError $data.Err }} | | {{ ConvertStateToMySQLState $data.State }} | +{{- end }} ` ) @@ -53,7 +58,16 @@ func main() { }) t = template.Must(t.Parse(tmpl)) - err := t.ExecuteTemplate(os.Stdout, "template", vterrors.Errors) + type data struct { + Errors []func(args ...any) *vterrors.VitessError + ErrorsWithNoCode []func(code vtrpcpb.Code, args ...any) *vterrors.VitessError + } + d := data{ + Errors: vterrors.Errors, + ErrorsWithNoCode: vterrors.ErrorsWithNoCode, + } + + err := t.ExecuteTemplate(os.Stdout, "template", d) if err != nil { log.Fatal(err) } diff --git a/go/vt/vttablet/grpctabletconn/conn.go b/go/vt/vttablet/grpctabletconn/conn.go index d2d5604d808..75d1198f615 100644 --- a/go/vt/vttablet/grpctabletconn/conn.go +++ b/go/vt/vttablet/grpctabletconn/conn.go @@ -19,11 +19,15 @@ package grpctabletconn import ( "context" "io" + "strings" "sync" "github.com/spf13/pflag" "google.golang.org/grpc" + "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" @@ -132,7 +136,14 @@ func (conn *gRPCQueryClient) Execute(ctx context.Context, target *querypb.Target } er, err := conn.c.Execute(ctx, req) if err != nil { - return nil, tabletconn.ErrorFromGRPC(err) + err = tabletconn.ErrorFromGRPC(err) + if err == nil { + return nil, nil + } + if vterrors.Code(err) == vtrpc.Code_UNAVAILABLE && strings.Contains(err.Error(), "connection refused") { + return nil, vterrors.VT15001(vtrpc.Code_UNAVAILABLE, err.Error()) + } + return nil, err } return sqltypes.Proto3ToResult(er.Result), nil } diff --git a/go/vt/vttablet/tabletserver/state_manager.go b/go/vt/vttablet/tabletserver/state_manager.go index 0ccd0e42735..8a1d7cb7889 100644 --- a/go/vt/vttablet/tabletserver/state_manager.go +++ b/go/vt/vttablet/tabletserver/state_manager.go @@ -436,7 +436,7 @@ func (sm *stateManager) verifyTargetLocked(ctx context.Context, target *querypb. return nil } } - return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s: %v, want: %v or %v", vterrors.WrongTablet, target.TabletType, sm.target.TabletType, sm.alsoAllow) + return vterrors.VT15001(vtrpcpb.Code_FAILED_PRECONDITION, fmt.Sprintf("%s: %v, want: %v or %v", vterrors.WrongTablet, target.TabletType, sm.target.TabletType, sm.alsoAllow)) } } else { if !tabletenv.IsLocalContext(ctx) {