Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dumpling: fix consistency lock table when tables to dump is empty #38687

Merged
merged 4 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions dumpling/export/consistency.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ func NewConsistencyController(ctx context.Context, conf *Config, session *sql.DB
}, nil
case ConsistencyTypeLock:
return &ConsistencyLockDumpingTables{
conn: conn,
conf: conf,
conn: conn,
conf: conf,
emptyLockSQL: false,
}, nil
case ConsistencyTypeSnapshot:
if conf.ServerInfo.ServerType != version.ServerTypeTiDB {
Expand Down Expand Up @@ -118,8 +119,9 @@ func (c *ConsistencyFlushTableWithReadLock) PingContext(ctx context.Context) err

// ConsistencyLockDumpingTables execute lock tables read on all tables before dump
type ConsistencyLockDumpingTables struct {
conn *sql.Conn
conf *Config
conn *sql.Conn
conf *Config
emptyLockSQL bool
}

// Setup implements ConsistencyController.Setup
Expand All @@ -135,7 +137,15 @@ func (c *ConsistencyLockDumpingTables) Setup(tctx *tcontext.Context) error {
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)
var err error
if len(lockTablesSQL) == 0 {
c.emptyLockSQL = true
// transfer to ConsistencyNone
_ = c.conn.Close()
c.conn = nil
} else {
_, err = c.conn.ExecContext(tctx, lockTablesSQL)
}
if err == nil {
if len(blockList) > 0 {
filterTablesFunc(tctx, c.conf, func(db string, tbl string) bool {
Expand Down Expand Up @@ -166,6 +176,9 @@ func (c *ConsistencyLockDumpingTables) TearDown(ctx context.Context) error {

// PingContext implements ConsistencyController.PingContext
func (c *ConsistencyLockDumpingTables) PingContext(ctx context.Context) error {
if c.emptyLockSQL {
return nil
}
if c.conn == nil {
return errors.New("consistency connection has already been closed")
}
Expand Down
33 changes: 33 additions & 0 deletions dumpling/export/consistency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,39 @@ func TestConsistencyLockControllerRetry(t *testing.T) {
require.NoError(t, mock.ExpectationsWereMet())
}

func TestConsistencyLockControllerEmpty(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer func() {
_ = db.Close()
}()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tctx := tcontext.Background().WithContext(ctx)
conf := defaultConfigForTest(t)

conf.ServerInfo.ServerType = version.ServerTypeMySQL
conf.Consistency = ConsistencyTypeLock
conf.Tables = NewDatabaseTables().
AppendTables("db1", []string{"t1"}, []uint64{1}).
AppendViews("db2", "t4")
mock.ExpectExec("LOCK TABLES `db1`.`t1` READ").
WillReturnError(&mysql.MySQLError{Number: ErrNoSuchTable, Message: "Table 'db1.t1' doesn't exist"})
ctrl, _ := NewConsistencyController(ctx, conf, db)
_, ok := ctrl.(*ConsistencyLockDumpingTables)
require.True(t, ok)
require.NoError(t, ctrl.Setup(tctx))
require.NoError(t, ctrl.TearDown(tctx))

// should remove table db1.t1 in tables to dump
expectedDumpTables := NewDatabaseTables().
AppendViews("db2", "t4")
expectedDumpTables["db1"] = make([]*TableInfo, 0)
require.Equal(t, expectedDumpTables, conf.Tables)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we check ctrl.Setup for this no-table case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check includes no-table case. After db1.tb1 is removed there is no more table.

require.NoError(t, mock.ExpectationsWereMet())
}

func TestResolveAutoConsistency(t *testing.T) {
conf := defaultConfigForTest(t)
cases := []struct {
Expand Down
1 change: 1 addition & 0 deletions dumpling/tests/_utils/run_services
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ start_services() {

cat > "$DUMPLING_TEST_DIR/tidb.toml" <<EOF
port = 4000
enable-table-lock = true
[status]
status-port = 10080
[log.file]
Expand Down
11 changes: 11 additions & 0 deletions dumpling/tests/basic/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,14 @@ echo "expected panic 0, actual ${actual}"
actual=$(grep -w "Error 1064: You have an error in your SQL syntax" ${DUMPLING_OUTPUT_DIR}/dumpling.log|wc -l)
echo "expect contain Error 1064, actual ${actual}"
[ "$actual" -ge 1 ]

# Test for consistency lock with empty database.
export DUMPLING_TEST_PORT=3306
run_sql "drop database if exists \`$DB_NAME\`;"
run_sql "create database \`$DB_NAME\` DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;"

run_dumpling --consistency lock -B "$DB_NAME" -L ${DUMPLING_OUTPUT_DIR}/dumpling.log

cnt=$(grep -w "$DB_NAME" ${DUMPLING_OUTPUT_DIR}/${DB_NAME}-schema-create.sql|wc -l)
echo "records count is ${cnt}"
[ "$cnt" = 1 ]