vitess.io/vitess@v0.16.2/go/cmd/vtgateclienttest/services/errors.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package services 18 19 import ( 20 "strings" 21 22 "context" 23 24 "vitess.io/vitess/go/sqltypes" 25 "vitess.io/vitess/go/vt/vterrors" 26 "vitess.io/vitess/go/vt/vtgate/vtgateservice" 27 28 querypb "vitess.io/vitess/go/vt/proto/query" 29 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 30 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 31 ) 32 33 // errorClient implements vtgateservice.VTGateService 34 // and returns specific errors. It is meant to test all possible error cases, 35 // and make sure all clients handle the errors correctly. 36 37 const ( 38 // ErrorPrefix is the prefix to send with queries so they go through this service handler. 39 ErrorPrefix = "error://" 40 // PartialErrorPrefix is the prefix to send with queries so the RPC returns a partial error. 41 // A partial error is when we return an error as part of the RPC response instead of via 42 // the regular error channels. This occurs if an RPC partially succeeds, and therefore 43 // requires some kind of response, but still needs to return an error. 44 // VTGate Execute* calls do this: they always return a new session ID, but might also 45 // return an error in the response. 46 PartialErrorPrefix = "partialerror://" 47 ) 48 49 type errorClient struct { 50 fallbackClient 51 } 52 53 func newErrorClient(fallback vtgateservice.VTGateService) *errorClient { 54 return &errorClient{ 55 fallbackClient: newFallbackClient(fallback), 56 } 57 } 58 59 // requestToError returns an error for the given request, by looking at the 60 // request's prefix and requested error type. If the request doesn't match an 61 // error request, return nil. 62 func requestToError(request string) error { 63 if !strings.HasPrefix(request, ErrorPrefix) { 64 return nil 65 } 66 return trimmedRequestToError(strings.TrimPrefix(request, ErrorPrefix)) 67 } 68 69 // requestToPartialError fills reply for a partial error if requested 70 // (that is, an error that may change the session). 71 // It returns true if a partial error was requested, false otherwise. 72 // This partial error should only be returned by Execute* calls. 73 func requestToPartialError(request string, session *vtgatepb.Session) error { 74 if !strings.HasPrefix(request, PartialErrorPrefix) { 75 return nil 76 } 77 request = strings.TrimPrefix(request, PartialErrorPrefix) 78 parts := strings.Split(request, "/") 79 if len(parts) > 1 && parts[1] == "close transaction" { 80 session.InTransaction = false 81 } 82 return trimmedRequestToError(parts[0]) 83 } 84 85 // trimmedRequestToError returns an error for a trimmed request by looking at the 86 // requested error type. It assumes that prefix checking has already been done. 87 // If the received string doesn't match a known error, returns an unknown error. 88 func trimmedRequestToError(received string) error { 89 switch received { 90 case "bad input": 91 return vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "vtgate test client forced error: bad input") 92 case "deadline exceeded": 93 return vterrors.New(vtrpcpb.Code_DEADLINE_EXCEEDED, "vtgate test client forced error: deadline exceeded") 94 case "integrity error": 95 return vterrors.New(vtrpcpb.Code_ALREADY_EXISTS, "vtgate test client forced error: integrity error (errno 1062) (sqlstate 23000)") 96 // request backlog and general throttling type errors 97 case "transient error": 98 return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "request_backlog: too many requests in flight: vtgate test client forced error: transient error") 99 case "throttled error": 100 return vterrors.New(vtrpcpb.Code_UNAVAILABLE, "request_backlog: exceeded XXX quota, rate limiting: vtgate test client forced error: transient error") 101 case "unauthenticated": 102 return vterrors.New(vtrpcpb.Code_UNAUTHENTICATED, "vtgate test client forced error: unauthenticated") 103 case "aborted": 104 return vterrors.New(vtrpcpb.Code_ABORTED, "vtgate test client forced error: aborted") 105 case "query not served": 106 return vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "vtgate test client forced error: query not served") 107 case "unknown error": 108 return vterrors.New(vtrpcpb.Code_UNKNOWN, "vtgate test client forced error: unknown error") 109 default: 110 return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "vtgate test client error request unrecognized: %v", received) 111 } 112 } 113 114 func (c *errorClient) Execute(ctx context.Context, session *vtgatepb.Session, sql string, bindVariables map[string]*querypb.BindVariable) (*vtgatepb.Session, *sqltypes.Result, error) { 115 if err := requestToPartialError(sql, session); err != nil { 116 return session, nil, err 117 } 118 if err := requestToError(sql); err != nil { 119 return session, nil, err 120 } 121 return c.fallbackClient.Execute(ctx, session, sql, bindVariables) 122 } 123 124 func (c *errorClient) ExecuteBatch(ctx context.Context, session *vtgatepb.Session, sqlList []string, bindVariablesList []map[string]*querypb.BindVariable) (*vtgatepb.Session, []sqltypes.QueryResponse, error) { 125 if len(sqlList) == 1 { 126 if err := requestToPartialError(sqlList[0], session); err != nil { 127 return session, nil, err 128 } 129 if err := requestToError(sqlList[0]); err != nil { 130 return session, nil, err 131 } 132 } 133 return c.fallbackClient.ExecuteBatch(ctx, session, sqlList, bindVariablesList) 134 } 135 136 func (c *errorClient) StreamExecute(ctx context.Context, session *vtgatepb.Session, sql string, bindVariables map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error) error { 137 if err := requestToError(sql); err != nil { 138 return err 139 } 140 return c.fallbackClient.StreamExecute(ctx, session, sql, bindVariables, callback) 141 } 142 143 func (c *errorClient) Prepare(ctx context.Context, session *vtgatepb.Session, sql string, bindVariables map[string]*querypb.BindVariable) (*vtgatepb.Session, []*querypb.Field, error) { 144 if err := requestToPartialError(sql, session); err != nil { 145 return session, nil, err 146 } 147 if err := requestToError(sql); err != nil { 148 return session, nil, err 149 } 150 return c.fallbackClient.Prepare(ctx, session, sql, bindVariables) 151 } 152 153 func (c *errorClient) CloseSession(ctx context.Context, session *vtgatepb.Session) error { 154 return c.fallbackClient.CloseSession(ctx, session) 155 }