vitess-gh/go/vt/tabletserver/tablet_error.go

188 строки
4.4 KiB
Go
Исходник Обычный вид История

// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2012-02-25 11:30:03 +04:00
package tabletserver
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
log "github.com/golang/glog"
"github.com/youtube/vitess/go/mysql"
"github.com/youtube/vitess/go/tb"
"github.com/youtube/vitess/go/vt/logutil"
2012-02-25 11:30:03 +04:00
)
const (
2015-01-06 20:37:34 +03:00
// ErrFail is returned when a query fails
ErrFail = iota
// ErrRetry is returned when a query can be retried
ErrRetry
// ErrFatal is returned when a query cannot be retried
ErrFatal
// ErrTxPoolFull is returned when we can't get a connection
ErrTxPoolFull
// ErrNotInTx is returned when we're not in a transaction but should be
ErrNotInTx
2012-02-25 11:30:03 +04:00
)
var logTxPoolFull = logutil.NewThrottledLogger("TxPoolFull", 1*time.Minute)
2015-01-06 20:37:34 +03:00
// TabletError is the erro type we use in this library
2012-02-25 11:30:03 +04:00
type TabletError struct {
ErrorType int
Message string
SqlError int
}
// This is how go-mysql exports its error number
type hasNumber interface {
Number() int
}
2015-01-06 20:37:34 +03:00
// NewTabletError returns a TabletError of the given type
2012-02-25 11:30:03 +04:00
func NewTabletError(errorType int, format string, args ...interface{}) *TabletError {
return &TabletError{
ErrorType: errorType,
Message: fmt.Sprintf(format, args...),
}
2012-02-25 11:30:03 +04:00
}
2015-01-06 20:37:34 +03:00
// NewTabletErrorSql returns a TabletError based on the error
2012-02-25 11:30:03 +04:00
func NewTabletErrorSql(errorType int, err error) *TabletError {
var errnum int
errstr := err.Error()
2012-02-25 11:30:03 +04:00
if sqlErr, ok := err.(hasNumber); ok {
errnum = sqlErr.Number()
// Override error type if MySQL is in read-only mode. It's probably because
// there was a remaster and there are old clients still connected.
2015-01-06 20:37:34 +03:00
if errnum == mysql.ErrOptionPreventsStatement && strings.Contains(errstr, "read-only") {
errorType = ErrRetry
}
}
return &TabletError{
ErrorType: errorType,
Message: errstr,
SqlError: errnum,
2012-02-25 11:30:03 +04:00
}
}
var errExtract = regexp.MustCompile(`.*\(errno ([0-9]*)\).*`)
// IsConnErr returns true if the error is a connection error. If
// the error is of type TabletError or hasNumber, it checks the error
// code. Otherwise, it parses the string looking for (errno xxxx)
// and uses the extracted value to determine if it's a conn error.
func IsConnErr(err error) bool {
var sqlError int
switch err := err.(type) {
case *TabletError:
sqlError = err.SqlError
case hasNumber:
sqlError = err.Number()
default:
match := errExtract.FindStringSubmatch(err.Error())
if len(match) < 2 {
return false
}
var convErr error
sqlError, convErr = strconv.Atoi(match[1])
if convErr != nil {
return false
}
}
// 2013 means that someone sniped the query.
if sqlError == 2013 {
return false
}
return sqlError >= 2000 && sqlError <= 2018
}
2013-04-11 02:43:10 +04:00
func (te *TabletError) Error() string {
2012-02-25 11:30:03 +04:00
format := "error: %s"
2013-04-11 02:43:10 +04:00
switch te.ErrorType {
2015-01-06 20:37:34 +03:00
case ErrRetry:
2012-02-25 11:30:03 +04:00
format = "retry: %s"
2015-01-06 20:37:34 +03:00
case ErrFatal:
2012-02-25 11:30:03 +04:00
format = "fatal: %s"
2015-01-06 20:37:34 +03:00
case ErrTxPoolFull:
2013-08-21 01:00:00 +04:00
format = "tx_pool_full: %s"
2015-01-06 20:37:34 +03:00
case ErrNotInTx:
2013-09-21 09:21:30 +04:00
format = "not_in_tx: %s"
2012-02-25 11:30:03 +04:00
}
2013-04-11 02:43:10 +04:00
return fmt.Sprintf(format, te.Message)
2012-02-25 11:30:03 +04:00
}
2015-01-06 20:37:34 +03:00
// RecordStats will record the error in the proper stat bucket
2013-04-11 02:43:10 +04:00
func (te *TabletError) RecordStats() {
switch te.ErrorType {
2015-01-06 20:37:34 +03:00
case ErrRetry:
infoErrors.Add("Retry", 1)
2015-01-06 20:37:34 +03:00
case ErrFatal:
infoErrors.Add("Fatal", 1)
2015-01-06 20:37:34 +03:00
case ErrTxPoolFull:
errorStats.Add("TxPoolFull", 1)
2015-01-06 20:37:34 +03:00
case ErrNotInTx:
2013-09-21 09:21:30 +04:00
errorStats.Add("NotInTx", 1)
2012-02-25 11:30:03 +04:00
default:
switch te.SqlError {
2015-01-06 20:37:34 +03:00
case mysql.ErrDupEntry:
infoErrors.Add("DupKey", 1)
2015-01-06 20:37:34 +03:00
case mysql.ErrLockWaitTimeout, mysql.ErrLockDeadlock:
errorStats.Add("Deadlock", 1)
default:
2012-02-25 11:30:03 +04:00
errorStats.Add("Fail", 1)
}
}
}
func handleError(err *error, logStats *SQLQueryStats) {
2012-02-25 11:30:03 +04:00
if x := recover(); x != nil {
terr, ok := x.(*TabletError)
if !ok {
log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
2015-01-06 20:37:34 +03:00
*err = NewTabletError(ErrFail, "%v: uncaught panic", x)
internalErrors.Add("Panic", 1)
return
}
2012-02-25 11:30:03 +04:00
*err = terr
terr.RecordStats()
2015-01-06 20:37:34 +03:00
if terr.ErrorType == ErrRetry { // Retry errors are too spammy
2012-02-25 11:30:03 +04:00
return
}
2015-01-06 20:37:34 +03:00
if terr.ErrorType == ErrTxPoolFull {
logTxPoolFull.Errorf("%v", terr)
} else {
log.Errorf("%v", terr)
}
2012-02-25 11:30:03 +04:00
}
if logStats != nil {
logStats.Error = *err
logStats.Send()
}
2012-02-25 11:30:03 +04:00
}
func logError() {
if x := recover(); x != nil {
2014-04-29 22:18:46 +04:00
terr, ok := x.(*TabletError)
if !ok {
log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
internalErrors.Add("Panic", 1)
return
}
2015-01-06 20:37:34 +03:00
if terr.ErrorType == ErrTxPoolFull {
logTxPoolFull.Errorf("%v", terr)
} else {
log.Errorf("%v", terr)
}
2012-02-25 11:30:03 +04:00
}
}