github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/optimism/keeper_test.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package optimism
    15  
    16  import (
    17  	"testing"
    18  
    19  	. "github.com/pingcap/check"
    20  	"github.com/pingcap/tidb/pkg/parser"
    21  	"github.com/pingcap/tidb/pkg/parser/model"
    22  	"github.com/pingcap/tidb/pkg/util/mock"
    23  	"github.com/pingcap/tiflow/dm/config/dbconfig"
    24  	"github.com/pingcap/tiflow/dm/pkg/conn"
    25  	"github.com/pingcap/tiflow/dm/pkg/terror"
    26  	"go.etcd.io/etcd/tests/v3/integration"
    27  )
    28  
    29  type testKeeper struct{}
    30  
    31  var _ = Suite(&testKeeper{})
    32  
    33  func TestKeeper(t *testing.T) {
    34  	integration.BeforeTestExternal(t)
    35  	mockCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
    36  	defer mockCluster.Terminate(t)
    37  
    38  	etcdTestCli = mockCluster.RandClient()
    39  
    40  	TestingT(t)
    41  }
    42  
    43  func (t *testKeeper) TestLockKeeper(c *C) {
    44  	var (
    45  		lk         = NewLockKeeper(getDownstreamMeta)
    46  		upSchema   = "foo_1"
    47  		upTable    = "bar_1"
    48  		downSchema = "foo"
    49  		downTable  = "bar"
    50  		DDLs       = []string{"ALTER TABLE bar ADD COLUMN c1 INT"}
    51  		task1      = "task1"
    52  		task2      = "task2"
    53  		source1    = "mysql-replica-1"
    54  		source2    = "mysql-replica-2"
    55  
    56  		p              = parser.New()
    57  		se             = mock.NewContext()
    58  		tblID    int64 = 111
    59  		tiBefore       = createTableInfo(c, p, se, tblID, `CREATE TABLE bar (id INT PRIMARY KEY)`)
    60  		tiAfter        = createTableInfo(c, p, se, tblID, `CREATE TABLE bar (id INT PRIMARY KEY, c1 INT)`)
    61  
    62  		i11 = NewInfo(task1, source1, upSchema, upTable, downSchema, downTable, DDLs, tiBefore, []*model.TableInfo{tiAfter})
    63  		i12 = NewInfo(task1, source2, upSchema, upTable, downSchema, downTable, DDLs, tiBefore, []*model.TableInfo{tiAfter})
    64  		i21 = NewInfo(task2, source1, upSchema, upTable, downSchema, downTable, DDLs, tiBefore, []*model.TableInfo{tiAfter})
    65  
    66  		tts1 = []TargetTable{
    67  			newTargetTable(task1, source1, downSchema, downTable, map[string]map[string]struct{}{upSchema: {upTable: struct{}{}}}),
    68  			newTargetTable(task1, source2, downSchema, downTable, map[string]map[string]struct{}{upSchema: {upTable: struct{}{}}}),
    69  		}
    70  		tts2 = []TargetTable{
    71  			newTargetTable(task2, source1, downSchema, downTable, map[string]map[string]struct{}{upSchema: {upTable: struct{}{}}}),
    72  		}
    73  	)
    74  
    75  	// lock with 2 sources.
    76  	lockID1, newDDLs, cols, err := lk.TrySync(etcdTestCli, i11, tts1)
    77  	c.Assert(err, IsNil)
    78  	c.Assert(lockID1, Equals, "task1-`foo`.`bar`")
    79  	c.Assert(newDDLs, DeepEquals, DDLs)
    80  	c.Assert(cols, DeepEquals, []string{})
    81  	lock1 := lk.FindLock(lockID1)
    82  	c.Assert(lock1, NotNil)
    83  	c.Assert(lock1.ID, Equals, lockID1)
    84  	c.Assert(lk.FindLockByInfo(i11).ID, Equals, lockID1)
    85  
    86  	lks := lk.FindLocksByTask("hahaha")
    87  	c.Assert(len(lks), Equals, 0)
    88  	lks = lk.FindLocksByTask(task1)
    89  	c.Assert(len(lks), Equals, 1)
    90  	c.Assert(lks[0].ID, Equals, lockID1)
    91  
    92  	synced, remain := lock1.IsSynced()
    93  	c.Assert(synced, IsFalse)
    94  	c.Assert(remain, Equals, 1)
    95  
    96  	lockID1, newDDLs, cols, err = lk.TrySync(etcdTestCli, i12, tts1)
    97  	c.Assert(err, IsNil)
    98  	c.Assert(lockID1, Equals, "task1-`foo`.`bar`")
    99  	c.Assert(newDDLs, DeepEquals, DDLs)
   100  	c.Assert(cols, DeepEquals, []string{})
   101  	lock1 = lk.FindLock(lockID1)
   102  	c.Assert(lock1, NotNil)
   103  	c.Assert(lock1.ID, Equals, lockID1)
   104  	synced, remain = lock1.IsSynced()
   105  	c.Assert(synced, IsTrue)
   106  	c.Assert(remain, Equals, 0)
   107  
   108  	// lock with only 1 source.
   109  	lockID2, newDDLs, cols, err := lk.TrySync(etcdTestCli, i21, tts2)
   110  	c.Assert(err, IsNil)
   111  	c.Assert(lockID2, Equals, "task2-`foo`.`bar`")
   112  	c.Assert(newDDLs, DeepEquals, DDLs)
   113  	c.Assert(cols, DeepEquals, []string{})
   114  	lock2 := lk.FindLock(lockID2)
   115  	c.Assert(lock2, NotNil)
   116  	c.Assert(lock2.ID, Equals, lockID2)
   117  	synced, remain = lock2.IsSynced()
   118  	c.Assert(synced, IsTrue)
   119  	c.Assert(remain, Equals, 0)
   120  
   121  	lks = lk.FindLocksByTask(task1)
   122  	c.Assert(len(lks), Equals, 1)
   123  	c.Assert(lks[0].ID, Equals, lockID1)
   124  	lks = lk.FindLocksByTask(task2)
   125  	c.Assert(len(lks), Equals, 1)
   126  	c.Assert(lks[0].ID, Equals, lockID2)
   127  
   128  	// try to find not-exists lock.
   129  	lockIDNotExists := "lock-not-exists"
   130  	c.Assert(lk.FindLock(lockIDNotExists), IsNil)
   131  
   132  	// all locks.
   133  	locks := lk.Locks()
   134  	c.Assert(locks, HasLen, 2)
   135  	c.Assert(locks[lockID1], Equals, lock1) // compare pointer
   136  	c.Assert(locks[lockID2], Equals, lock2)
   137  
   138  	// remove lock.
   139  	c.Assert(lk.RemoveLock(lockID1), IsTrue)
   140  	c.Assert(lk.RemoveLock(lockIDNotExists), IsFalse)
   141  	c.Assert(lk.Locks(), HasLen, 1)
   142  
   143  	// clear locks.
   144  	lk.Clear()
   145  
   146  	// no locks exist.
   147  	c.Assert(lk.Locks(), HasLen, 0)
   148  }
   149  
   150  func (t *testKeeper) TestLockKeeperMultipleTarget(c *C) {
   151  	var (
   152  		lk         = NewLockKeeper(getDownstreamMeta)
   153  		task       = "test-lock-keeper-multiple-target"
   154  		source     = "mysql-replica-1"
   155  		upSchema   = "foo"
   156  		upTables   = []string{"bar-1", "bar-2"}
   157  		downSchema = "foo"
   158  		downTable1 = "bar"
   159  		downTable2 = "rab"
   160  		DDLs       = []string{"ALTER TABLE bar ADD COLUMN c1 INT"}
   161  
   162  		p              = parser.New()
   163  		se             = mock.NewContext()
   164  		tblID    int64 = 111
   165  		tiBefore       = createTableInfo(c, p, se, tblID, `CREATE TABLE bar (id INT PRIMARY KEY)`)
   166  		tiAfter        = createTableInfo(c, p, se, tblID, `CREATE TABLE bar (id INT PRIMARY KEY, c1 INT)`)
   167  
   168  		i11 = NewInfo(task, source, upSchema, upTables[0], downSchema, downTable1, DDLs, tiBefore, []*model.TableInfo{tiAfter})
   169  		i12 = NewInfo(task, source, upSchema, upTables[1], downSchema, downTable1, DDLs, tiBefore, []*model.TableInfo{tiAfter})
   170  		i21 = NewInfo(task, source, upSchema, upTables[0], downSchema, downTable2, DDLs, tiBefore, []*model.TableInfo{tiAfter})
   171  		i22 = NewInfo(task, source, upSchema, upTables[1], downSchema, downTable2, DDLs, tiBefore, []*model.TableInfo{tiAfter})
   172  
   173  		tts1 = []TargetTable{
   174  			newTargetTable(task, source, downSchema, downTable1, map[string]map[string]struct{}{
   175  				upSchema: {upTables[0]: struct{}{}, upTables[1]: struct{}{}},
   176  			}),
   177  		}
   178  		tts2 = []TargetTable{
   179  			newTargetTable(task, source, downSchema, downTable2, map[string]map[string]struct{}{
   180  				upSchema: {upTables[0]: struct{}{}, upTables[1]: struct{}{}},
   181  			}),
   182  		}
   183  	)
   184  
   185  	// lock for target1.
   186  	lockID1, newDDLs, cols, err := lk.TrySync(etcdTestCli, i11, tts1)
   187  	c.Assert(err, IsNil)
   188  	c.Assert(lockID1, DeepEquals, "test-lock-keeper-multiple-target-`foo`.`bar`")
   189  	c.Assert(newDDLs, DeepEquals, DDLs)
   190  	c.Assert(cols, DeepEquals, []string{})
   191  
   192  	// lock for target2.
   193  	lockID2, newDDLs, cols, err := lk.TrySync(etcdTestCli, i21, tts2)
   194  	c.Assert(err, IsNil)
   195  	c.Assert(lockID2, DeepEquals, "test-lock-keeper-multiple-target-`foo`.`rab`")
   196  	c.Assert(newDDLs, DeepEquals, DDLs)
   197  	c.Assert(cols, DeepEquals, []string{})
   198  
   199  	// check two locks exist.
   200  	lock1 := lk.FindLock(lockID1)
   201  	c.Assert(lock1, NotNil)
   202  	c.Assert(lock1.ID, Equals, lockID1)
   203  	c.Assert(lk.FindLockByInfo(i11).ID, Equals, lockID1)
   204  	synced, remain := lock1.IsSynced()
   205  	c.Assert(synced, IsFalse)
   206  	c.Assert(remain, Equals, 1)
   207  	lock2 := lk.FindLock(lockID2)
   208  	c.Assert(lock2, NotNil)
   209  	c.Assert(lock2.ID, Equals, lockID2)
   210  	c.Assert(lk.FindLockByInfo(i21).ID, Equals, lockID2)
   211  	synced, remain = lock2.IsSynced()
   212  	c.Assert(synced, IsFalse)
   213  	c.Assert(remain, Equals, 1)
   214  
   215  	// sync for two locks.
   216  	lockID1, newDDLs, cols, err = lk.TrySync(etcdTestCli, i12, tts1)
   217  	c.Assert(err, IsNil)
   218  	c.Assert(lockID1, DeepEquals, "test-lock-keeper-multiple-target-`foo`.`bar`")
   219  	c.Assert(newDDLs, DeepEquals, DDLs)
   220  	c.Assert(cols, DeepEquals, []string{})
   221  	lockID2, newDDLs, cols, err = lk.TrySync(etcdTestCli, i22, tts2)
   222  	c.Assert(err, IsNil)
   223  	c.Assert(lockID2, DeepEquals, "test-lock-keeper-multiple-target-`foo`.`rab`")
   224  	c.Assert(newDDLs, DeepEquals, DDLs)
   225  	c.Assert(cols, DeepEquals, []string{})
   226  
   227  	lock1 = lk.FindLock(lockID1)
   228  	c.Assert(lock1, NotNil)
   229  	c.Assert(lock1.ID, Equals, lockID1)
   230  	synced, remain = lock1.IsSynced()
   231  	c.Assert(synced, IsTrue)
   232  	c.Assert(remain, Equals, 0)
   233  	lock2 = lk.FindLock(lockID2)
   234  	c.Assert(lock2, NotNil)
   235  	c.Assert(lock2.ID, Equals, lockID2)
   236  	synced, remain = lock2.IsSynced()
   237  	c.Assert(synced, IsTrue)
   238  	c.Assert(remain, Equals, 0)
   239  }
   240  
   241  func (t *testKeeper) TestTableKeeper(c *C) {
   242  	var (
   243  		tk         = NewTableKeeper()
   244  		task1      = "task-1"
   245  		task2      = "task-2"
   246  		source1    = "mysql-replica-1"
   247  		source2    = "mysql-replica-2"
   248  		downSchema = "db"
   249  		downTable  = "tbl"
   250  
   251  		tt11 = newTargetTable(task1, source1, downSchema, downTable, map[string]map[string]struct{}{
   252  			"db": {"tbl-1": struct{}{}, "tbl-2": struct{}{}},
   253  		})
   254  		tt12 = newTargetTable(task1, source2, downSchema, downTable, map[string]map[string]struct{}{
   255  			"db": {"tbl-1": struct{}{}, "tbl-2": struct{}{}},
   256  		})
   257  		tt21 = newTargetTable(task2, source2, downSchema, downTable, map[string]map[string]struct{}{
   258  			"db": {"tbl-3": struct{}{}},
   259  		})
   260  		tt22 = newTargetTable(task2, source2, downSchema, downTable, map[string]map[string]struct{}{
   261  			"db": {"tbl-3": struct{}{}, "tbl-4": struct{}{}},
   262  		})
   263  
   264  		st11 = NewSourceTables(task1, source1)
   265  		st12 = NewSourceTables(task1, source2)
   266  		st21 = NewSourceTables(task2, source2)
   267  		st22 = NewSourceTables(task2, source2)
   268  		stm  = map[string]map[string]SourceTables{
   269  			task1: {source2: st12, source1: st11},
   270  		}
   271  	)
   272  	for schema, tables := range tt11.UpTables {
   273  		for table := range tables {
   274  			st11.AddTable(schema, table, tt11.DownSchema, tt11.DownTable)
   275  		}
   276  	}
   277  	for schema, tables := range tt12.UpTables {
   278  		for table := range tables {
   279  			st12.AddTable(schema, table, tt12.DownSchema, tt12.DownTable)
   280  		}
   281  	}
   282  	for schema, tables := range tt21.UpTables {
   283  		for table := range tables {
   284  			st21.AddTable(schema, table, tt21.DownSchema, tt21.DownTable)
   285  		}
   286  	}
   287  	for schema, tables := range tt22.UpTables {
   288  		for table := range tables {
   289  			st22.AddTable(schema, table, tt22.DownSchema, tt22.DownTable)
   290  		}
   291  	}
   292  
   293  	// no tables exist before Init/Update.
   294  	c.Assert(tk.FindTables(task1, downSchema, downTable), IsNil)
   295  	for schema, tables := range tt11.UpTables {
   296  		for table := range tables {
   297  			c.Assert(tk.SourceTableExist(tt11.Task, tt11.Source, schema, table, downSchema, downTable), IsFalse)
   298  		}
   299  	}
   300  
   301  	// Init with `nil` is fine.
   302  	tk.Init(nil)
   303  	c.Assert(tk.FindTables(task1, downSchema, downTable), IsNil)
   304  
   305  	// tables for task1 exit after Init.
   306  	tk.Init(stm)
   307  	tts := tk.FindTables(task1, downSchema, downTable)
   308  	c.Assert(tts, HasLen, 2)
   309  	c.Assert(tts[0], DeepEquals, tt11)
   310  	c.Assert(tts[1], DeepEquals, tt12)
   311  	for schema, tables := range tt11.UpTables {
   312  		for table := range tables {
   313  			c.Assert(tk.SourceTableExist(tt11.Task, tt11.Source, schema, table, downSchema, downTable), IsTrue)
   314  		}
   315  	}
   316  
   317  	// adds new tables.
   318  	addTables, dropTables := tk.Update(st21)
   319  	c.Assert(addTables, HasLen, 1)
   320  	c.Assert(dropTables, HasLen, 0)
   321  	tts = tk.FindTables(task2, downSchema, downTable)
   322  	c.Assert(tts, HasLen, 1)
   323  	c.Assert(tts[0], DeepEquals, tt21)
   324  
   325  	// updates/appends new tables.
   326  	addTables, dropTables = tk.Update(st22)
   327  	c.Assert(addTables, HasLen, 1)
   328  	c.Assert(dropTables, HasLen, 0)
   329  	tts = tk.FindTables(task2, downSchema, downTable)
   330  	c.Assert(tts, HasLen, 1)
   331  	c.Assert(tts[0], DeepEquals, tt22)
   332  	for schema, tables := range tt22.UpTables {
   333  		for table := range tables {
   334  			c.Assert(tk.SourceTableExist(tt22.Task, tt22.Source, schema, table, downSchema, downTable), IsTrue)
   335  		}
   336  	}
   337  
   338  	// deletes tables.
   339  	st22.IsDeleted = true
   340  	addTables, dropTables = tk.Update(st22)
   341  	c.Assert(addTables, HasLen, 0)
   342  	c.Assert(dropTables, HasLen, 2)
   343  	c.Assert(tk.FindTables(task2, downSchema, downTable), IsNil)
   344  	for schema, tables := range tt22.UpTables {
   345  		for table := range tables {
   346  			c.Assert(tk.SourceTableExist(tt22.Task, tt22.Source, schema, table, downSchema, downTable), IsFalse)
   347  		}
   348  	}
   349  
   350  	// try to delete, but not exist.
   351  	addTables, dropTables = tk.Update(st22)
   352  	c.Assert(addTables, HasLen, 0)
   353  	c.Assert(dropTables, HasLen, 0)
   354  
   355  	st22.Task = "not-exist"
   356  	addTables, dropTables = tk.Update(st22)
   357  	c.Assert(addTables, HasLen, 0)
   358  	c.Assert(dropTables, HasLen, 0)
   359  
   360  	// tables for task1 not affected.
   361  	tts = tk.FindTables(task1, downSchema, downTable)
   362  	c.Assert(tts, HasLen, 2)
   363  	c.Assert(tts[0], DeepEquals, tt11)
   364  	c.Assert(tts[1], DeepEquals, tt12)
   365  	for schema, tables := range tt11.UpTables {
   366  		for table := range tables {
   367  			c.Assert(tk.SourceTableExist(tt11.Task, tt11.Source, schema, table, downSchema, downTable), IsTrue)
   368  		}
   369  	}
   370  
   371  	// add a table for st11.
   372  	c.Assert(tk.AddTable(task1, st11.Source, "db-2", "tbl-3", downSchema, downTable), IsTrue)
   373  	c.Assert(tk.AddTable(task1, st11.Source, "db-2", "tbl-3", downSchema, downTable), IsFalse)
   374  	tts = tk.FindTables(task1, downSchema, downTable)
   375  	st11n := tts[0]
   376  	c.Assert(st11n.UpTables, HasKey, "db-2")
   377  	c.Assert(st11n.UpTables["db-2"], HasKey, "tbl-3")
   378  
   379  	// removed the added table in st11.
   380  	c.Assert(tk.RemoveTable(task1, st11.Source, "db-2", "tbl-3", downSchema, downTable), IsTrue)
   381  	c.Assert(tk.RemoveTable(task1, st11.Source, "db-2", "tbl-3", downSchema, downTable), IsFalse)
   382  	tts = tk.FindTables(task1, downSchema, downTable)
   383  	st11n = tts[0]
   384  	c.Assert(st11n.UpTables["db-2"], IsNil)
   385  
   386  	// adds for not existing task takes no effect.
   387  	c.Assert(tk.AddTable("not-exist", st11.Source, "db-2", "tbl-3", downSchema, downTable), IsFalse)
   388  	// adds for not existing source takes effect.
   389  	c.Assert(tk.AddTable(task1, "new-source", "db-2", "tbl-3", downSchema, downTable), IsTrue)
   390  	tts = tk.FindTables(task1, downSchema, downTable)
   391  	c.Assert(tts, HasLen, 3)
   392  	c.Assert(tts[2].Source, Equals, "new-source")
   393  	c.Assert(tts[2].UpTables["db-2"], HasKey, "tbl-3")
   394  
   395  	// removes for not existing task/source takes no effect.
   396  	c.Assert(tk.RemoveTable("not-exit", st12.Source, "db", "tbl-1", downSchema, downTable), IsFalse)
   397  	c.Assert(tk.RemoveTable(task1, "not-exit", "db", "tbl-1", downSchema, downTable), IsFalse)
   398  	tts = tk.FindTables(task1, downSchema, downTable)
   399  	c.Assert(tts[1], DeepEquals, tt12)
   400  
   401  	c.Assert(tk.RemoveTableByTask("hahaha"), IsFalse)
   402  	tk.RemoveTableByTaskAndSources("hahaha", nil)
   403  	tts = tk.FindTables(task1, downSchema, downTable)
   404  	c.Assert(tts, HasLen, 3)
   405  	tk.RemoveTableByTaskAndSources(task1, []string{"hahaha"})
   406  	tts = tk.FindTables(task1, downSchema, downTable)
   407  	c.Assert(tts, HasLen, 3)
   408  	tk.RemoveTableByTaskAndSources(task1, []string{source1, source2})
   409  	tts = tk.FindTables(task1, downSchema, downTable)
   410  	c.Assert(tts, HasLen, 1)
   411  	c.Assert(tts[0].Source, Equals, "new-source")
   412  	c.Assert(tts[0].UpTables["db-2"], HasKey, "tbl-3")
   413  }
   414  
   415  func (t *testKeeper) TestTargetTablesForTask(c *C) {
   416  	var (
   417  		tk         = NewTableKeeper()
   418  		task1      = "task1"
   419  		task2      = "task2"
   420  		source1    = "mysql-replica-1"
   421  		source2    = "mysql-replica-2"
   422  		downSchema = "foo"
   423  		downTable1 = "bar"
   424  		downTable2 = "rab"
   425  		stm        = map[string]map[string]SourceTables{
   426  			task1: {source1: NewSourceTables(task1, source1), source2: NewSourceTables(task1, source2)},
   427  			task2: {source1: NewSourceTables(task2, source1), source2: NewSourceTables(task2, source2)},
   428  		}
   429  	)
   430  
   431  	// not exist task.
   432  	c.Assert(TargetTablesForTask("not-exist", downSchema, downTable1, stm), IsNil)
   433  
   434  	// no tables exist.
   435  	tts := TargetTablesForTask(task1, downSchema, downTable1, stm)
   436  	c.Assert(tts, DeepEquals, []TargetTable{})
   437  
   438  	// add some tables.
   439  	tt11 := stm[task1][source1]
   440  	tt11.AddTable("foo-1", "bar-1", downSchema, downTable1)
   441  	tt11.AddTable("foo-1", "bar-2", downSchema, downTable1)
   442  	tt12 := stm[task1][source2]
   443  	tt12.AddTable("foo-2", "bar-3", downSchema, downTable1)
   444  	tt21 := stm[task2][source1]
   445  	tt21.AddTable("foo-3", "bar-1", downSchema, downTable1)
   446  	tt22 := stm[task2][source2]
   447  	tt22.AddTable("foo-4", "bar-2", downSchema, downTable1)
   448  	tt22.AddTable("foo-4", "bar-3", downSchema, downTable1)
   449  
   450  	// get tables back.
   451  	tts = TargetTablesForTask(task1, downSchema, downTable1, stm)
   452  	c.Assert(tts, DeepEquals, []TargetTable{
   453  		tt11.TargetTable(downSchema, downTable1),
   454  		tt12.TargetTable(downSchema, downTable1),
   455  	})
   456  	tts = TargetTablesForTask(task2, downSchema, downTable1, stm)
   457  	c.Assert(tts, DeepEquals, []TargetTable{
   458  		tt21.TargetTable(downSchema, downTable1),
   459  		tt22.TargetTable(downSchema, downTable1),
   460  	})
   461  
   462  	tk.Init(stm)
   463  	tts = tk.FindTables(task1, downSchema, downTable1)
   464  	c.Assert(tts, DeepEquals, []TargetTable{
   465  		tt11.TargetTable(downSchema, downTable1),
   466  		tt12.TargetTable(downSchema, downTable1),
   467  	})
   468  
   469  	// add some tables for another target table.
   470  	c.Assert(tk.AddTable(task1, source1, "foo-1", "bar-3", downSchema, downTable2), IsTrue)
   471  	c.Assert(tk.AddTable(task1, source1, "foo-1", "bar-4", downSchema, downTable2), IsTrue)
   472  	tts = tk.FindTables(task1, downSchema, downTable2)
   473  	c.Assert(tts, DeepEquals, []TargetTable{
   474  		newTargetTable(task1, source1, downSchema, downTable2,
   475  			map[string]map[string]struct{}{
   476  				"foo-1": {"bar-3": struct{}{}, "bar-4": struct{}{}},
   477  			}),
   478  	})
   479  }
   480  
   481  func getDownstreamMeta(string) (*dbconfig.DBConfig, string) {
   482  	return nil, ""
   483  }
   484  
   485  func (t *testKeeper) TestGetDownstreamMeta(c *C) {
   486  	var (
   487  		task1 = "hahaha"
   488  		task2 = "hihihi"
   489  		task3 = "hehehe"
   490  	)
   491  	getDownstreamMetaFunc := func(task string) (*dbconfig.DBConfig, string) {
   492  		switch task {
   493  		case task1, task2:
   494  			return &dbconfig.DBConfig{}, "meta"
   495  		default:
   496  			return nil, ""
   497  		}
   498  	}
   499  
   500  	conn.InitMockDB(c)
   501  	lk := NewLockKeeper(getDownstreamMetaFunc)
   502  	c.Assert(lk.downstreamMetaMap, HasLen, 0)
   503  
   504  	downstreamMeta, err := lk.getDownstreamMeta(task3)
   505  	c.Assert(downstreamMeta, IsNil)
   506  	c.Assert(terror.ErrMasterOptimisticDownstreamMetaNotFound.Equal(err), IsTrue)
   507  
   508  	downstreamMeta, err = lk.getDownstreamMeta(task1)
   509  	c.Assert(err, IsNil)
   510  	c.Assert(lk.downstreamMetaMap, HasLen, 1)
   511  	c.Assert(downstreamMeta, Equals, lk.downstreamMetaMap[task1])
   512  	downstreamMeta2, err := lk.getDownstreamMeta(task1)
   513  	c.Assert(err, IsNil)
   514  	c.Assert(lk.downstreamMetaMap, HasLen, 1)
   515  	c.Assert(downstreamMeta, Equals, downstreamMeta2)
   516  
   517  	downstreamMeta3, err := lk.getDownstreamMeta(task2)
   518  	c.Assert(err, IsNil)
   519  	c.Assert(lk.downstreamMetaMap, HasLen, 2)
   520  	c.Assert(lk.downstreamMetaMap, HasKey, task1)
   521  	c.Assert(lk.downstreamMetaMap, HasKey, task2)
   522  	c.Assert(downstreamMeta3, Equals, lk.downstreamMetaMap[task2])
   523  
   524  	lk.RemoveDownstreamMeta(task3)
   525  	c.Assert(lk.downstreamMetaMap, HasLen, 2)
   526  	c.Assert(lk.downstreamMetaMap, HasKey, task1)
   527  	c.Assert(lk.downstreamMetaMap, HasKey, task2)
   528  
   529  	lk.RemoveDownstreamMeta(task1)
   530  	c.Assert(lk.downstreamMetaMap, HasLen, 1)
   531  	c.Assert(lk.downstreamMetaMap, HasKey, task2)
   532  	c.Assert(downstreamMeta3, Equals, lk.downstreamMetaMap[task2])
   533  
   534  	downstreamMeta, err = lk.getDownstreamMeta(task1)
   535  	c.Assert(err, IsNil)
   536  	c.Assert(lk.downstreamMetaMap, HasLen, 2)
   537  	c.Assert(downstreamMeta, Equals, lk.downstreamMetaMap[task1])
   538  	c.Assert(downstreamMeta3, Equals, lk.downstreamMetaMap[task2])
   539  
   540  	lk.Clear()
   541  	c.Assert(lk.downstreamMetaMap, HasLen, 0)
   542  }
   543  
   544  func (t *testKeeper) TestUpdateSourceTables(c *C) {
   545  	var (
   546  		tk         = NewTableKeeper()
   547  		task1      = "task-1"
   548  		source1    = "mysql-replica-1"
   549  		source2    = "mysql-replica-2"
   550  		downSchema = "db"
   551  		downTable  = "tbl"
   552  
   553  		tt11 = newTargetTable(task1, source1, downSchema, downTable, map[string]map[string]struct{}{
   554  			"db": {"tbl-1": struct{}{}, "tbl-2": struct{}{}},
   555  		})
   556  		tt12 = newTargetTable(task1, source2, downSchema, downTable, map[string]map[string]struct{}{
   557  			"db": {"tbl-1": struct{}{}, "tbl-2": struct{}{}},
   558  		})
   559  
   560  		st11 = NewSourceTables(task1, source1)
   561  		st12 = NewSourceTables(task1, source2)
   562  	)
   563  	for schema, tables := range tt11.UpTables {
   564  		for table := range tables {
   565  			st11.AddTable(schema, table, tt11.DownSchema, tt11.DownTable)
   566  		}
   567  	}
   568  	for schema, tables := range tt12.UpTables {
   569  		for table := range tables {
   570  			st12.AddTable(schema, table, tt12.DownSchema, tt12.DownTable)
   571  		}
   572  	}
   573  
   574  	// put st11
   575  	addTables, dropTables := tk.Update(st11)
   576  	c.Assert(addTables, HasLen, 2)
   577  	c.Assert(dropTables, HasLen, 0)
   578  
   579  	// put st11 again
   580  	addTables, dropTables = tk.Update(st11)
   581  	c.Assert(addTables, HasLen, 0)
   582  	c.Assert(dropTables, HasLen, 0)
   583  
   584  	// put st12
   585  	addTables, dropTables = tk.Update(st12)
   586  	c.Assert(addTables, HasLen, 2)
   587  	c.Assert(dropTables, HasLen, 0)
   588  
   589  	// update and put st12
   590  	newST := NewSourceTables(task1, source2)
   591  	for schema, tables := range tt12.UpTables {
   592  		for table := range tables {
   593  			newST.AddTable(schema, table, tt12.DownSchema, tt12.DownTable)
   594  		}
   595  	}
   596  	newST.RemoveTable("db", "tbl-1", downSchema, downTable)
   597  	newST.AddTable("db", "tbl-3", downSchema, downTable)
   598  	addTables, dropTables = tk.Update(newST)
   599  	c.Assert(addTables, HasLen, 1)
   600  	c.Assert(dropTables, HasLen, 1)
   601  	// put st12 again
   602  	addTables, dropTables = tk.Update(newST)
   603  	c.Assert(addTables, HasLen, 0)
   604  	c.Assert(dropTables, HasLen, 0)
   605  
   606  	// delete source table
   607  	newST.IsDeleted = true
   608  	addTables, dropTables = tk.Update(newST)
   609  	c.Assert(addTables, HasLen, 0)
   610  	c.Assert(dropTables, HasLen, 2)
   611  }