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 }