github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/client/errors.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package client
    22  
    23  import (
    24  	"fmt"
    25  	"sync"
    26  
    27  	"github.com/m3db/m3/src/dbnode/generated/thrift/rpc"
    28  	tterrors "github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/errors"
    29  	xerrors "github.com/m3db/m3/src/x/errors"
    30  
    31  	"github.com/uber/tchannel-go"
    32  )
    33  
    34  // IsInternalServerError determines if the error is an internal server error.
    35  func IsInternalServerError(err error) bool {
    36  	for err != nil {
    37  		if e, ok := err.(*rpc.Error); ok && tterrors.IsInternalError(e) {
    38  			return true
    39  		}
    40  		err = xerrors.InnerError(err)
    41  	}
    42  	return false
    43  }
    44  
    45  // IsBadRequestError determines if the error is a bad request error.
    46  func IsBadRequestError(err error) bool {
    47  	for err != nil {
    48  		if e, ok := err.(*rpc.Error); ok && tterrors.IsBadRequestError(e) {
    49  			return true
    50  		}
    51  		if e := xerrors.GetInnerInvalidParamsError(err); e != nil {
    52  			return true
    53  		}
    54  		err = xerrors.InnerError(err)
    55  	}
    56  	return false
    57  }
    58  
    59  // IsResourceExhaustedError determines if the error is a resource exhausted error.
    60  func IsResourceExhaustedError(err error) bool {
    61  	for err != nil {
    62  		if e, ok := err.(*rpc.Error); ok && tterrors.IsResourceExhaustedErrorFlag(e) { //nolint:errorlint
    63  			return true
    64  		}
    65  		if e := xerrors.GetInnerResourceExhaustedError(err); e != nil {
    66  			return true
    67  		}
    68  		err = xerrors.InnerError(err)
    69  	}
    70  	return false
    71  }
    72  
    73  // IsTimeoutError determines if the error is a timeout.
    74  func IsTimeoutError(err error) bool {
    75  	for err != nil {
    76  		// nolint:errorlint
    77  		if e, ok := err.(*rpc.Error); ok {
    78  			if tterrors.IsTimeoutError(e) {
    79  				return true
    80  			}
    81  		}
    82  		// Need to also check if the message is directly the tchannel ErrTimeout error.
    83  		// This is because those errors can come through at the tchannel layer,
    84  		// rather than in our application layer, meaning we don't have any
    85  		// means to intercept / set the SERVER_TIMEOUT flag.
    86  		if err.Error() == tchannel.ErrTimeout.Error() {
    87  			return true
    88  		}
    89  		err = xerrors.InnerError(err)
    90  	}
    91  	return false
    92  }
    93  
    94  // IsConsistencyResultError determines if the error is a consistency result error.
    95  func IsConsistencyResultError(err error) bool {
    96  	for err != nil {
    97  		if _, ok := err.(consistencyResultErr); ok { //nolint:errorlint
    98  			return true
    99  		}
   100  		err = xerrors.InnerError(err)
   101  	}
   102  	return false
   103  }
   104  
   105  // NumResponded returns how many nodes responded for a given error
   106  func NumResponded(err error) int {
   107  	for err != nil {
   108  		if e, ok := err.(consistencyResultError); ok {
   109  			return e.numResponded()
   110  		}
   111  		err = xerrors.InnerError(err)
   112  	}
   113  	return 0
   114  }
   115  
   116  // NumSuccess returns how many nodes responded with success for a given error
   117  func NumSuccess(err error) int {
   118  	for err != nil {
   119  		if e, ok := err.(consistencyResultError); ok {
   120  			return e.numSuccess()
   121  		}
   122  		err = xerrors.InnerError(err)
   123  	}
   124  	return 0
   125  }
   126  
   127  // NumError returns how many nodes responded with error for a given error
   128  func NumError(err error) int {
   129  	for err != nil {
   130  		if e, ok := err.(consistencyResultError); ok {
   131  			return e.numResponded() -
   132  				e.numSuccess()
   133  		}
   134  		err = xerrors.InnerError(err)
   135  	}
   136  	return 0
   137  }
   138  
   139  type hostNotAvailableError struct {
   140  	err error
   141  }
   142  
   143  func (h hostNotAvailableError) Error() string {
   144  	return h.err.Error()
   145  }
   146  
   147  func newHostNotAvailableError(err error) error {
   148  	return xerrors.NewNonRetryableError(hostNotAvailableError{err: err})
   149  }
   150  
   151  func isHostNotAvailableError(err error) bool {
   152  	inner := xerrors.GetInnerNonRetryableError(err)
   153  	if inner == nil {
   154  		return false
   155  	}
   156  	_, ok := inner.(hostNotAvailableError)
   157  	return ok
   158  }
   159  
   160  type consistencyResultError interface {
   161  	error
   162  	xerrors.ContainedError
   163  
   164  	numResponded() int
   165  	numSuccess() int
   166  }
   167  
   168  type consistencyResultErr struct {
   169  	level       fmt.Stringer
   170  	success     int
   171  	enqueued    int
   172  	responded   int
   173  	topLevelErr error
   174  	errs        []error
   175  }
   176  
   177  func newConsistencyResultError(
   178  	level fmt.Stringer,
   179  	enqueued, responded int,
   180  	errs []error,
   181  ) consistencyResultError {
   182  	// NB(r): if any errors are bad request errors, encapsulate that error
   183  	// to ensure the error itself is wholly classified as a bad request error
   184  	var topLevelErr error
   185  	for i := 0; i < len(errs); i++ {
   186  		if topLevelErr == nil {
   187  			topLevelErr = errs[i]
   188  			continue
   189  		}
   190  		if IsBadRequestError(errs[i]) {
   191  			topLevelErr = errs[i]
   192  			break
   193  		}
   194  		if IsTimeoutError(errs[i]) {
   195  			topLevelErr = errs[i]
   196  			// Still continue iterating since bad request errors take precedence.
   197  			continue
   198  		}
   199  	}
   200  	return consistencyResultErr{
   201  		level:       level,
   202  		success:     enqueued - len(errs),
   203  		enqueued:    enqueued,
   204  		responded:   responded,
   205  		topLevelErr: topLevelErr,
   206  		errs:        append([]error(nil), errs...),
   207  	}
   208  }
   209  
   210  func (e consistencyResultErr) InnerError() error {
   211  	return e.topLevelErr
   212  }
   213  
   214  func (e consistencyResultErr) Error() string {
   215  	return fmt.Sprintf(
   216  		"failed to meet consistency level %s with %d/%d success, "+
   217  			"%d nodes responded, errors: %v",
   218  		e.level.String(), e.success, e.enqueued, e.responded, e.errs)
   219  }
   220  
   221  func (e consistencyResultErr) numResponded() int {
   222  	return e.responded
   223  }
   224  
   225  func (e consistencyResultErr) numSuccess() int {
   226  	return e.success
   227  }
   228  
   229  type syncAbortableErrorsMap struct {
   230  	sync.RWMutex
   231  	errors     map[int]error
   232  	abortError error
   233  }
   234  
   235  func newSyncAbortableErrorsMap() *syncAbortableErrorsMap {
   236  	return &syncAbortableErrorsMap{
   237  		errors: make(map[int]error),
   238  	}
   239  }
   240  
   241  func (e *syncAbortableErrorsMap) setError(idx int, err error) {
   242  	e.Lock()
   243  	e.errors[idx] = err
   244  	e.Unlock()
   245  }
   246  
   247  func (e *syncAbortableErrorsMap) getErrors() []error {
   248  	var result []error
   249  	e.RLock()
   250  	for _, err := range e.errors {
   251  		if err == nil {
   252  			continue
   253  		}
   254  		result = append(result, err)
   255  	}
   256  	e.RUnlock()
   257  	return result
   258  }
   259  
   260  func (e *syncAbortableErrorsMap) setAbortError(err error) {
   261  	e.Lock()
   262  	e.abortError = err
   263  	e.Unlock()
   264  }
   265  func (e *syncAbortableErrorsMap) getAbortError() error {
   266  	e.RLock()
   267  	result := e.abortError
   268  	e.RUnlock()
   269  	return result
   270  }