github.com/matrixorigin/matrixone@v1.2.0/pkg/backup/backup_test.go (about) 1 // Copyright 2023 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package backup 16 17 import ( 18 "context" 19 "fmt" 20 "path" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/matrixorigin/matrixone/pkg/container/types" 26 "github.com/matrixorigin/matrixone/pkg/defines" 27 "github.com/matrixorigin/matrixone/pkg/fileservice" 28 "github.com/matrixorigin/matrixone/pkg/logservice" 29 pb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 30 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 31 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" 32 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 33 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/testutil" 34 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/handle" 35 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/testutils" 36 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/testutils/config" 37 "github.com/panjf2000/ants/v2" 38 "github.com/prashantv/gostub" 39 "github.com/stretchr/testify/assert" 40 ) 41 42 const ( 43 ModuleName = "Backup" 44 ) 45 46 func TestBackupData(t *testing.T) { 47 defer testutils.AfterTest(t)() 48 testutils.EnsureNoLeak(t) 49 ctx := context.Background() 50 51 opts := config.WithLongScanAndCKPOptsAndQuickGC(nil) 52 db := testutil.NewTestEngine(ctx, ModuleName, t, opts) 53 defer db.Close() 54 defer opts.Fs.Close() 55 56 schema := catalog.MockSchemaAll(13, 3) 57 schema.BlockMaxRows = 10 58 schema.ObjectMaxBlocks = 10 59 db.BindSchema(schema) 60 testutil.CreateRelation(t, db.DB, "db", schema, true) 61 62 totalRows := uint64(schema.BlockMaxRows * 30) 63 bat := catalog.MockBatch(schema, int(totalRows)) 64 defer bat.Close() 65 bats := bat.Split(100) 66 67 var wg sync.WaitGroup 68 pool, _ := ants.NewPool(80) 69 defer pool.Release() 70 71 start := time.Now() 72 for _, data := range bats { 73 wg.Add(1) 74 err := pool.Submit(testutil.AppendClosure(t, data, schema.Name, db.DB, &wg)) 75 assert.Nil(t, err) 76 } 77 wg.Wait() 78 t.Logf("Append %d rows takes: %s", totalRows, time.Since(start)) 79 { 80 txn, rel := testutil.GetDefaultRelation(t, db.DB, schema.Name) 81 testutil.CheckAllColRowsByScan(t, rel, int(totalRows), false) 82 assert.NoError(t, txn.Commit(context.Background())) 83 } 84 t.Log(db.Catalog.SimplePPString(common.PPL1)) 85 db.ForceLongCheckpoint() 86 87 dir := path.Join(db.Dir, "/local") 88 c := fileservice.Config{ 89 Name: defines.LocalFileServiceName, 90 Backend: "DISK", 91 DataDir: dir, 92 } 93 service, err := fileservice.NewFileService(ctx, c, nil) 94 assert.Nil(t, err) 95 defer service.Close() 96 for _, data := range bats { 97 txn, rel := db.GetRelation() 98 v := testutil.GetSingleSortKeyValue(data, schema, 2) 99 filter := handle.NewEQFilter(v) 100 err := rel.DeleteByFilter(context.Background(), filter) 101 assert.NoError(t, err) 102 assert.NoError(t, txn.Commit(context.Background())) 103 } 104 backupTime := time.Now().UTC() 105 currTs := types.BuildTS(backupTime.UnixNano(), 0) 106 locations := make([]string, 0) 107 locations = append(locations, backupTime.Format(time.DateTime)) 108 location, err := db.ForceCheckpointForBackup(ctx, currTs, 20*time.Second) 109 assert.Nil(t, err) 110 db.BGCheckpointRunner.DisableCheckpoint() 111 locations = append(locations, location) 112 checkpoints := db.BGCheckpointRunner.GetAllCheckpoints() 113 files := make(map[string]string, 0) 114 for _, candidate := range checkpoints { 115 if files[candidate.GetLocation().Name().String()] == "" { 116 var loc string 117 loc = candidate.GetLocation().String() 118 loc += ":" 119 loc += fmt.Sprintf("%d", candidate.GetVersion()) 120 files[candidate.GetLocation().Name().String()] = loc 121 } 122 } 123 for _, location := range files { 124 locations = append(locations, location) 125 } 126 err = execBackup(ctx, db.Opts.Fs, service, locations, 1, types.TS{}, "full") 127 assert.Nil(t, err) 128 db.Opts.Fs = service 129 db.Restart(ctx) 130 txn, rel := testutil.GetDefaultRelation(t, db.DB, schema.Name) 131 testutil.CheckAllColRowsByScan(t, rel, int(totalRows-100), true) 132 assert.NoError(t, txn.Commit(context.Background())) 133 } 134 135 func Test_saveTaeFilesList(t *testing.T) { 136 type args struct { 137 ctx context.Context 138 Fs fileservice.FileService 139 taeFiles []*taeFile 140 backupTime string 141 } 142 143 Fs := getTestFs(t, true) 144 Fs2 := getTestFs(t, true) 145 ts := time.Now().Format(time.DateTime) 146 tests := []struct { 147 name string 148 args args 149 wantErr assert.ErrorAssertionFunc 150 }{ 151 { 152 name: "t1", 153 args: args{ 154 ctx: context.Background(), 155 Fs: Fs, 156 taeFiles: nil, 157 backupTime: "", 158 }, 159 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 160 assert.Error(t, err) 161 return true 162 }, 163 }, 164 { 165 name: "t2", 166 args: args{ 167 ctx: context.Background(), 168 Fs: Fs, 169 taeFiles: nil, 170 backupTime: ts, 171 }, 172 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 173 assert.NoError(t, err) 174 //check file 175 check, err2 := readFileAndCheck(context.Background(), Fs, taeList) 176 assert.NoError(t, err2) 177 assert.Equal(t, check, []byte("")) 178 check, err2 = readFileAndCheck(context.Background(), Fs, taeSum) 179 assert.NoError(t, err2) 180 lines, err2 := fromCsvBytes(check) 181 assert.NoError(t, err2) 182 assert.Equal(t, lines[0][0], ts) 183 assert.Equal(t, lines[0][1], "0") 184 return false 185 }, 186 }, 187 { 188 name: "t3", 189 args: args{ 190 ctx: context.Background(), 191 Fs: nil, 192 taeFiles: nil, 193 backupTime: "", 194 }, 195 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 196 assert.Error(t, err) 197 return true 198 }, 199 }, 200 { 201 name: "t4", 202 args: args{ 203 ctx: context.Background(), 204 Fs: Fs2, 205 taeFiles: []*taeFile{ 206 { 207 path: "t1", 208 size: 1, 209 checksum: []byte{1}, 210 }, 211 }, 212 backupTime: ts, 213 }, 214 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 215 assert.NoError(t, err) 216 //check file 217 check, err2 := readFileAndCheck(context.Background(), Fs2, taeList) 218 assert.NoError(t, err2) 219 lines, err2 := fromCsvBytes(check) 220 assert.NoError(t, err2) 221 assert.Equal(t, lines[0][0], "t1") 222 assert.Equal(t, lines[0][1], "1") 223 assert.Equal(t, lines[0][2], hexStr([]byte{1})) 224 check, err2 = readFileAndCheck(context.Background(), Fs2, taeSum) 225 assert.NoError(t, err2) 226 lines, err2 = fromCsvBytes(check) 227 assert.NoError(t, err2) 228 assert.Equal(t, lines[0][0], ts) 229 assert.Equal(t, lines[0][1], "1") 230 return false 231 }, 232 }, 233 } 234 for _, tt := range tests { 235 t.Run(tt.name, func(t *testing.T) { 236 tt.wantErr(t, saveTaeFilesList(tt.args.ctx, tt.args.Fs, tt.args.taeFiles, tt.args.backupTime, tt.args.backupTime, ""), fmt.Sprintf("saveTaeFilesList(%v, %v, %v, %v)", tt.args.ctx, tt.args.Fs, tt.args.taeFiles, tt.args.backupTime)) 237 }) 238 } 239 } 240 241 func Test_saveMetas(t *testing.T) { 242 type args struct { 243 ctx context.Context 244 cfg *Config 245 } 246 247 Fs := getTestFs(t, true) 248 249 tests := []struct { 250 name string 251 args args 252 wantErr assert.ErrorAssertionFunc 253 }{ 254 { 255 name: "t1", 256 args: args{ 257 ctx: context.Background(), 258 cfg: nil, 259 }, 260 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 261 assert.Error(t, err) 262 return false 263 }, 264 }, 265 { 266 name: "t2", 267 args: args{ 268 ctx: context.Background(), 269 cfg: &Config{ 270 Timestamp: types.TS{}, 271 GeneralDir: Fs, 272 SharedFs: nil, 273 TaeDir: nil, 274 HAkeeper: nil, 275 Metas: &Metas{ 276 metas: []*Meta{ 277 { 278 Typ: TypeVersion, 279 Version: "version", 280 }, 281 { 282 Typ: TypeBuildinfo, 283 Buildinfo: "build_info", 284 }, 285 { 286 Typ: TypeLaunchconfig, 287 LaunchConfigFile: "launch_conf", 288 }, 289 { 290 Typ: TypeLaunchconfig, 291 SubTyp: CnConfig, 292 LaunchConfigFile: "launch_cn_conf", 293 }, 294 }, 295 }, 296 }, 297 }, 298 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 299 assert.NoError(t, err) 300 check, err2 := readFileAndCheck(context.Background(), Fs, moMeta) 301 assert.NoError(t, err2) 302 lines, err2 := fromCsvBytes(check) 303 assert.NoError(t, err2) 304 assert.Equal(t, lines[0][0], "version") 305 assert.Equal(t, lines[0][1], "version") 306 assert.Equal(t, lines[1][0], "buildinfo") 307 assert.Equal(t, lines[1][1], "build_info") 308 assert.Equal(t, lines[2][0], "launchconfig") 309 assert.Equal(t, lines[2][1], "") 310 assert.Equal(t, lines[2][2], "launch_conf") 311 assert.Equal(t, lines[3][0], "launchconfig") 312 assert.Equal(t, lines[3][1], CnConfig) 313 assert.Equal(t, lines[3][2], "launch_cn_conf") 314 return false 315 }, 316 }, 317 } 318 for _, tt := range tests { 319 t.Run(tt.name, func(t *testing.T) { 320 tt.wantErr(t, saveMetas(tt.args.ctx, tt.args.cfg), fmt.Sprintf("saveMetas(%v, %v)", tt.args.ctx, tt.args.cfg)) 321 }) 322 } 323 } 324 325 func Test_backupConfigFile(t *testing.T) { 326 type args struct { 327 ctx context.Context 328 typ string 329 configPath string 330 cfg *Config 331 } 332 333 Fs := getTestFs(t, true) 334 file := getTempFile(t, "", "t1", "test_t1") 335 336 tests := []struct { 337 name string 338 args args 339 wantErr assert.ErrorAssertionFunc 340 }{ 341 { 342 name: "t1", 343 args: args{ 344 ctx: context.Background(), 345 typ: "", 346 configPath: "", 347 cfg: nil, 348 }, 349 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 350 assert.Error(t, err) 351 return true 352 }, 353 }, 354 { 355 name: "t2", 356 args: args{ 357 ctx: context.Background(), 358 typ: CnConfig, 359 configPath: file.Name(), 360 cfg: &Config{ 361 Timestamp: types.TS{}, 362 GeneralDir: Fs, 363 SharedFs: nil, 364 TaeDir: nil, 365 HAkeeper: nil, 366 Metas: &Metas{}, 367 }, 368 }, 369 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 370 assert.NoError(t, err) 371 list, err2 := Fs.List(context.Background(), configDir) 372 assert.NoError(t, err2) 373 var configFile string 374 for _, entry := range list { 375 if entry.IsDir { 376 continue 377 } 378 configFile = entry.Name 379 break 380 } 381 check, err2 := readFileAndCheck(context.Background(), Fs, configDir+"/"+configFile) 382 assert.NoError(t, err2) 383 assert.Equal(t, check, []byte("test_t1")) 384 return true 385 }, 386 }, 387 } 388 for _, tt := range tests { 389 t.Run(tt.name, func(t *testing.T) { 390 tt.wantErr(t, backupConfigFile(tt.args.ctx, tt.args.typ, tt.args.configPath, tt.args.cfg), fmt.Sprintf("backupConfigFile(%v, %v, %v, %v)", tt.args.ctx, tt.args.typ, tt.args.configPath, tt.args.cfg)) 391 }) 392 } 393 } 394 395 var _ logservice.CNHAKeeperClient = new(dumpHakeeper) 396 397 const ( 398 backupData = "backup_data" 399 ) 400 401 type dumpHakeeper struct { 402 } 403 404 func (d *dumpHakeeper) Close() error { 405 //TODO implement me 406 panic("implement me") 407 } 408 409 func (d *dumpHakeeper) AllocateID(ctx context.Context) (uint64, error) { 410 //TODO implement me 411 panic("implement me") 412 } 413 414 func (d *dumpHakeeper) AllocateIDByKey(ctx context.Context, key string) (uint64, error) { 415 //TODO implement me 416 panic("implement me") 417 } 418 419 func (d *dumpHakeeper) AllocateIDByKeyWithBatch(ctx context.Context, key string, batch uint64) (uint64, error) { 420 //TODO implement me 421 panic("implement me") 422 } 423 424 func (d *dumpHakeeper) GetClusterDetails(ctx context.Context) (pb.ClusterDetails, error) { 425 //TODO implement me 426 panic("implement me") 427 } 428 429 func (d *dumpHakeeper) GetClusterState(ctx context.Context) (pb.CheckerState, error) { 430 //TODO implement me 431 panic("implement me") 432 } 433 434 func (d *dumpHakeeper) GetBackupData(ctx context.Context) ([]byte, error) { 435 return []byte(backupData), nil 436 } 437 438 func (d *dumpHakeeper) SendCNHeartbeat(ctx context.Context, hb pb.CNStoreHeartbeat) (pb.CommandBatch, error) { 439 //TODO implement me 440 panic("implement me") 441 } 442 443 func Test_backupHakeeper(t *testing.T) { 444 type args struct { 445 ctx context.Context 446 config *Config 447 } 448 etlFs := getTestFs(t, true) 449 taeFs := getTestFs(t, false) 450 451 tests := []struct { 452 name string 453 args args 454 wantErr assert.ErrorAssertionFunc 455 }{ 456 { 457 name: "t1", 458 args: args{ 459 ctx: context.Background(), 460 config: nil, 461 }, 462 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 463 assert.Error(t, err) 464 return false 465 }, 466 }, 467 { 468 name: "t2", 469 args: args{ 470 ctx: context.Background(), 471 config: &Config{ 472 Timestamp: types.TS{}, 473 GeneralDir: nil, 474 SharedFs: nil, 475 TaeDir: nil, 476 HAkeeper: nil, 477 Metas: &Metas{}, 478 }, 479 }, 480 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 481 assert.Error(t, err) 482 return false 483 }, 484 }, 485 { 486 name: "t3", 487 args: args{ 488 ctx: context.Background(), 489 config: &Config{ 490 Timestamp: types.TS{}, 491 GeneralDir: etlFs, 492 SharedFs: nil, 493 TaeDir: etlFs, 494 HAkeeper: &dumpHakeeper{}, 495 Metas: &Metas{}, 496 }, 497 }, 498 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 499 assert.NoError(t, err) 500 check, err2 := readFileAndCheck(context.Background(), etlFs, hakeeperDir+"/"+HakeeperFile) 501 assert.NoError(t, err2) 502 assert.Equal(t, check, []byte(backupData)) 503 return false 504 }, 505 }, 506 { 507 name: "t4", 508 args: args{ 509 ctx: context.Background(), 510 config: &Config{ 511 Timestamp: types.TS{}, 512 GeneralDir: etlFs, 513 SharedFs: nil, 514 TaeDir: taeFs, 515 HAkeeper: &dumpHakeeper{}, 516 Metas: &Metas{}, 517 }, 518 }, 519 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 520 assert.NoError(t, err) 521 check, err2 := readFileAndCheck(context.Background(), taeFs, hakeeperDir+"/"+HakeeperFile) 522 assert.NoError(t, err2) 523 assert.Equal(t, check, []byte(backupData)) 524 return false 525 }, 526 }, 527 } 528 for _, tt := range tests { 529 t.Run(tt.name, func(t *testing.T) { 530 tt.wantErr(t, backupHakeeper(tt.args.ctx, tt.args.config), fmt.Sprintf("backupHakeeper(%v, %v)", tt.args.ctx, tt.args.config)) 531 }) 532 } 533 } 534 535 func TestBackup(t *testing.T) { 536 type args struct { 537 ctx context.Context 538 bs *tree.BackupStart 539 cfg *Config 540 } 541 542 stubs := gostub.StubFunc(&backupTae, nil) 543 defer stubs.Reset() 544 545 tDir := getTempDir(t, "test") 546 tf1 := getTempFile(t, "", "t1", "test_t1") 547 548 bs := &tree.BackupStart{ 549 IsS3: false, 550 Dir: tDir, 551 Parallelism: "10", 552 } 553 554 //backup configs 555 SaveLaunchConfigPath(CnConfig, []string{tf1.Name()}) 556 557 tests := []struct { 558 name string 559 args args 560 wantErr assert.ErrorAssertionFunc 561 }{ 562 { 563 name: "t1", 564 args: args{ 565 ctx: nil, 566 bs: bs, 567 cfg: nil, 568 }, 569 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 570 assert.Error(t, err) 571 return false 572 }, 573 }, 574 { 575 name: "t2", 576 args: args{ 577 ctx: context.Background(), 578 bs: bs, 579 cfg: &Config{ 580 Timestamp: types.TS{}, 581 GeneralDir: nil, 582 SharedFs: nil, 583 TaeDir: nil, 584 HAkeeper: &dumpHakeeper{}, 585 Metas: NewMetas(), 586 }, 587 }, 588 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 589 cfg := i[0].(*Config) 590 assert.NoError(t, err) 591 assert.NotNil(t, cfg) 592 593 //checkup config files 594 list, err2 := cfg.GeneralDir.List(context.Background(), configDir) 595 assert.NoError(t, err2) 596 var configFile string 597 for _, entry := range list { 598 if entry.IsDir { 599 continue 600 } 601 configFile = entry.Name 602 break 603 } 604 check, err2 := readFileAndCheck(context.Background(), cfg.GeneralDir, configDir+"/"+configFile) 605 assert.NoError(t, err2) 606 assert.Equal(t, check, []byte("test_t1")) 607 608 //check hakeeper files 609 check, err2 = readFileAndCheck(context.Background(), cfg.TaeDir, hakeeperDir+"/"+HakeeperFile) 610 assert.NoError(t, err2) 611 assert.Equal(t, check, []byte(backupData)) 612 613 //check metas 614 check, err2 = readFileAndCheck(context.Background(), cfg.GeneralDir, moMeta) 615 assert.NoError(t, err2) 616 lines, err2 := fromCsvBytes(check) 617 assert.NoError(t, err2) 618 assert.Equal(t, lines[0][0], "version") 619 assert.Equal(t, lines[0][1], Version) 620 assert.Equal(t, lines[1][0], "buildinfo") 621 assert.Equal(t, lines[1][1], buildInfo()) 622 assert.Equal(t, lines[2][0], "launchconfig") 623 assert.Equal(t, lines[2][1], CnConfig) 624 assert.Equal(t, lines[2][2], cfg.Metas.metas[2].LaunchConfigFile) 625 return false 626 }, 627 }, 628 } 629 for _, tt := range tests { 630 t.Run(tt.name, func(t *testing.T) { 631 tt.wantErr(t, Backup(tt.args.ctx, tt.args.bs, tt.args.cfg), tt.args.cfg, fmt.Sprintf("Backup(%v, %v, %v)", tt.args.ctx, tt.args.bs, tt.args.cfg)) 632 }) 633 } 634 }