tidb retry 源码

  • 2022-09-19
  • 浏览 (607)

tidb retry 代码

文件路径:/dumpling/export/retry.go

// Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.

package export

import (
	"strings"
	"time"

	"github.com/go-sql-driver/mysql"
	"github.com/pingcap/errors"
	"github.com/pingcap/tidb/br/pkg/utils"
	tcontext "github.com/pingcap/tidb/dumpling/context"
	"github.com/pingcap/tidb/util/dbutil"
	"go.uber.org/zap"
)

const (
	dumpChunkRetryTime       = 3
	lockTablesRetryTime      = 5
	dumpChunkWaitInterval    = 50 * time.Millisecond
	dumpChunkMaxWaitInterval = 200 * time.Millisecond
	// ErrNoSuchTable is the error code no such table in MySQL/TiDB
	ErrNoSuchTable uint16 = 1146
)

type backOfferResettable interface {
	utils.Backoffer
	Reset()
}

func newRebuildConnBackOffer(shouldRetry bool) backOfferResettable { // revive:disable-line:flag-parameter
	if !shouldRetry {
		return &noopBackoffer{
			attempt: 1,
		}
	}
	return &dumpChunkBackoffer{
		attempt:      dumpChunkRetryTime,
		delayTime:    dumpChunkWaitInterval,
		maxDelayTime: dumpChunkMaxWaitInterval,
	}
}

type dumpChunkBackoffer struct {
	attempt      int
	delayTime    time.Duration
	maxDelayTime time.Duration
}

func (b *dumpChunkBackoffer) NextBackoff(err error) time.Duration {
	err = errors.Cause(err)
	if _, ok := err.(*mysql.MySQLError); ok && !dbutil.IsRetryableError(err) {
		b.attempt = 0
		return 0
	}
	b.delayTime = 2 * b.delayTime
	b.attempt--
	if b.delayTime > b.maxDelayTime {
		return b.maxDelayTime
	}
	return b.delayTime
}

func (b *dumpChunkBackoffer) Attempt() int {
	return b.attempt
}

func (b *dumpChunkBackoffer) Reset() {
	b.attempt = dumpChunkRetryTime
	b.delayTime = dumpChunkWaitInterval
}

type noopBackoffer struct {
	attempt int
}

func (b *noopBackoffer) NextBackoff(_ error) time.Duration {
	b.attempt--
	return time.Duration(0)
}

func (b *noopBackoffer) Attempt() int {
	return b.attempt
}

func (b *noopBackoffer) Reset() {
	b.attempt = 1
}

func newLockTablesBackoffer(tctx *tcontext.Context, blockList map[string]map[string]interface{}, conf *Config) *lockTablesBackoffer {
	if conf.specifiedTables {
		return &lockTablesBackoffer{
			tctx:      tctx,
			attempt:   1,
			blockList: blockList,
		}
	}
	return &lockTablesBackoffer{
		tctx:      tctx,
		attempt:   lockTablesRetryTime,
		blockList: blockList,
	}
}

type lockTablesBackoffer struct {
	tctx      *tcontext.Context
	attempt   int
	blockList map[string]map[string]interface{}
}

func (b *lockTablesBackoffer) NextBackoff(err error) time.Duration {
	err = errors.Cause(err)
	if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == ErrNoSuchTable {
		b.attempt--
		db, table, err := getTableFromMySQLError(mysqlErr.Message)
		if err != nil {
			b.tctx.L().Error("fail to retry lock tables", zap.Error(err))
			b.attempt = 0
			return 0
		}
		if _, ok := b.blockList[db]; !ok {
			b.blockList[db] = make(map[string]interface{})
		}
		b.blockList[db][table] = struct{}{}
		return 0
	}
	b.attempt = 0
	return 0
}

func (b *lockTablesBackoffer) Attempt() int {
	return b.attempt
}

func getTableFromMySQLError(msg string) (db, table string, err error) {
	// examples of the error msg:
	// Error 1146: Table 'pingcap.t1' doesn't exist
	// Error 1146: Table 'quo`te/database.quo.te/database-1.quo.te/table-1' doesn't exist /* doesn't support */
	msg = strings.TrimPrefix(msg, "Table '")
	msg = strings.TrimSuffix(msg, "' doesn't exist")
	failPart := strings.Split(msg, ".")
	if len(failPart) != 2 {
		err = errors.Errorf("doesn't support retry lock table %s", msg)
		return
	}
	return failPart[0], failPart[1], nil
}

相关信息

tidb 源码目录

相关文章

tidb block_allow_list 源码

tidb config 源码

tidb conn 源码

tidb consistency 源码

tidb dump 源码

tidb http_handler 源码

tidb ir 源码

tidb ir_impl 源码

tidb metadata 源码

tidb metrics 源码

0  赞