github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/checkpoint_test.go (about) 1 // Copyright 2019 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 syncer 15 16 import ( 17 "context" 18 "encoding/json" 19 "fmt" 20 "os" 21 "path/filepath" 22 "testing" 23 "time" 24 25 "github.com/DATA-DOG/go-sqlmock" 26 "github.com/go-mysql-org/go-mysql/mysql" 27 . "github.com/pingcap/check" 28 "github.com/pingcap/log" 29 tidbddl "github.com/pingcap/tidb/pkg/ddl" 30 "github.com/pingcap/tidb/pkg/parser/ast" 31 "github.com/pingcap/tidb/pkg/util/dbutil" 32 "github.com/pingcap/tidb/pkg/util/filter" 33 "github.com/pingcap/tiflow/dm/config" 34 "github.com/pingcap/tiflow/dm/pkg/binlog" 35 "github.com/pingcap/tiflow/dm/pkg/conn" 36 tcontext "github.com/pingcap/tiflow/dm/pkg/context" 37 "github.com/pingcap/tiflow/dm/pkg/cputil" 38 "github.com/pingcap/tiflow/dm/pkg/gtid" 39 dlog "github.com/pingcap/tiflow/dm/pkg/log" 40 "github.com/pingcap/tiflow/dm/pkg/retry" 41 "github.com/pingcap/tiflow/dm/pkg/schema" 42 "github.com/pingcap/tiflow/dm/syncer/dbconn" 43 "github.com/stretchr/testify/require" 44 "go.uber.org/zap/zapcore" 45 ) 46 47 var ( 48 cpid = "test_for_db" 49 schemaCreateSQL = "" 50 tableCreateSQL = "" 51 clearCheckPointSQL = "" 52 loadCheckPointSQL = "" 53 flushCheckPointSQL = "" 54 deleteCheckPointSQL = "" 55 deleteSchemaPointSQL = "" 56 ) 57 58 var _ = Suite(&testCheckpointSuite{}) 59 60 type testCheckpointSuite struct { 61 cfg *config.SubTaskConfig 62 mock sqlmock.Sqlmock 63 tracker *schema.Tracker 64 } 65 66 func (s *testCheckpointSuite) SetUpSuite(c *C) { 67 s.cfg = &config.SubTaskConfig{ 68 ServerID: 101, 69 MetaSchema: "test", 70 Name: "syncer_checkpoint_ut", 71 Flavor: mysql.MySQLFlavor, 72 } 73 74 log.SetLevel(zapcore.ErrorLevel) 75 var err error 76 77 s.tracker, err = schema.NewTestTracker(context.Background(), s.cfg.Name, nil, dlog.L()) 78 c.Assert(err, IsNil) 79 } 80 81 func (s *testCheckpointSuite) TestUpTest(c *C) { 82 s.tracker.Reset() 83 } 84 85 func (s *testCheckpointSuite) prepareCheckPointSQL() { 86 schemaCreateSQL = fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS `%s`", s.cfg.MetaSchema) 87 tableCreateSQL = fmt.Sprintf("CREATE TABLE IF NOT EXISTS `%s`.`%s` .*", s.cfg.MetaSchema, cputil.SyncerCheckpoint(s.cfg.Name)) 88 flushCheckPointSQL = fmt.Sprintf("INSERT INTO `%s`.`%s` .* VALUES.* ON DUPLICATE KEY UPDATE .*", s.cfg.MetaSchema, cputil.SyncerCheckpoint(s.cfg.Name)) 89 clearCheckPointSQL = fmt.Sprintf("DELETE FROM `%s`.`%s` WHERE id = \\?", s.cfg.MetaSchema, cputil.SyncerCheckpoint(s.cfg.Name)) 90 loadCheckPointSQL = fmt.Sprintf("SELECT .* FROM `%s`.`%s` WHERE id = \\?", s.cfg.MetaSchema, cputil.SyncerCheckpoint(s.cfg.Name)) 91 deleteCheckPointSQL = fmt.Sprintf("DELETE FROM %s WHERE id = \\? AND cp_schema = \\? AND cp_table = \\?", dbutil.TableName(s.cfg.MetaSchema, cputil.SyncerCheckpoint(s.cfg.Name))) 92 deleteSchemaPointSQL = fmt.Sprintf("DELETE FROM %s WHERE id = \\? AND cp_schema = \\?", dbutil.TableName(s.cfg.MetaSchema, cputil.SyncerCheckpoint(s.cfg.Name))) 93 } 94 95 // this test case uses sqlmock to simulate all SQL operations in tests. 96 func (s *testCheckpointSuite) TestCheckPoint(c *C) { 97 tctx := tcontext.Background() 98 99 cp := NewRemoteCheckPoint(tctx, s.cfg, nil, cpid) 100 defer func() { 101 s.mock.ExpectClose() 102 cp.Close() 103 }() 104 105 var err error 106 db, mock, err := sqlmock.New() 107 c.Assert(err, IsNil) 108 s.mock = mock 109 110 s.prepareCheckPointSQL() 111 112 mock.ExpectBegin() 113 mock.ExpectExec(schemaCreateSQL).WillReturnResult(sqlmock.NewResult(0, 1)) 114 mock.ExpectCommit() 115 mock.ExpectBegin() 116 mock.ExpectExec(tableCreateSQL).WillReturnResult(sqlmock.NewResult(0, 1)) 117 mock.ExpectCommit() 118 mock.ExpectBegin() 119 mock.ExpectExec(clearCheckPointSQL).WithArgs(cpid).WillReturnResult(sqlmock.NewResult(0, 1)) 120 mock.ExpectCommit() 121 122 dbConn, err := db.Conn(tcontext.Background().Context()) 123 c.Assert(err, IsNil) 124 conn := dbconn.NewDBConn(s.cfg, conn.NewBaseConnForTest(dbConn, &retry.FiniteRetryStrategy{})) 125 cp.(*RemoteCheckPoint).dbConn = conn 126 err = cp.(*RemoteCheckPoint).prepare(tctx) 127 c.Assert(err, IsNil) 128 c.Assert(cp.Clear(tctx), IsNil) 129 130 // test operation for global checkpoint 131 s.testGlobalCheckPoint(c, cp) 132 133 // test operation for table checkpoint 134 s.testTableCheckPoint(c, cp) 135 } 136 137 func (s *testCheckpointSuite) testGlobalCheckPoint(c *C, cp CheckPoint) { 138 tctx := tcontext.Background() 139 140 // global checkpoint init to min 141 c.Assert(cp.GlobalPoint().Position, Equals, binlog.MinPosition) 142 c.Assert(cp.FlushedGlobalPoint().Position, Equals, binlog.MinPosition) 143 144 // try load, but should load nothing 145 s.mock.ExpectQuery(loadCheckPointSQL).WillReturnRows(sqlmock.NewRows(nil)) 146 err := cp.Load(tctx) 147 c.Assert(err, IsNil) 148 c.Assert(cp.GlobalPoint().Position, Equals, binlog.MinPosition) 149 c.Assert(cp.FlushedGlobalPoint().Position, Equals, binlog.MinPosition) 150 151 oldMode := s.cfg.Mode 152 oldDir := s.cfg.Dir 153 defer func() { 154 s.cfg.Mode = oldMode 155 s.cfg.Dir = oldDir 156 }() 157 158 pos1 := mysql.Position{ 159 Name: "mysql-bin.000003", 160 Pos: 1943, 161 } 162 163 s.mock.ExpectQuery(loadCheckPointSQL).WithArgs(cpid).WillReturnRows(sqlmock.NewRows(nil)) 164 err = cp.Load(tctx) 165 c.Assert(err, IsNil) 166 cp.SaveGlobalPoint(binlog.Location{Position: pos1}) 167 168 s.mock.ExpectBegin() 169 s.mock.ExpectExec("(162)?"+flushCheckPointSQL).WithArgs(cpid, "", "", pos1.Name, pos1.Pos, "", "", 0, "", "null", true).WillReturnResult(sqlmock.NewResult(0, 1)) 170 s.mock.ExpectCommit() 171 // Create a new snapshot, and discard it, then create a new snapshot again. 172 cp.Snapshot(true) 173 cp.DiscardPendingSnapshots() 174 snap := cp.Snapshot(true) 175 err = cp.FlushPointsExcept(tctx, snap.id, nil, nil, nil) 176 c.Assert(err, IsNil) 177 c.Assert(cp.GlobalPoint().Position, Equals, pos1) 178 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos1) 179 180 // try load from config 181 pos1.Pos = 2044 182 s.cfg.Mode = config.ModeIncrement 183 s.cfg.Meta = &config.Meta{BinLogName: pos1.Name, BinLogPos: pos1.Pos} 184 err = cp.LoadMeta(tctx.Ctx) 185 c.Assert(err, IsNil) 186 c.Assert(cp.GlobalPoint().Position, Equals, pos1) 187 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos1) 188 189 s.cfg.Mode = oldMode 190 s.cfg.Meta = nil 191 192 // test save global point 193 pos2 := mysql.Position{ 194 Name: "mysql-bin.000005", 195 Pos: 2052, 196 } 197 cp.SaveGlobalPoint(binlog.Location{Position: pos2}) 198 c.Assert(cp.GlobalPoint().Position, Equals, pos2) 199 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos1) 200 201 // test rollback 202 cp.Rollback() 203 c.Assert(cp.GlobalPoint().Position, Equals, pos1) 204 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos1) 205 206 // save again 207 cp.SaveGlobalPoint(binlog.Location{Position: pos2}) 208 c.Assert(cp.GlobalPoint().Position, Equals, pos2) 209 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos1) 210 211 // flush + rollback 212 s.mock.ExpectBegin() 213 s.mock.ExpectExec("(202)?"+flushCheckPointSQL).WithArgs(cpid, "", "", pos2.Name, pos2.Pos, "", "", 0, "", "null", true).WillReturnResult(sqlmock.NewResult(0, 1)) 214 s.mock.ExpectCommit() 215 err = cp.FlushPointsExcept(tctx, cp.Snapshot(true).id, nil, nil, nil) 216 c.Assert(err, IsNil) 217 cp.Rollback() 218 c.Assert(cp.GlobalPoint().Position, Equals, pos2) 219 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos2) 220 221 // try load from DB 222 pos3 := pos2 223 pos3.Pos = pos2.Pos + 1000 // > pos2 to enable save 224 cp.SaveGlobalPoint(binlog.Location{Position: pos3}) 225 columns := []string{"cp_schema", "cp_table", "binlog_name", "binlog_pos", "binlog_gtid", "exit_safe_binlog_name", "exit_safe_binlog_pos", "exit_safe_binlog_gtid", "table_info", "is_global"} 226 s.mock.ExpectQuery(loadCheckPointSQL).WithArgs(cpid).WillReturnRows(sqlmock.NewRows(columns).AddRow("", "", pos2.Name, pos2.Pos, "", "", 0, "", "null", true)) 227 err = cp.Load(tctx) 228 c.Assert(err, IsNil) 229 c.Assert(cp.GlobalPoint().Position, Equals, pos2) 230 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos2) 231 232 // test save older point 233 /*var buf bytes.Buffer 234 log.SetOutput(&buf) 235 cp.SaveGlobalPoint(pos1) 236 c.Assert(cp.GlobalPoint(), Equals, pos2) 237 c.Assert(cp.FlushedGlobalPoint(), Equals, pos2) 238 matchStr := fmt.Sprintf(".*try to save %s is older than current pos %s", pos1, pos2) 239 matchStr = strings.Replace(strings.Replace(matchStr, ")", "\\)", -1), "(", "\\(", -1) 240 c.Assert(strings.TrimSpace(buf.String()), Matches, matchStr) 241 log.SetOutput(os.Stdout)*/ 242 243 // test clear 244 s.mock.ExpectBegin() 245 s.mock.ExpectExec(clearCheckPointSQL).WithArgs(cpid).WillReturnResult(sqlmock.NewResult(0, 1)) 246 s.mock.ExpectCommit() 247 err = cp.Clear(tctx) 248 c.Assert(err, IsNil) 249 c.Assert(cp.GlobalPoint().Position, Equals, binlog.MinPosition) 250 c.Assert(cp.FlushedGlobalPoint().Position, Equals, binlog.MinPosition) 251 252 s.mock.ExpectQuery(loadCheckPointSQL).WillReturnRows(sqlmock.NewRows(nil)) 253 err = cp.Load(tctx) 254 c.Assert(err, IsNil) 255 c.Assert(cp.GlobalPoint().Position, Equals, binlog.MinPosition) 256 c.Assert(cp.FlushedGlobalPoint().Position, Equals, binlog.MinPosition) 257 258 // try load from mydumper's output 259 dir := c.MkDir() 260 261 filename := filepath.Join(dir, "metadata") 262 err = os.WriteFile(filename, []byte( 263 fmt.Sprintf("SHOW MASTER STATUS:\n\tLog: %s\n\tPos: %d\n\tGTID:\n\nSHOW SLAVE STATUS:\n\tHost: %s\n\tLog: %s\n\tPos: %d\n\tGTID:\n\n", pos1.Name, pos1.Pos, "slave_host", pos1.Name, pos1.Pos+1000)), 264 0o644) 265 c.Assert(err, IsNil) 266 s.cfg.Mode = config.ModeAll 267 s.cfg.Dir = dir 268 c.Assert(cp.LoadMeta(tctx.Ctx), IsNil) 269 270 // should flush because checkpoint hasn't been updated before (cp.globalPointCheckOrSaveTime.IsZero() == true). 271 snapshot := cp.Snapshot(true) 272 c.Assert(snapshot.id, Equals, 4) 273 274 s.mock.ExpectQuery(loadCheckPointSQL).WillReturnRows(sqlmock.NewRows(nil)) 275 err = cp.Load(tctx) 276 c.Assert(err, IsNil) 277 c.Assert(cp.GlobalPoint().Position, Equals, pos1) 278 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos1) 279 280 s.mock.ExpectBegin() 281 s.mock.ExpectExec(clearCheckPointSQL).WithArgs(cpid).WillReturnResult(sqlmock.NewResult(0, 1)) 282 s.mock.ExpectCommit() 283 err = cp.Clear(tctx) 284 c.Assert(err, IsNil) 285 286 // check dumpling write exitSafeModeLocation in metadata 287 err = os.WriteFile(filename, []byte( 288 fmt.Sprintf(`SHOW MASTER STATUS: 289 Log: %s 290 Pos: %d 291 GTID: 292 293 SHOW SLAVE STATUS: 294 Host: %s 295 Log: %s 296 Pos: %d 297 GTID: 298 299 SHOW MASTER STATUS: /* AFTER CONNECTION POOL ESTABLISHED */ 300 Log: %s 301 Pos: %d 302 GTID: 303 `, pos1.Name, pos1.Pos, "slave_host", pos1.Name, pos1.Pos+1000, pos2.Name, pos2.Pos)), 0o644) 304 c.Assert(err, IsNil) 305 c.Assert(cp.LoadMeta(tctx.Ctx), IsNil) 306 307 // should flush because exitSafeModeLocation is true 308 snapshot = cp.Snapshot(true) 309 c.Assert(snapshot, NotNil) 310 s.mock.ExpectBegin() 311 s.mock.ExpectExec("(202)?"+flushCheckPointSQL).WithArgs(cpid, "", "", pos1.Name, pos1.Pos, "", pos2.Name, pos2.Pos, "", "null", true).WillReturnResult(sqlmock.NewResult(0, 1)) 312 s.mock.ExpectCommit() 313 err = cp.FlushPointsExcept(tctx, snapshot.id, nil, nil, nil) 314 c.Assert(err, IsNil) 315 s.mock.ExpectQuery(loadCheckPointSQL).WillReturnRows(sqlmock.NewRows(nil)) 316 err = cp.Load(tctx) 317 c.Assert(err, IsNil) 318 c.Assert(cp.GlobalPoint().Position, Equals, pos1) 319 c.Assert(cp.FlushedGlobalPoint().Position, Equals, pos1) 320 c.Assert(cp.SafeModeExitPoint().Position, Equals, pos2) 321 322 // when use async flush, even exitSafeModeLocation is true we won't flush 323 c.Assert(cp.LoadMeta(tctx.Ctx), IsNil) 324 snapshot = cp.Snapshot(false) 325 c.Assert(snapshot, IsNil) 326 } 327 328 func (s *testCheckpointSuite) testTableCheckPoint(c *C, cp CheckPoint) { 329 var ( 330 tctx = tcontext.Background() 331 table = &filter.Table{ 332 Schema: "test_db", 333 Name: "test_table", 334 } 335 schemaName = "test_db" 336 tableName = "test_table" 337 pos1 = mysql.Position{ 338 Name: "mysql-bin.000008", 339 Pos: 123, 340 } 341 pos2 = mysql.Position{ 342 Name: "mysql-bin.000008", 343 Pos: 456, 344 } 345 err error 346 ) 347 348 // not exist 349 older := cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 350 c.Assert(older, IsFalse) 351 352 // save 353 cp.SaveTablePoint(table, binlog.Location{Position: pos2}, nil) 354 older = cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 355 c.Assert(older, IsTrue) 356 357 // rollback, to min 358 cp.Rollback() 359 older = cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 360 c.Assert(older, IsFalse) 361 362 // save again 363 cp.SaveTablePoint(table, binlog.Location{Position: pos2}, nil) 364 older = cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 365 c.Assert(older, IsTrue) 366 367 // flush + rollback 368 s.mock.ExpectBegin() 369 s.mock.ExpectExec("(284)?"+flushCheckPointSQL).WithArgs(cpid, table.Schema, table.Name, pos2.Name, pos2.Pos, "", "", 0, "", sqlmock.AnyArg(), false).WillReturnResult(sqlmock.NewResult(0, 1)) 370 s.mock.ExpectCommit() 371 err = cp.FlushPointsExcept(tctx, cp.Snapshot(true).id, nil, nil, nil) 372 c.Assert(err, IsNil) 373 cp.Rollback() 374 older = cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 375 c.Assert(older, IsTrue) 376 377 // save 378 cp.SaveTablePoint(table, binlog.Location{Position: pos2}, nil) 379 older = cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 380 c.Assert(older, IsTrue) 381 382 // delete 383 s.mock.ExpectBegin() 384 s.mock.ExpectExec(deleteCheckPointSQL).WithArgs(cpid, schemaName, tableName).WillReturnResult(sqlmock.NewResult(0, 1)) 385 s.mock.ExpectCommit() 386 c.Assert(cp.DeleteTablePoint(tctx, table), IsNil) 387 s.mock.ExpectBegin() 388 s.mock.ExpectExec(deleteSchemaPointSQL).WithArgs(cpid, schemaName).WillReturnResult(sqlmock.NewResult(0, 1)) 389 s.mock.ExpectCommit() 390 c.Assert(cp.DeleteSchemaPoint(tctx, schemaName), IsNil) 391 392 ctx := context.Background() 393 394 // test save with table info and rollback 395 c.Assert(s.tracker.CreateSchemaIfNotExists(schemaName), IsNil) 396 stmt, err := parseSQL("create table " + tableName + " (c int);") 397 c.Assert(err, IsNil) 398 err = s.tracker.Exec(ctx, schemaName, stmt) 399 c.Assert(err, IsNil) 400 ti, err := s.tracker.GetTableInfo(table) 401 c.Assert(err, IsNil) 402 cp.SaveTablePoint(table, binlog.Location{Position: pos1}, ti) 403 rcp := cp.(*RemoteCheckPoint) 404 c.Assert(rcp.points[schemaName][tableName].TableInfo(), NotNil) 405 c.Assert(rcp.points[schemaName][tableName].flushedPoint.ti, IsNil) 406 407 cp.Rollback() 408 rcp = cp.(*RemoteCheckPoint) 409 c.Assert(rcp.points[schemaName][tableName].TableInfo(), IsNil) 410 c.Assert(rcp.points[schemaName][tableName].flushedPoint.ti, IsNil) 411 412 // test save, flush and rollback to not nil table info 413 cp.SaveTablePoint(table, binlog.Location{Position: pos1}, ti) 414 tiBytes, _ := json.Marshal(ti) 415 s.mock.ExpectBegin() 416 s.mock.ExpectExec(flushCheckPointSQL).WithArgs(cpid, schemaName, tableName, pos1.Name, pos1.Pos, "", "", 0, "", string(tiBytes), false).WillReturnResult(sqlmock.NewResult(0, 1)) 417 s.mock.ExpectCommit() 418 lastGlobalPoint := cp.GlobalPoint() 419 lastGlobalPointSavedTime := cp.GlobalPointSaveTime() 420 c.Assert(cp.FlushPointsExcept(tctx, cp.Snapshot(true).id, nil, nil, nil), IsNil) 421 c.Assert(cp.GlobalPoint(), Equals, lastGlobalPoint) 422 c.Assert(cp.GlobalPointSaveTime(), Equals, lastGlobalPointSavedTime) 423 stmt, err = parseSQL("alter table " + tableName + " add c2 int;") 424 c.Assert(err, IsNil) 425 err = s.tracker.Exec(ctx, schemaName, stmt) 426 c.Assert(err, IsNil) 427 ti2, err := s.tracker.GetTableInfo(table) 428 c.Assert(err, IsNil) 429 cp.SaveTablePoint(table, binlog.Location{Position: pos2}, ti2) 430 cp.Rollback() 431 432 // clear, to min 433 s.mock.ExpectBegin() 434 s.mock.ExpectExec(clearCheckPointSQL).WithArgs(cpid).WillReturnResult(sqlmock.NewResult(0, 1)) 435 s.mock.ExpectCommit() 436 err = cp.Clear(tctx) 437 c.Assert(err, IsNil) 438 older = cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 439 c.Assert(older, IsFalse) 440 441 // test save table point less than global point 442 func() { 443 defer func() { 444 r := recover() 445 matchStr := ".*less than global checkpoint.*" 446 c.Assert(r, Matches, matchStr) 447 }() 448 cp.SaveGlobalPoint(binlog.Location{Position: pos2}) 449 cp.SaveTablePoint(table, binlog.Location{Position: pos1}, nil) 450 }() 451 452 // flush but except + rollback 453 s.mock.ExpectBegin() 454 s.mock.ExpectExec("(320)?"+flushCheckPointSQL).WithArgs(cpid, "", "", pos2.Name, pos2.Pos, "", "", 0, "", "null", true).WillReturnResult(sqlmock.NewResult(0, 1)) 455 s.mock.ExpectCommit() 456 lastGlobalPoint = cp.GlobalPoint() 457 lastGlobalPointSavedTime = cp.GlobalPointSaveTime() 458 err = cp.FlushPointsExcept(tctx, cp.Snapshot(true).id, []*filter.Table{table}, nil, nil) 459 fmt.Println(cp.GlobalPoint(), lastGlobalPoint) 460 c.Assert(cp.GlobalPoint(), Equals, lastGlobalPoint) 461 c.Assert(cp.GlobalPointSaveTime(), Not(Equals), lastGlobalPointSavedTime) 462 c.Assert(err, IsNil) 463 cp.Rollback() 464 older = cp.IsOlderThanTablePoint(table, binlog.Location{Position: pos1}) 465 c.Assert(older, IsFalse) 466 467 s.mock.ExpectBegin() 468 s.mock.ExpectExec(clearCheckPointSQL).WithArgs(cpid).WillReturnResult(sqlmock.NewResult(0, 1)) 469 s.mock.ExpectCommit() 470 c.Assert(cp.Clear(tctx), IsNil) 471 // load table point and exitSafe, with enable GTID 472 s.cfg.EnableGTID = true 473 flavor := mysql.MySQLFlavor 474 gSetStr := "03fc0263-28c7-11e7-a653-6c0b84d59f30:123" 475 gs, _ := gtid.ParserGTID(flavor, gSetStr) 476 columns := []string{"cp_schema", "cp_table", "binlog_name", "binlog_pos", "binlog_gtid", "exit_safe_binlog_name", "exit_safe_binlog_pos", "exit_safe_binlog_gtid", "table_info", "is_global"} 477 s.mock.ExpectQuery(loadCheckPointSQL).WithArgs(cpid).WillReturnRows( 478 sqlmock.NewRows(columns).AddRow("", "", pos2.Name, pos2.Pos, gs.String(), pos2.Name, pos2.Pos, gs.String(), "null", true). 479 AddRow(schemaName, tableName, pos2.Name, pos2.Pos, gs.String(), "", 0, "", tiBytes, false)) 480 err = cp.Load(tctx) 481 c.Assert(err, IsNil) 482 c.Assert(cp.GlobalPoint(), DeepEquals, binlog.NewLocation(pos2, gs)) 483 rcp = cp.(*RemoteCheckPoint) 484 c.Assert(rcp.points[schemaName][tableName].TableInfo(), NotNil) 485 c.Assert(rcp.points[schemaName][tableName].flushedPoint.ti, NotNil) 486 c.Assert(*rcp.safeModeExitPoint, DeepEquals, binlog.NewLocation(pos2, gs)) 487 } 488 489 func TestRemoteCheckPointLoadIntoSchemaTracker(t *testing.T) { 490 cfg := genDefaultSubTaskConfig4Test() 491 cfg.WorkerCount = 0 492 ctx := context.Background() 493 494 db, _, err := sqlmock.New() 495 require.NoError(t, err) 496 dbConn, err := db.Conn(ctx) 497 require.NoError(t, err) 498 downstreamTrackConn := dbconn.NewDBConn(cfg, conn.NewBaseConnForTest(dbConn, &retry.FiniteRetryStrategy{})) 499 schemaTracker, err := schema.NewTestTracker(ctx, cfg.Name, downstreamTrackConn, dlog.L()) 500 require.NoError(t, err) 501 defer schemaTracker.Close() //nolint 502 503 tbl1 := &filter.Table{Schema: "test", Name: "tbl1"} 504 tbl2 := &filter.Table{Schema: "test", Name: "tbl2"} 505 506 // before load 507 _, err = schemaTracker.GetTableInfo(tbl1) 508 require.Error(t, err) 509 _, err = schemaTracker.GetTableInfo(tbl2) 510 require.Error(t, err) 511 512 cp := NewRemoteCheckPoint(tcontext.Background(), cfg, nil, "1") 513 checkpoint := cp.(*RemoteCheckPoint) 514 515 parser, err := conn.GetParserFromSQLModeStr("") 516 require.NoError(t, err) 517 createNode, err := parser.ParseOneStmt("create table tbl1(id int)", "", "") 518 require.NoError(t, err) 519 ti, err := tidbddl.BuildTableInfoFromAST(createNode.(*ast.CreateTableStmt)) 520 require.NoError(t, err) 521 522 tp1 := tablePoint{ti: ti} 523 tp2 := tablePoint{} 524 checkpoint.points[tbl1.Schema] = make(map[string]*binlogPoint) 525 checkpoint.points[tbl1.Schema][tbl1.Name] = &binlogPoint{flushedPoint: tp1} 526 checkpoint.points[tbl2.Schema][tbl2.Name] = &binlogPoint{flushedPoint: tp2} 527 528 // after load 529 err = checkpoint.LoadIntoSchemaTracker(ctx, schemaTracker) 530 require.NoError(t, err) 531 tableInfo, err := schemaTracker.GetTableInfo(tbl1) 532 require.NoError(t, err) 533 require.Len(t, tableInfo.Columns, 1) 534 _, err = schemaTracker.GetTableInfo(tbl2) 535 require.Error(t, err) 536 537 // test BatchCreateTableWithInfo will not meet kv entry too large error 538 539 // create 100K comment string 540 comment := make([]byte, 0, 100000) 541 for i := 0; i < 100000; i++ { 542 comment = append(comment, 'A') 543 } 544 ti.Comment = string(comment) 545 546 tp1 = tablePoint{ti: ti} 547 amount := 100 548 for i := 0; i < amount; i++ { 549 tableName := fmt.Sprintf("tbl_%d", i) 550 checkpoint.points[tbl1.Schema][tableName] = &binlogPoint{flushedPoint: tp1} 551 } 552 err = checkpoint.LoadIntoSchemaTracker(ctx, schemaTracker) 553 require.NoError(t, err) 554 } 555 556 func TestLastFlushOutdated(t *testing.T) { 557 cfg := genDefaultSubTaskConfig4Test() 558 cfg.WorkerCount = 0 559 cfg.CheckpointFlushInterval = 1 560 561 cp := NewRemoteCheckPoint(tcontext.Background(), cfg, nil, "1") 562 checkpoint := cp.(*RemoteCheckPoint) 563 checkpoint.globalPointSaveTime = time.Now().Add(-2 * time.Second) 564 565 require.True(t, checkpoint.LastFlushOutdated()) 566 require.Nil(t, checkpoint.Snapshot(true)) 567 // though snapshot is nil, checkpoint is not outdated 568 require.False(t, checkpoint.LastFlushOutdated()) 569 }