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  }