172 lines
4.9 KiB
Go
172 lines
4.9 KiB
Go
// Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
|
|
|
|
package export
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"github.com/pingcap/errors"
|
|
|
|
"github.com/pingcap/tidb/br/pkg/utils"
|
|
"github.com/pingcap/tidb/br/pkg/version"
|
|
tcontext "github.com/pingcap/tidb/dumpling/context"
|
|
)
|
|
|
|
const (
|
|
consistencyTypeAuto = "auto"
|
|
consistencyTypeFlush = "flush"
|
|
consistencyTypeLock = "lock"
|
|
consistencyTypeSnapshot = "snapshot"
|
|
consistencyTypeNone = "none"
|
|
)
|
|
|
|
var tiDBDisableTableLockErr = errors.New("try to apply lock consistency on TiDB but it doesn't enable table lock. please set enable-table-lock=true in tidb server config")
|
|
|
|
// NewConsistencyController returns a new consistency controller
|
|
func NewConsistencyController(ctx context.Context, conf *Config, session *sql.DB) (ConsistencyController, error) {
|
|
conn, err := session.Conn(ctx)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
switch conf.Consistency {
|
|
case consistencyTypeFlush:
|
|
return &ConsistencyFlushTableWithReadLock{
|
|
serverType: conf.ServerInfo.ServerType,
|
|
conn: conn,
|
|
}, nil
|
|
case consistencyTypeLock:
|
|
return &ConsistencyLockDumpingTables{
|
|
conn: conn,
|
|
conf: conf,
|
|
}, nil
|
|
case consistencyTypeSnapshot:
|
|
if conf.ServerInfo.ServerType != version.ServerTypeTiDB {
|
|
return nil, errors.New("snapshot consistency is not supported for this server")
|
|
}
|
|
return &ConsistencyNone{}, nil
|
|
case consistencyTypeNone:
|
|
return &ConsistencyNone{}, nil
|
|
default:
|
|
return nil, errors.Errorf("invalid consistency option %s", conf.Consistency)
|
|
}
|
|
}
|
|
|
|
// ConsistencyController is the interface that controls the consistency of exporting progress
|
|
type ConsistencyController interface {
|
|
Setup(*tcontext.Context) error
|
|
TearDown(context.Context) error
|
|
PingContext(context.Context) error
|
|
}
|
|
|
|
// ConsistencyNone dumps without adding locks, which cannot guarantee consistency
|
|
type ConsistencyNone struct{}
|
|
|
|
// Setup implements ConsistencyController.Setup
|
|
func (c *ConsistencyNone) Setup(_ *tcontext.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// TearDown implements ConsistencyController.TearDown
|
|
func (c *ConsistencyNone) TearDown(_ context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// PingContext implements ConsistencyController.PingContext
|
|
func (c *ConsistencyNone) PingContext(_ context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
// ConsistencyFlushTableWithReadLock uses FlushTableWithReadLock before the dump
|
|
type ConsistencyFlushTableWithReadLock struct {
|
|
serverType version.ServerType
|
|
conn *sql.Conn
|
|
}
|
|
|
|
// Setup implements ConsistencyController.Setup
|
|
func (c *ConsistencyFlushTableWithReadLock) Setup(tctx *tcontext.Context) error {
|
|
if c.serverType == version.ServerTypeTiDB {
|
|
return errors.New("'flush table with read lock' cannot be used to ensure the consistency in TiDB")
|
|
}
|
|
return FlushTableWithReadLock(tctx, c.conn)
|
|
}
|
|
|
|
// TearDown implements ConsistencyController.TearDown
|
|
func (c *ConsistencyFlushTableWithReadLock) TearDown(ctx context.Context) error {
|
|
if c.conn == nil {
|
|
return nil
|
|
}
|
|
defer func() {
|
|
c.conn.Close()
|
|
c.conn = nil
|
|
}()
|
|
return UnlockTables(ctx, c.conn)
|
|
}
|
|
|
|
// PingContext implements ConsistencyController.PingContext
|
|
func (c *ConsistencyFlushTableWithReadLock) PingContext(ctx context.Context) error {
|
|
if c.conn == nil {
|
|
return errors.New("consistency connection has already been closed")
|
|
}
|
|
return c.conn.PingContext(ctx)
|
|
}
|
|
|
|
// ConsistencyLockDumpingTables execute lock tables read on all tables before dump
|
|
type ConsistencyLockDumpingTables struct {
|
|
conn *sql.Conn
|
|
conf *Config
|
|
}
|
|
|
|
// Setup implements ConsistencyController.Setup
|
|
func (c *ConsistencyLockDumpingTables) Setup(tctx *tcontext.Context) error {
|
|
if c.conf.ServerInfo.ServerType == version.ServerTypeTiDB {
|
|
if enableTableLock, err := CheckTiDBEnableTableLock(c.conn); err != nil || !enableTableLock {
|
|
if err != nil {
|
|
return err
|
|
} else {
|
|
return tiDBDisableTableLockErr
|
|
}
|
|
}
|
|
}
|
|
blockList := make(map[string]map[string]interface{})
|
|
return utils.WithRetry(tctx, func() error {
|
|
lockTablesSQL := buildLockTablesSQL(c.conf.Tables, blockList)
|
|
_, err := c.conn.ExecContext(tctx, lockTablesSQL)
|
|
if err == nil {
|
|
if len(blockList) > 0 {
|
|
filterTablesFunc(tctx, c.conf, func(db string, tbl string) bool {
|
|
if blockTable, ok := blockList[db]; ok {
|
|
if _, ok := blockTable[tbl]; ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
return errors.Trace(err)
|
|
}, newLockTablesBackoffer(tctx, blockList))
|
|
}
|
|
|
|
// TearDown implements ConsistencyController.TearDown
|
|
func (c *ConsistencyLockDumpingTables) TearDown(ctx context.Context) error {
|
|
if c.conn == nil {
|
|
return nil
|
|
}
|
|
defer func() {
|
|
c.conn.Close()
|
|
c.conn = nil
|
|
}()
|
|
return UnlockTables(ctx, c.conn)
|
|
}
|
|
|
|
// PingContext implements ConsistencyController.PingContext
|
|
func (c *ConsistencyLockDumpingTables) PingContext(ctx context.Context) error {
|
|
if c.conn == nil {
|
|
return errors.New("consistency connection has already been closed")
|
|
}
|
|
return c.conn.PingContext(ctx)
|
|
}
|
|
|
|
const snapshotFieldIndex = 1
|