github.com/pingcap/tidb-lightning@v5.0.0-rc.0.20210428090220-84b649866577+incompatible/lightning/checkpoints/checkpoints_sql_test.go (about) 1 package checkpoints_test 2 3 import ( 4 "context" 5 "database/sql" 6 "strings" 7 "time" 8 9 "github.com/DATA-DOG/go-sqlmock" 10 . "github.com/pingcap/check" 11 12 "github.com/pingcap/tidb-lightning/lightning/checkpoints" 13 "github.com/pingcap/tidb-lightning/lightning/common" 14 "github.com/pingcap/tidb-lightning/lightning/mydump" 15 "github.com/pingcap/tidb-lightning/lightning/verification" 16 ) 17 18 var _ = Suite(&cpSQLSuite{}) 19 20 type cpSQLSuite struct { 21 db *sql.DB 22 mock sqlmock.Sqlmock 23 cpdb *checkpoints.MySQLCheckpointsDB 24 } 25 26 func (s *cpSQLSuite) SetUpTest(c *C) { 27 db, mock, err := sqlmock.New() 28 c.Assert(err, IsNil) 29 s.db = db 30 s.mock = mock 31 32 // 1. create the checkpoints database. 33 s.mock. 34 ExpectExec("CREATE DATABASE IF NOT EXISTS `mock-schema`"). 35 WillReturnResult(sqlmock.NewResult(1, 1)) 36 s.mock. 37 ExpectExec("CREATE TABLE IF NOT EXISTS `mock-schema`\\.task_v\\d+ .+"). 38 WillReturnResult(sqlmock.NewResult(2, 1)) 39 s.mock. 40 ExpectExec("CREATE TABLE IF NOT EXISTS `mock-schema`\\.table_v\\d+ .+"). 41 WillReturnResult(sqlmock.NewResult(3, 1)) 42 s.mock. 43 ExpectExec("CREATE TABLE IF NOT EXISTS `mock-schema`\\.engine_v\\d+ .+"). 44 WillReturnResult(sqlmock.NewResult(4, 1)) 45 s.mock. 46 ExpectExec("CREATE TABLE IF NOT EXISTS `mock-schema`\\.chunk_v\\d+ .+"). 47 WillReturnResult(sqlmock.NewResult(5, 1)) 48 49 cpdb, err := checkpoints.NewMySQLCheckpointsDB(context.Background(), s.db, "mock-schema") 50 c.Assert(err, IsNil) 51 c.Assert(s.mock.ExpectationsWereMet(), IsNil) 52 s.cpdb = cpdb 53 } 54 55 func (s *cpSQLSuite) TearDownTest(c *C) { 56 s.mock.ExpectClose() 57 c.Assert(s.cpdb.Close(), IsNil) 58 c.Assert(s.mock.ExpectationsWereMet(), IsNil) 59 } 60 61 func (s *cpSQLSuite) TestNormalOperations(c *C) { 62 ctx := context.Background() 63 cpdb := s.cpdb 64 65 // 2. initialize with checkpoint data. 66 67 s.mock.ExpectBegin() 68 initializeStmt := s.mock.ExpectPrepare( 69 "REPLACE INTO `mock-schema`\\.task_v\\d+") 70 initializeStmt.ExpectExec(). 71 WithArgs(123, "/data", "importer", "127.0.0.1:8287", "127.0.0.1", 4000, "127.0.0.1:2379", "/tmp/sorted-kv", common.ReleaseVersion). 72 WillReturnResult(sqlmock.NewResult(6, 1)) 73 initializeStmt = s.mock. 74 ExpectPrepare("INSERT INTO `mock-schema`\\.table_v\\d+") 75 initializeStmt.ExpectExec(). 76 WithArgs(123, "`db1`.`t1`", sqlmock.AnyArg(), int64(1)). 77 WillReturnResult(sqlmock.NewResult(7, 1)) 78 initializeStmt.ExpectExec(). 79 WithArgs(123, "`db1`.`t2`", sqlmock.AnyArg(), int64(2)). 80 WillReturnResult(sqlmock.NewResult(8, 1)) 81 initializeStmt.ExpectExec(). 82 WithArgs(123, "`db2`.`t3`", sqlmock.AnyArg(), int64(3)). 83 WillReturnResult(sqlmock.NewResult(9, 1)) 84 s.mock.ExpectCommit() 85 86 s.mock.MatchExpectationsInOrder(false) 87 cfg := newTestConfig() 88 err := cpdb.Initialize(ctx, cfg, map[string]*checkpoints.TidbDBInfo{ 89 "db1": { 90 Name: "db1", 91 Tables: map[string]*checkpoints.TidbTableInfo{ 92 "t1": {Name: "t1", ID: 1}, 93 "t2": {Name: "t2", ID: 2}, 94 }, 95 }, 96 "db2": { 97 Name: "db2", 98 Tables: map[string]*checkpoints.TidbTableInfo{ 99 "t3": {Name: "t3", ID: 3}, 100 }, 101 }, 102 }) 103 s.mock.MatchExpectationsInOrder(true) 104 c.Assert(err, IsNil) 105 c.Assert(s.mock.ExpectationsWereMet(), IsNil) 106 107 // 3. set some checkpoints 108 109 s.mock.ExpectBegin() 110 insertEngineStmt := s.mock. 111 ExpectPrepare("REPLACE INTO `mock-schema`\\.engine_v\\d+ .+") 112 insertEngineStmt. 113 ExpectExec(). 114 WithArgs("`db1`.`t2`", 0, 30). 115 WillReturnResult(sqlmock.NewResult(8, 1)) 116 insertEngineStmt. 117 ExpectExec(). 118 WithArgs("`db1`.`t2`", -1, 30). 119 WillReturnResult(sqlmock.NewResult(9, 1)) 120 insertChunkStmt := s.mock. 121 ExpectPrepare("REPLACE INTO `mock-schema`\\.chunk_v\\d+ .+") 122 insertChunkStmt. 123 ExpectExec(). 124 WithArgs("`db1`.`t2`", 0, "/tmp/path/1.sql", 0, mydump.SourceTypeSQL, 0, "", 123, []byte("null"), 12, 102400, 1, 5000, 1234567890). 125 WillReturnResult(sqlmock.NewResult(10, 1)) 126 s.mock.ExpectCommit() 127 128 s.mock.MatchExpectationsInOrder(false) 129 err = cpdb.InsertEngineCheckpoints(ctx, "`db1`.`t2`", map[int32]*checkpoints.EngineCheckpoint{ 130 0: { 131 Status: checkpoints.CheckpointStatusLoaded, 132 Chunks: []*checkpoints.ChunkCheckpoint{{ 133 Key: checkpoints.ChunkCheckpointKey{ 134 Path: "/tmp/path/1.sql", 135 Offset: 0, 136 }, 137 FileMeta: mydump.SourceFileMeta{ 138 Path: "/tmp/path/1.sql", 139 Type: mydump.SourceTypeSQL, 140 FileSize: 123, 141 }, 142 Chunk: mydump.Chunk{ 143 Offset: 12, 144 EndOffset: 102400, 145 PrevRowIDMax: 1, 146 RowIDMax: 5000, 147 }, 148 Timestamp: 1234567890, 149 }}, 150 }, 151 -1: { 152 Status: checkpoints.CheckpointStatusLoaded, 153 Chunks: nil, 154 }, 155 }) 156 s.mock.MatchExpectationsInOrder(true) 157 c.Assert(err, IsNil) 158 c.Assert(s.mock.ExpectationsWereMet(), IsNil) 159 160 // 4. update some checkpoints 161 162 cpd := checkpoints.NewTableCheckpointDiff() 163 scm := checkpoints.StatusCheckpointMerger{ 164 EngineID: 0, 165 Status: checkpoints.CheckpointStatusImported, 166 } 167 scm.MergeInto(cpd) 168 scm = checkpoints.StatusCheckpointMerger{ 169 EngineID: checkpoints.WholeTableEngineID, 170 Status: checkpoints.CheckpointStatusAllWritten, 171 } 172 scm.MergeInto(cpd) 173 rcm := checkpoints.RebaseCheckpointMerger{ 174 AllocBase: 132861, 175 } 176 rcm.MergeInto(cpd) 177 ccm := checkpoints.ChunkCheckpointMerger{ 178 EngineID: 0, 179 Key: checkpoints.ChunkCheckpointKey{Path: "/tmp/path/1.sql", Offset: 0}, 180 Checksum: verification.MakeKVChecksum(4491, 586, 486070148917), 181 Pos: 55904, 182 RowID: 681, 183 } 184 ccm.MergeInto(cpd) 185 186 s.mock.ExpectBegin() 187 s.mock. 188 ExpectPrepare("UPDATE `mock-schema`\\.chunk_v\\d+ SET pos = .+"). 189 ExpectExec(). 190 WithArgs( 191 55904, 681, 4491, 586, 486070148917, []byte("null"), 192 "`db1`.`t2`", 0, "/tmp/path/1.sql", 0, 193 ). 194 WillReturnResult(sqlmock.NewResult(11, 1)) 195 s.mock. 196 ExpectPrepare("UPDATE `mock-schema`\\.table_v\\d+ SET alloc_base = .+"). 197 ExpectExec(). 198 WithArgs(132861, "`db1`.`t2`"). 199 WillReturnResult(sqlmock.NewResult(12, 1)) 200 s.mock. 201 ExpectPrepare("UPDATE `mock-schema`\\.engine_v\\d+ SET status = .+"). 202 ExpectExec(). 203 WithArgs(120, "`db1`.`t2`", 0). 204 WillReturnResult(sqlmock.NewResult(13, 1)) 205 s.mock. 206 ExpectPrepare("UPDATE `mock-schema`\\.table_v\\d+ SET status = .+"). 207 ExpectExec(). 208 WithArgs(60, "`db1`.`t2`"). 209 WillReturnResult(sqlmock.NewResult(14, 1)) 210 s.mock.ExpectCommit() 211 212 s.mock.MatchExpectationsInOrder(false) 213 cpdb.Update(map[string]*checkpoints.TableCheckpointDiff{"`db1`.`t2`": cpd}) 214 s.mock.MatchExpectationsInOrder(true) 215 c.Assert(s.mock.ExpectationsWereMet(), IsNil) 216 217 // 5. get back the checkpoints 218 219 s.mock.ExpectBegin() 220 s.mock. 221 ExpectQuery("SELECT .+ FROM `mock-schema`\\.engine_v\\d+"). 222 WithArgs("`db1`.`t2`"). 223 WillReturnRows( 224 sqlmock.NewRows([]string{"engine_id", "status"}). 225 AddRow(0, 120). 226 AddRow(-1, 30), 227 ) 228 s.mock. 229 ExpectQuery("SELECT (?s:.+) FROM `mock-schema`\\.chunk_v\\d+"). 230 WithArgs("`db1`.`t2`"). 231 WillReturnRows( 232 sqlmock.NewRows([]string{ 233 "engine_id", "path", "offset", "type", "compression", "sort_key", "file_size", "columns", 234 "pos", "end_offset", "prev_rowid_max", "rowid_max", 235 "kvc_bytes", "kvc_kvs", "kvc_checksum", "unix_timestamp(create_time)", 236 }). 237 AddRow( 238 0, "/tmp/path/1.sql", 0, mydump.SourceTypeSQL, 0, "", 123, "[]", 239 55904, 102400, 681, 5000, 240 4491, 586, 486070148917, 1234567894, 241 ), 242 ) 243 s.mock. 244 ExpectQuery("SELECT .+ FROM `mock-schema`\\.table_v\\d+"). 245 WithArgs("`db1`.`t2`"). 246 WillReturnRows( 247 sqlmock.NewRows([]string{"status", "alloc_base", "table_id"}). 248 AddRow(60, 132861, int64(2)), 249 ) 250 s.mock.ExpectCommit() 251 252 cp, err := cpdb.Get(ctx, "`db1`.`t2`") 253 c.Assert(err, IsNil) 254 c.Assert(cp, DeepEquals, &checkpoints.TableCheckpoint{ 255 Status: checkpoints.CheckpointStatusAllWritten, 256 AllocBase: 132861, 257 TableID: int64(2), 258 Engines: map[int32]*checkpoints.EngineCheckpoint{ 259 -1: {Status: checkpoints.CheckpointStatusLoaded}, 260 0: { 261 Status: checkpoints.CheckpointStatusImported, 262 Chunks: []*checkpoints.ChunkCheckpoint{{ 263 Key: checkpoints.ChunkCheckpointKey{ 264 Path: "/tmp/path/1.sql", 265 Offset: 0, 266 }, 267 FileMeta: mydump.SourceFileMeta{ 268 Path: "/tmp/path/1.sql", 269 Type: mydump.SourceTypeSQL, 270 FileSize: 123, 271 }, 272 ColumnPermutation: []int{}, 273 Chunk: mydump.Chunk{ 274 Offset: 55904, 275 EndOffset: 102400, 276 PrevRowIDMax: 681, 277 RowIDMax: 5000, 278 }, 279 Checksum: verification.MakeKVChecksum(4491, 586, 486070148917), 280 Timestamp: 1234567894, 281 }}, 282 }, 283 }, 284 }) 285 c.Assert(s.mock.ExpectationsWereMet(), IsNil) 286 } 287 288 func (s *cpSQLSuite) TestRemoveAllCheckpoints(c *C) { 289 s.mock.ExpectExec("DROP SCHEMA `mock-schema`").WillReturnResult(sqlmock.NewResult(0, 1)) 290 291 err := s.cpdb.RemoveCheckpoint(context.Background(), "all") 292 c.Assert(err, IsNil) 293 } 294 295 func (s *cpSQLSuite) TestRemoveOneCheckpoint(c *C) { 296 s.mock.ExpectBegin() 297 s.mock. 298 ExpectExec("DELETE FROM `mock-schema`\\.chunk_v\\d+ WHERE table_name = \\?"). 299 WithArgs("`db1`.`t2`"). 300 WillReturnResult(sqlmock.NewResult(0, 4)) 301 s.mock. 302 ExpectExec("DELETE FROM `mock-schema`\\.engine_v\\d+ WHERE table_name = \\?"). 303 WithArgs("`db1`.`t2`"). 304 WillReturnResult(sqlmock.NewResult(0, 2)) 305 s.mock. 306 ExpectExec("DELETE FROM `mock-schema`\\.table_v\\d+ WHERE table_name = \\?"). 307 WithArgs("`db1`.`t2`"). 308 WillReturnResult(sqlmock.NewResult(0, 1)) 309 s.mock.ExpectCommit() 310 311 err := s.cpdb.RemoveCheckpoint(context.Background(), "`db1`.`t2`") 312 c.Assert(err, IsNil) 313 } 314 315 func (s *cpSQLSuite) TestIgnoreAllErrorCheckpoints(c *C) { 316 s.mock.ExpectBegin() 317 s.mock. 318 ExpectExec("UPDATE `mock-schema`\\.engine_v\\d+ SET status = 30 WHERE 'all' = \\? AND status <= 25"). 319 WithArgs(sqlmock.AnyArg()). 320 WillReturnResult(sqlmock.NewResult(5, 3)) 321 s.mock. 322 ExpectExec("UPDATE `mock-schema`\\.table_v\\d+ SET status = 30 WHERE 'all' = \\? AND status <= 25"). 323 WithArgs(sqlmock.AnyArg()). 324 WillReturnResult(sqlmock.NewResult(6, 2)) 325 s.mock.ExpectCommit() 326 327 err := s.cpdb.IgnoreErrorCheckpoint(context.Background(), "all") 328 c.Assert(err, IsNil) 329 } 330 331 func (s *cpSQLSuite) TestIgnoreOneErrorCheckpoint(c *C) { 332 s.mock.ExpectBegin() 333 s.mock. 334 ExpectExec("UPDATE `mock-schema`\\.engine_v\\d+ SET status = 30 WHERE table_name = \\? AND status <= 25"). 335 WithArgs("`db1`.`t2`"). 336 WillReturnResult(sqlmock.NewResult(5, 2)) 337 s.mock. 338 ExpectExec("UPDATE `mock-schema`\\.table_v\\d+ SET status = 30 WHERE table_name = \\? AND status <= 25"). 339 WithArgs("`db1`.`t2`"). 340 WillReturnResult(sqlmock.NewResult(6, 1)) 341 s.mock.ExpectCommit() 342 343 err := s.cpdb.IgnoreErrorCheckpoint(context.Background(), "`db1`.`t2`") 344 c.Assert(err, IsNil) 345 } 346 347 func (s *cpSQLSuite) TestDestroyAllErrorCheckpoints(c *C) { 348 s.mock.ExpectBegin() 349 s.mock. 350 ExpectQuery("SELECT (?s:.+)'all' = \\?"). 351 WithArgs(sqlmock.AnyArg()). 352 WillReturnRows( 353 sqlmock.NewRows([]string{"table_name", "__min__", "__max__"}). 354 AddRow("`db1`.`t2`", -1, 0), 355 ) 356 s.mock. 357 ExpectExec("DELETE FROM `mock-schema`\\.chunk_v\\d+ WHERE table_name IN .+ 'all' = \\?"). 358 WithArgs(sqlmock.AnyArg()). 359 WillReturnResult(sqlmock.NewResult(0, 5)) 360 s.mock. 361 ExpectExec("DELETE FROM `mock-schema`\\.engine_v\\d+ WHERE table_name IN .+ 'all' = \\?"). 362 WithArgs(sqlmock.AnyArg()). 363 WillReturnResult(sqlmock.NewResult(0, 3)) 364 s.mock. 365 ExpectExec("DELETE FROM `mock-schema`\\.table_v\\d+ WHERE 'all' = \\?"). 366 WithArgs(sqlmock.AnyArg()). 367 WillReturnResult(sqlmock.NewResult(0, 2)) 368 s.mock.ExpectCommit() 369 370 dtc, err := s.cpdb.DestroyErrorCheckpoint(context.Background(), "all") 371 c.Assert(err, IsNil) 372 c.Assert(dtc, DeepEquals, []checkpoints.DestroyedTableCheckpoint{{ 373 TableName: "`db1`.`t2`", 374 MinEngineID: -1, 375 MaxEngineID: 0, 376 }}) 377 } 378 379 func (s *cpSQLSuite) TestDestroyOneErrorCheckpoints(c *C) { 380 s.mock.ExpectBegin() 381 s.mock. 382 ExpectQuery("SELECT (?s:.+)table_name = \\?"). 383 WithArgs("`db1`.`t2`"). 384 WillReturnRows( 385 sqlmock.NewRows([]string{"table_name", "__min__", "__max__"}). 386 AddRow("`db1`.`t2`", -1, 0), 387 ) 388 s.mock. 389 ExpectExec("DELETE FROM `mock-schema`\\.chunk_v\\d+ WHERE .+table_name = \\?"). 390 WithArgs("`db1`.`t2`"). 391 WillReturnResult(sqlmock.NewResult(0, 4)) 392 s.mock. 393 ExpectExec("DELETE FROM `mock-schema`\\.engine_v\\d+ WHERE .+table_name = \\?"). 394 WithArgs("`db1`.`t2`"). 395 WillReturnResult(sqlmock.NewResult(0, 2)) 396 s.mock. 397 ExpectExec("DELETE FROM `mock-schema`\\.table_v\\d+ WHERE table_name = \\?"). 398 WithArgs("`db1`.`t2`"). 399 WillReturnResult(sqlmock.NewResult(0, 1)) 400 s.mock.ExpectCommit() 401 402 dtc, err := s.cpdb.DestroyErrorCheckpoint(context.Background(), "`db1`.`t2`") 403 c.Assert(err, IsNil) 404 c.Assert(dtc, DeepEquals, []checkpoints.DestroyedTableCheckpoint{{ 405 TableName: "`db1`.`t2`", 406 MinEngineID: -1, 407 MaxEngineID: 0, 408 }}) 409 } 410 411 func (s *cpSQLSuite) TestDump(c *C) { 412 ctx := context.Background() 413 t := time.Unix(1555555555, 0).UTC() 414 415 s.mock. 416 ExpectQuery("SELECT (?s:.+) FROM `mock-schema`\\.chunk_v\\d+"). 417 WillReturnRows( 418 sqlmock.NewRows([]string{ 419 "table_name", "path", "offset", "type", "compression", "sort_key", "file_size", "columns", 420 "pos", "end_offset", "prev_rowid_max", "rowid_max", 421 "kvc_bytes", "kvc_kvs", "kvc_checksum", 422 "create_time", "update_time", 423 }).AddRow( 424 "`db1`.`t2`", "/tmp/path/1.sql", 0, mydump.SourceTypeSQL, mydump.CompressionNone, "", 456, "[]", 425 55904, 102400, 681, 5000, 426 4491, 586, 486070148917, 427 t, t, 428 ), 429 ) 430 431 var csvBuilder strings.Builder 432 err := s.cpdb.DumpChunks(ctx, &csvBuilder) 433 c.Assert(err, IsNil) 434 c.Assert(csvBuilder.String(), Equals, 435 "table_name,path,offset,type,compression,sort_key,file_size,columns,pos,end_offset,prev_rowid_max,rowid_max,kvc_bytes,kvc_kvs,kvc_checksum,create_time,update_time\n"+ 436 "`db1`.`t2`,/tmp/path/1.sql,0,3,0,,456,[],55904,102400,681,5000,4491,586,486070148917,2019-04-18 02:45:55 +0000 UTC,2019-04-18 02:45:55 +0000 UTC\n", 437 ) 438 439 s.mock. 440 ExpectQuery("SELECT .+ FROM `mock-schema`\\.engine_v\\d+"). 441 WillReturnRows( 442 sqlmock.NewRows([]string{"table_name", "engine_id", "status", "create_time", "update_time"}). 443 AddRow("`db1`.`t2`", -1, 30, t, t). 444 AddRow("`db1`.`t2`", 0, 120, t, t), 445 ) 446 447 csvBuilder.Reset() 448 err = s.cpdb.DumpEngines(ctx, &csvBuilder) 449 c.Assert(err, IsNil) 450 c.Assert(csvBuilder.String(), Equals, 451 "table_name,engine_id,status,create_time,update_time\n"+ 452 "`db1`.`t2`,-1,30,2019-04-18 02:45:55 +0000 UTC,2019-04-18 02:45:55 +0000 UTC\n"+ 453 "`db1`.`t2`,0,120,2019-04-18 02:45:55 +0000 UTC,2019-04-18 02:45:55 +0000 UTC\n", 454 ) 455 456 s.mock. 457 ExpectQuery("SELECT .+ FROM `mock-schema`\\.table_v\\d+"). 458 WillReturnRows( 459 sqlmock.NewRows([]string{"task_id", "table_name", "hash", "status", "alloc_base", "create_time", "update_time"}). 460 AddRow(1555555555, "`db1`.`t2`", 0, 90, 132861, t, t), 461 ) 462 463 csvBuilder.Reset() 464 err = s.cpdb.DumpTables(ctx, &csvBuilder) 465 c.Assert(err, IsNil) 466 c.Assert(csvBuilder.String(), Equals, 467 "task_id,table_name,hash,status,alloc_base,create_time,update_time\n"+ 468 "1555555555,`db1`.`t2`,0,90,132861,2019-04-18 02:45:55 +0000 UTC,2019-04-18 02:45:55 +0000 UTC\n", 469 ) 470 } 471 472 func (s *cpSQLSuite) TestMoveCheckpoints(c *C) { 473 ctx := context.Background() 474 475 s.mock. 476 ExpectExec("CREATE SCHEMA IF NOT EXISTS `mock-schema\\.12345678\\.bak`"). 477 WillReturnResult(sqlmock.NewResult(1, 1)) 478 s.mock. 479 ExpectExec("RENAME TABLE `mock-schema`\\.chunk_v\\d+ TO `mock-schema\\.12345678\\.bak`\\.chunk_v\\d+"). 480 WillReturnResult(sqlmock.NewResult(0, 1)) 481 s.mock. 482 ExpectExec("RENAME TABLE `mock-schema`\\.engine_v\\d+ TO `mock-schema\\.12345678\\.bak`\\.engine_v\\d+"). 483 WillReturnResult(sqlmock.NewResult(0, 1)) 484 s.mock. 485 ExpectExec("RENAME TABLE `mock-schema`\\.table_v\\d+ TO `mock-schema\\.12345678\\.bak`\\.table_v\\d+"). 486 WillReturnResult(sqlmock.NewResult(0, 1)) 487 s.mock. 488 ExpectExec("RENAME TABLE `mock-schema`\\.task_v\\d+ TO `mock-schema\\.12345678\\.bak`\\.task_v\\d+"). 489 WillReturnResult(sqlmock.NewResult(0, 1)) 490 491 err := s.cpdb.MoveCheckpoints(ctx, 12345678) 492 c.Assert(err, IsNil) 493 }