github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/db/testutil/engine.go (about) 1 // Copyright 2021 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 testutil 16 17 import ( 18 "context" 19 "fmt" 20 "strconv" 21 "strings" 22 "testing" 23 "time" 24 25 pkgcatalog "github.com/matrixorigin/matrixone/pkg/catalog" 26 "github.com/matrixorigin/matrixone/pkg/container/batch" 27 "github.com/matrixorigin/matrixone/pkg/container/types" 28 "github.com/matrixorigin/matrixone/pkg/fileservice" 29 "github.com/matrixorigin/matrixone/pkg/logutil" 30 "github.com/matrixorigin/matrixone/pkg/objectio" 31 "github.com/matrixorigin/matrixone/pkg/pb/api" 32 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/blockio" 33 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" 34 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 35 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers" 36 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db" 37 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/checkpoint" 38 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/data" 39 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/handle" 40 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 41 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logtail" 42 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/options" 43 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/testutils" 44 "github.com/stretchr/testify/assert" 45 "github.com/stretchr/testify/require" 46 ) 47 48 const ( 49 DefaultTestDB = "db" 50 ) 51 52 type CtxOldVersion struct{} 53 54 type TestEngine struct { 55 *db.DB 56 t *testing.T 57 schema *catalog.Schema 58 tenantID uint32 // for almost tests, userID and roleID is not important 59 } 60 61 func NewTestEngineWithDir( 62 ctx context.Context, 63 dir string, 64 t *testing.T, 65 opts *options.Options, 66 ) *TestEngine { 67 blockio.Start() 68 db := InitTestDBWithDir(ctx, dir, t, opts) 69 return &TestEngine{ 70 DB: db, 71 t: t, 72 } 73 } 74 75 func NewTestEngine( 76 ctx context.Context, 77 moduleName string, 78 t *testing.T, 79 opts *options.Options, 80 ) *TestEngine { 81 blockio.Start() 82 db := InitTestDB(ctx, moduleName, t, opts) 83 return &TestEngine{ 84 DB: db, 85 t: t, 86 } 87 } 88 89 func (e *TestEngine) BindSchema(schema *catalog.Schema) { e.schema = schema } 90 91 func (e *TestEngine) BindTenantID(tenantID uint32) { e.tenantID = tenantID } 92 93 func (e *TestEngine) Restart(ctx context.Context) { 94 _ = e.DB.Close() 95 var err error 96 e.DB, err = db.Open(ctx, e.Dir, e.Opts) 97 // only ut executes this checker 98 e.DB.DiskCleaner.GetCleaner().AddChecker( 99 func(item any) bool { 100 min := e.DB.TxnMgr.MinTSForTest() 101 ckp := item.(*checkpoint.CheckpointEntry) 102 //logutil.Infof("min: %v, checkpoint: %v", min.ToString(), checkpoint.GetStart().ToString()) 103 end := ckp.GetEnd() 104 return !end.GreaterEq(&min) 105 }) 106 assert.NoError(e.t, err) 107 } 108 func (e *TestEngine) RestartDisableGC(ctx context.Context) { 109 _ = e.DB.Close() 110 var err error 111 e.Opts.GCCfg.GCTTL = 100 * time.Second 112 e.DB, err = db.Open(ctx, e.Dir, e.Opts) 113 // only ut executes this checker 114 e.DB.DiskCleaner.GetCleaner().AddChecker( 115 func(item any) bool { 116 min := e.DB.TxnMgr.MinTSForTest() 117 ckp := item.(*checkpoint.CheckpointEntry) 118 //logutil.Infof("min: %v, checkpoint: %v", min.ToString(), checkpoint.GetStart().ToString()) 119 end := ckp.GetEnd() 120 return !end.GreaterEq(&min) 121 }) 122 assert.NoError(e.t, err) 123 } 124 125 func (e *TestEngine) Close() error { 126 err := e.DB.Close() 127 blockio.Stop() 128 blockio.ResetPipeline() 129 return err 130 } 131 132 func (e *TestEngine) CreateRelAndAppend(bat *containers.Batch, createDB bool) (handle.Database, handle.Relation) { 133 clonedSchema := e.schema.Clone() 134 return CreateRelationAndAppend(e.t, e.tenantID, e.DB, DefaultTestDB, clonedSchema, bat, createDB) 135 } 136 137 func (e *TestEngine) CheckRowsByScan(exp int, applyDelete bool) { 138 txn, rel := e.GetRelation() 139 CheckAllColRowsByScan(e.t, rel, exp, applyDelete) 140 assert.NoError(e.t, txn.Commit(context.Background())) 141 } 142 func (e *TestEngine) ForceCheckpoint() { 143 err := e.BGCheckpointRunner.ForceFlushWithInterval(e.TxnMgr.Now(), context.Background(), time.Second*2, time.Millisecond*10) 144 assert.NoError(e.t, err) 145 err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now(), false) 146 assert.NoError(e.t, err) 147 } 148 149 func (e *TestEngine) ForceLongCheckpoint() { 150 err := e.BGCheckpointRunner.ForceFlush(e.TxnMgr.Now(), context.Background(), 20*time.Second) 151 assert.NoError(e.t, err) 152 err = e.BGCheckpointRunner.ForceIncrementalCheckpoint(e.TxnMgr.Now(), false) 153 assert.NoError(e.t, err) 154 } 155 156 func (e *TestEngine) DropRelation(t *testing.T) { 157 txn, err := e.StartTxn(nil) 158 assert.NoError(t, err) 159 db, err := txn.GetDatabase(DefaultTestDB) 160 assert.NoError(t, err) 161 _, err = db.DropRelationByName(e.schema.Name) 162 assert.NoError(t, err) 163 assert.NoError(t, txn.Commit(context.Background())) 164 } 165 func (e *TestEngine) GetRelation() (txn txnif.AsyncTxn, rel handle.Relation) { 166 return GetRelation(e.t, e.tenantID, e.DB, DefaultTestDB, e.schema.Name) 167 } 168 func (e *TestEngine) GetRelationWithTxn(txn txnif.AsyncTxn) (rel handle.Relation) { 169 return GetRelationWithTxn(e.t, txn, DefaultTestDB, e.schema.Name) 170 } 171 172 func (e *TestEngine) CompactBlocks(skipConflict bool) { 173 CompactBlocks(e.t, e.tenantID, e.DB, DefaultTestDB, e.schema, skipConflict) 174 } 175 176 func (e *TestEngine) MergeBlocks(skipConflict bool) { 177 MergeBlocks(e.t, e.tenantID, e.DB, DefaultTestDB, e.schema, skipConflict) 178 } 179 180 func (e *TestEngine) GetDB(name string) (txn txnif.AsyncTxn, db handle.Database) { 181 txn, err := e.DB.StartTxn(nil) 182 txn.BindAccessInfo(e.tenantID, 0, 0) 183 assert.NoError(e.t, err) 184 db, err = txn.GetDatabase(name) 185 assert.NoError(e.t, err) 186 return 187 } 188 189 func (e *TestEngine) GetTestDB() (txn txnif.AsyncTxn, db handle.Database) { 190 return e.GetDB(DefaultTestDB) 191 } 192 193 func (e *TestEngine) DoAppend(bat *containers.Batch) { 194 txn, rel := e.GetRelation() 195 err := rel.Append(context.Background(), bat) 196 assert.NoError(e.t, err) 197 assert.NoError(e.t, txn.Commit(context.Background())) 198 } 199 200 func (e *TestEngine) DoAppendWithTxn(bat *containers.Batch, txn txnif.AsyncTxn, skipConflict bool) (err error) { 201 rel := e.GetRelationWithTxn(txn) 202 err = rel.Append(context.Background(), bat) 203 if !skipConflict { 204 assert.NoError(e.t, err) 205 } 206 return 207 } 208 209 func (e *TestEngine) TryAppend(bat *containers.Batch) { 210 txn, err := e.DB.StartTxn(nil) 211 txn.BindAccessInfo(e.tenantID, 0, 0) 212 assert.NoError(e.t, err) 213 db, err := txn.GetDatabase(DefaultTestDB) 214 assert.NoError(e.t, err) 215 rel, err := db.GetRelationByName(e.schema.Name) 216 if err != nil { 217 _ = txn.Rollback(context.Background()) 218 return 219 } 220 221 err = rel.Append(context.Background(), bat) 222 if err != nil { 223 _ = txn.Rollback(context.Background()) 224 return 225 } 226 _ = txn.Commit(context.Background()) 227 } 228 func (e *TestEngine) DeleteAll(skipConflict bool) error { 229 txn, rel := e.GetRelation() 230 schema := rel.GetMeta().(*catalog.TableEntry).GetLastestSchemaLocked() 231 pkName := schema.GetPrimaryKey().Name 232 it := rel.MakeObjectIt() 233 for it.Valid() { 234 blk := it.GetObject() 235 defer blk.Close() 236 blkCnt := uint16(blk.BlkCnt()) 237 for i := uint16(0); i < blkCnt; i++ { 238 view, err := blk.GetColumnDataByName(context.Background(), i, catalog.PhyAddrColumnName, common.DefaultAllocator) 239 assert.NoError(e.t, err) 240 defer view.Close() 241 view.ApplyDeletes() 242 pkView, err := blk.GetColumnDataByName(context.Background(), i, pkName, common.DefaultAllocator) 243 assert.NoError(e.t, err) 244 defer pkView.Close() 245 pkView.ApplyDeletes() 246 err = rel.DeleteByPhyAddrKeys(view.GetData(), pkView.GetData()) 247 assert.NoError(e.t, err) 248 } 249 it.Next() 250 } 251 // CheckAllColRowsByScan(e.t, rel, 0, true) 252 err := txn.Commit(context.Background()) 253 if !skipConflict { 254 CheckAllColRowsByScan(e.t, rel, 0, true) 255 assert.NoError(e.t, err) 256 } 257 return err 258 } 259 260 func (e *TestEngine) Truncate() { 261 txn, db := e.GetTestDB() 262 _, err := db.TruncateByName(e.schema.Name) 263 assert.NoError(e.t, err) 264 assert.NoError(e.t, txn.Commit(context.Background())) 265 } 266 func (e *TestEngine) GlobalCheckpoint( 267 endTs types.TS, 268 versionInterval time.Duration, 269 enableAndCleanBGCheckpoint bool, 270 ) error { 271 if enableAndCleanBGCheckpoint { 272 e.DB.BGCheckpointRunner.DisableCheckpoint() 273 defer e.DB.BGCheckpointRunner.EnableCheckpoint() 274 e.DB.BGCheckpointRunner.CleanPenddingCheckpoint() 275 } 276 if e.DB.BGCheckpointRunner.GetPenddingIncrementalCount() == 0 { 277 testutils.WaitExpect(4000, func() bool { 278 flushed := e.DB.BGCheckpointRunner.IsAllChangesFlushed(types.TS{}, endTs, false) 279 return flushed 280 }) 281 flushed := e.DB.BGCheckpointRunner.IsAllChangesFlushed(types.TS{}, endTs, true) 282 assert.True(e.t, flushed) 283 } 284 err := e.DB.BGCheckpointRunner.ForceGlobalCheckpoint(endTs, versionInterval) 285 assert.NoError(e.t, err) 286 return nil 287 } 288 289 func (e *TestEngine) IncrementalCheckpoint( 290 end types.TS, 291 enableAndCleanBGCheckpoint bool, 292 waitFlush bool, 293 truncate bool, 294 ) error { 295 if enableAndCleanBGCheckpoint { 296 e.DB.BGCheckpointRunner.DisableCheckpoint() 297 defer e.DB.BGCheckpointRunner.EnableCheckpoint() 298 e.DB.BGCheckpointRunner.CleanPenddingCheckpoint() 299 } 300 if waitFlush { 301 testutils.WaitExpect(4000, func() bool { 302 flushed := e.DB.BGCheckpointRunner.IsAllChangesFlushed(types.TS{}, end, false) 303 return flushed 304 }) 305 flushed := e.DB.BGCheckpointRunner.IsAllChangesFlushed(types.TS{}, end, true) 306 assert.True(e.t, flushed) 307 } 308 err := e.DB.BGCheckpointRunner.ForceIncrementalCheckpoint(end, false) 309 assert.NoError(e.t, err) 310 if truncate { 311 lsn := e.DB.BGCheckpointRunner.MaxLSNInRange(end) 312 entry, err := e.DB.Wal.RangeCheckpoint(1, lsn) 313 assert.NoError(e.t, err) 314 assert.NoError(e.t, entry.WaitDone()) 315 testutils.WaitExpect(1000, func() bool { 316 return e.Runtime.Scheduler.GetPenddingLSNCnt() == 0 317 }) 318 } 319 return nil 320 } 321 322 func (e *TestEngine) TryDeleteByDeltaloc(vals []any) (ok bool, err error) { 323 txn, err := e.StartTxn(nil) 324 assert.NoError(e.t, err) 325 ok, err = e.TryDeleteByDeltalocWithTxn(vals, txn) 326 if ok { 327 assert.NoError(e.t, txn.Commit(context.Background())) 328 } else { 329 assert.NoError(e.t, txn.Rollback(context.Background())) 330 } 331 return 332 } 333 334 func (e *TestEngine) TryDeleteByDeltalocWithTxn(vals []any, txn txnif.AsyncTxn) (ok bool, err error) { 335 rel := e.GetRelationWithTxn(txn) 336 337 idOffsetsMap := make(map[common.ID][]uint32) 338 for _, val := range vals { 339 filter := handle.NewEQFilter(val) 340 id, offset, err := rel.GetByFilter(context.Background(), filter) 341 assert.NoError(e.t, err) 342 offsets, ok := idOffsetsMap[*id] 343 if !ok { 344 offsets = make([]uint32, 0) 345 } 346 offsets = append(offsets, offset) 347 idOffsetsMap[*id] = offsets 348 } 349 350 for id, offsets := range idOffsetsMap { 351 obj, err := rel.GetMeta().(*catalog.TableEntry).GetObjectByID(id.ObjectID()) 352 assert.NoError(e.t, err) 353 _, blkOffset := id.BlockID.Offsets() 354 deltaLoc, err := MockCNDeleteInS3(e.Runtime.Fs, obj.GetObjectData(), blkOffset, e.schema, txn, offsets) 355 assert.NoError(e.t, err) 356 ok, err = rel.TryDeleteByDeltaloc(&id, deltaLoc) 357 assert.NoError(e.t, err) 358 if !ok { 359 return ok, err 360 } 361 } 362 ok = true 363 return 364 } 365 366 func InitTestDBWithDir( 367 ctx context.Context, 368 dir string, 369 t *testing.T, 370 opts *options.Options, 371 ) *db.DB { 372 db, _ := db.Open(ctx, dir, opts) 373 // only ut executes this checker 374 db.DiskCleaner.GetCleaner().AddChecker( 375 func(item any) bool { 376 min := db.TxnMgr.MinTSForTest() 377 ckp := item.(*checkpoint.CheckpointEntry) 378 //logutil.Infof("min: %v, checkpoint: %v", min.ToString(), checkpoint.GetStart().ToString()) 379 end := ckp.GetEnd() 380 return !end.GreaterEq(&min) 381 }) 382 return db 383 } 384 385 func InitTestDB( 386 ctx context.Context, 387 moduleName string, 388 t *testing.T, 389 opts *options.Options, 390 ) *db.DB { 391 dir := testutils.InitTestEnv(moduleName, t) 392 db, _ := db.Open(ctx, dir, opts) 393 // only ut executes this checker 394 db.DiskCleaner.GetCleaner().AddChecker( 395 func(item any) bool { 396 min := db.TxnMgr.MinTSForTest() 397 ckp := item.(*checkpoint.CheckpointEntry) 398 //logutil.Infof("min: %v, checkpoint: %v", min.ToString(), checkpoint.GetStart().ToString()) 399 end := ckp.GetEnd() 400 return !end.GreaterEq(&min) 401 }) 402 return db 403 } 404 405 func writeIncrementalCheckpoint( 406 t *testing.T, 407 start, end types.TS, 408 c *catalog.Catalog, 409 checkpointBlockRows int, 410 checkpointSize int, 411 fs fileservice.FileService, 412 ) (objectio.Location, objectio.Location) { 413 factory := logtail.IncrementalCheckpointDataFactory(start, end, false, false) 414 data, err := factory(c) 415 assert.NoError(t, err) 416 defer data.Close() 417 cnLocation, tnLocation, _, err := data.WriteTo(fs, checkpointBlockRows, checkpointSize) 418 assert.NoError(t, err) 419 return cnLocation, tnLocation 420 } 421 422 func tnReadCheckpoint(t *testing.T, location objectio.Location, fs fileservice.FileService) *logtail.CheckpointData { 423 reader, err := blockio.NewObjectReader(fs, location) 424 assert.NoError(t, err) 425 data := logtail.NewCheckpointData(common.CheckpointAllocator) 426 err = data.ReadFrom( 427 context.Background(), 428 logtail.CheckpointCurrentVersion, 429 location, 430 reader, 431 fs, 432 ) 433 assert.NoError(t, err) 434 return data 435 } 436 func cnReadCheckpoint(t *testing.T, tid uint64, location objectio.Location, fs fileservice.FileService) (ins, del, cnIns, segDel *api.Batch, cb []func()) { 437 ins, del, cnIns, segDel, cb = cnReadCheckpointWithVersion(t, tid, location, fs, logtail.CheckpointCurrentVersion) 438 return 439 } 440 441 func ReadSnapshotCheckpoint(t *testing.T, tid uint64, location objectio.Location, fs fileservice.FileService) (ins, del, cnIns, segDel *api.Batch, cb []func()) { 442 ins, del, cnIns, segDel, cb = cnReadCheckpointWithVersion(t, tid, location, fs, logtail.CheckpointCurrentVersion) 443 return 444 } 445 446 func cnReadCheckpointWithVersion(t *testing.T, tid uint64, location objectio.Location, fs fileservice.FileService, ver uint32) (ins, del, cnIns, segDel *api.Batch, cb []func()) { 447 locs := make([]string, 0) 448 locs = append(locs, location.String()) 449 locs = append(locs, strconv.Itoa(int(ver))) 450 locations := strings.Join(locs, ";") 451 entries, cb, err := logtail.LoadCheckpointEntries( 452 context.Background(), 453 locations, 454 tid, 455 "tbl", 456 0, 457 "db", 458 common.CheckpointAllocator, 459 fs, 460 ) 461 assert.NoError(t, err) 462 for i := len(entries) - 1; i >= 0; i-- { 463 e := entries[i] 464 if e.TableName == fmt.Sprintf("_%d_obj", tid) { 465 segDel = e.Bat 466 } else if e.EntryType == api.Entry_Delete { 467 del = e.Bat 468 if tid != pkgcatalog.MO_DATABASE_ID && tid != pkgcatalog.MO_TABLES_ID && tid != pkgcatalog.MO_COLUMNS_ID { 469 cnIns = entries[i-1].Bat 470 i-- 471 } 472 } else { 473 ins = e.Bat 474 } 475 } 476 for _, c := range cb { 477 c() 478 } 479 return 480 } 481 482 func checkTNCheckpointData(ctx context.Context, t *testing.T, data *logtail.CheckpointData, 483 start, end types.TS, c *catalog.Catalog) { 484 factory := logtail.IncrementalCheckpointDataFactory(start, end, false, false) 485 data2, err := factory(c) 486 assert.NoError(t, err) 487 defer data2.Close() 488 489 bats1 := data.GetBatches() 490 bats2 := data2.GetBatches() 491 assert.Equal(t, len(bats1), len(bats2)) 492 for i, bat := range bats1 { 493 // skip metabatch 494 if i == 0 { 495 continue 496 } 497 bat2 := bats2[i] 498 // t.Logf("check bat %d", i) 499 isBatchEqual(ctx, t, bat, bat2) 500 } 501 } 502 503 func getBatchLength(bat *containers.Batch) int { 504 length := 0 505 for _, vec := range bat.Vecs { 506 if vec.Length() > length { 507 length = vec.Length() 508 } 509 } 510 return length 511 } 512 513 func isBatchEqual(ctx context.Context, t *testing.T, bat1, bat2 *containers.Batch) { 514 oldver := -1 515 if ver := ctx.Value(CtxOldVersion{}); ver != nil { 516 oldver = ver.(int) 517 } 518 require.Equal(t, getBatchLength(bat1), getBatchLength(bat2)) 519 require.Equal(t, len(bat1.Vecs), len(bat2.Vecs)) 520 for i := 0; i < getBatchLength(bat1); i++ { 521 for j, vec1 := range bat1.Vecs { 522 vec2 := bat2.Vecs[j] 523 if vec1.Length() == 0 || vec2.Length() == 0 { 524 // for commitTS and rowid in checkpoint 525 // logutil.Warnf("empty vec attr %v", bat1.Attrs[j]) 526 continue 527 } 528 if oldver >= 0 && oldver <= int(logtail.CheckpointVersion5) && // read old version checkpoint 529 logtail.CheckpointCurrentVersion > logtail.CheckpointVersion5 && // check on new version 530 bat1.Attrs[j] == pkgcatalog.BlockMeta_MemTruncPoint { 531 // memTruncatePoint vector is committs vec in old checkpoint 532 // it can't be the same with newly collected on new version checkpoint, just skip it 533 logutil.Infof("isBatchEqual skip attr %v for ver.%d on ver.%d", bat1.Attrs[j], oldver, logtail.CheckpointCurrentVersion) 534 continue 535 } 536 // t.Logf("attr %v, row %d", bat1.Attrs[j], i) 537 require.Equal(t, vec1.Get(i), vec2.Get(i), "name is \"%v\"", bat1.Attrs[j]) 538 } 539 } 540 } 541 542 func isProtoTNBatchEqual(ctx context.Context, t *testing.T, bat1 *api.Batch, bat2 *containers.Batch) { 543 if bat1 == nil { 544 if bat2 == nil { 545 return 546 } 547 assert.Equal(t, 0, getBatchLength(bat2)) 548 } else { 549 moIns, err := batch.ProtoBatchToBatch(bat1) 550 assert.NoError(t, err) 551 tnIns := containers.ToTNBatch(moIns, common.DefaultAllocator) 552 isBatchEqual(ctx, t, tnIns, bat2) 553 } 554 } 555 556 func checkCNCheckpointData(ctx context.Context, t *testing.T, tid uint64, ins, del, cnIns, segDel *api.Batch, start, end types.TS, c *catalog.Catalog) { 557 if tid == pkgcatalog.MO_DATABASE_ID { 558 checkMODatabase(ctx, t, ins, del, cnIns, segDel, start, end, c) 559 } else if tid == pkgcatalog.MO_TABLES_ID { 560 checkMOTables(ctx, t, ins, del, cnIns, segDel, start, end, c) 561 } else if tid == pkgcatalog.MO_COLUMNS_ID { 562 checkMOColumns(ctx, t, ins, del, cnIns, segDel, start, end, c) 563 } else { 564 checkUserTables(ctx, t, tid, ins, del, cnIns, segDel, start, end, c) 565 } 566 } 567 568 func checkMODatabase(ctx context.Context, t *testing.T, ins, del, cnIns, segDel *api.Batch, start, end types.TS, c *catalog.Catalog) { 569 collector := logtail.NewIncrementalCollector(start, end, false) 570 p := &catalog.LoopProcessor{} 571 p.DatabaseFn = collector.VisitDB 572 err := c.RecurLoop(p) 573 assert.NoError(t, err) 574 data2 := collector.OrphanData() 575 defer data2.Close() 576 ins2, _, del2, _ := data2.GetDBBatchs() 577 578 isProtoTNBatchEqual(ctx, t, ins, ins2) 579 isProtoTNBatchEqual(ctx, t, del, del2) 580 assert.Nil(t, cnIns) 581 assert.Nil(t, segDel) 582 } 583 584 func checkMOTables(ctx context.Context, t *testing.T, ins, del, cnIns, segDel *api.Batch, start, end types.TS, c *catalog.Catalog) { 585 collector := logtail.NewIncrementalCollector(start, end, false) 586 p := &catalog.LoopProcessor{} 587 p.TableFn = collector.VisitTable 588 err := c.RecurLoop(p) 589 assert.NoError(t, err) 590 data2 := collector.OrphanData() 591 defer data2.Close() 592 ins2, _, _, del2, _ := data2.GetTblBatchs() 593 594 isProtoTNBatchEqual(ctx, t, ins, ins2) 595 isProtoTNBatchEqual(ctx, t, del, del2) 596 assert.Nil(t, cnIns) 597 assert.Nil(t, segDel) 598 } 599 600 func checkMOColumns(ctx context.Context, t *testing.T, ins, del, cnIns, segDel *api.Batch, start, end types.TS, c *catalog.Catalog) { 601 collector := logtail.NewIncrementalCollector(start, end, false) 602 p := &catalog.LoopProcessor{} 603 p.TableFn = collector.VisitTable 604 err := c.RecurLoop(p) 605 assert.NoError(t, err) 606 data2 := collector.OrphanData() 607 bats := data2.GetBatches() 608 ins2 := bats[logtail.TBLColInsertIDX] 609 del2 := bats[logtail.TBLColDeleteIDX] 610 611 isProtoTNBatchEqual(ctx, t, ins, ins2) 612 isProtoTNBatchEqual(ctx, t, del, del2) 613 assert.Nil(t, cnIns) 614 assert.Nil(t, segDel) 615 } 616 617 func checkUserTables(ctx context.Context, t *testing.T, tid uint64, ins, del, cnIns, seg *api.Batch, start, end types.TS, c *catalog.Catalog) { 618 collector := logtail.NewIncrementalCollector(start, end, false) 619 p := &catalog.LoopProcessor{} 620 p.TombstoneFn = func(be data.Tombstone) error { 621 if be.GetObject().(*catalog.ObjectEntry).GetTable().ID != tid { 622 return nil 623 } 624 return collector.VisitTombstone(be) 625 } 626 p.ObjectFn = func(se *catalog.ObjectEntry) error { 627 if se.GetTable().ID != tid { 628 return nil 629 } 630 return collector.VisitObj(se) 631 } 632 err := c.RecurLoop(p) 633 assert.NoError(t, err) 634 collector.LoadAndCollectObject(c, collector.VisitObj) 635 data2 := collector.OrphanData() 636 bats := data2.GetBatches() 637 ins2 := bats[logtail.BLKMetaInsertIDX] 638 // del2 := bats[logtail.BLKMetaDeleteIDX] 639 // cnIns2 := bats[logtail.BLKCNMetaInsertIDX] 640 seg2 := bats[logtail.ObjectInfoIDX] 641 642 isProtoTNBatchEqual(ctx, t, ins, ins2) 643 // isProtoTNBatchEqual(ctx, t, del, del2)// del is always empty after block is removed 644 // isProtoTNBatchEqual(ctx, t, cnIns, cnIns2) 645 646 // seg batch doesn't exist before ckp V9 647 if seg != nil { 648 isProtoTNBatchEqual(ctx, t, seg, seg2) 649 } 650 } 651 652 func GetUserTablesInsBatch(t *testing.T, tid uint64, start, end types.TS, c *catalog.Catalog) (*containers.Batch, *containers.Batch) { 653 collector := logtail.NewIncrementalCollector(start, end, false) 654 p := &catalog.LoopProcessor{} 655 p.TombstoneFn = func(be data.Tombstone) error { 656 if be.GetObject().(*catalog.ObjectEntry).GetTable().ID != tid { 657 return nil 658 } 659 return collector.VisitTombstone(be) 660 } 661 p.ObjectFn = func(se *catalog.ObjectEntry) error { 662 if se.GetTable().ID != tid { 663 return nil 664 } 665 return collector.VisitObj(se) 666 } 667 err := c.RecurLoop(p) 668 assert.NoError(t, err) 669 collector.LoadAndCollectObject(c, collector.VisitObj) 670 data := collector.OrphanData() 671 bats := data.GetBatches() 672 return bats[logtail.BLKMetaInsertIDX], bats[logtail.ObjectInfoIDX] 673 } 674 675 func CheckCheckpointReadWrite( 676 t *testing.T, 677 start, end types.TS, 678 c *catalog.Catalog, 679 checkpointBlockRows int, 680 checkpointSize int, 681 fs fileservice.FileService, 682 ) { 683 location, _ := writeIncrementalCheckpoint(t, start, end, c, checkpointBlockRows, checkpointSize, fs) 684 tnData := tnReadCheckpoint(t, location, fs) 685 686 checkTNCheckpointData(context.Background(), t, tnData, start, end, c) 687 p := &catalog.LoopProcessor{} 688 689 ins, del, cnIns, seg, cbs := cnReadCheckpoint(t, pkgcatalog.MO_DATABASE_ID, location, fs) 690 checkCNCheckpointData(context.Background(), t, pkgcatalog.MO_DATABASE_ID, ins, del, cnIns, seg, start, end, c) 691 for _, cb := range cbs { 692 if cb != nil { 693 cb() 694 } 695 } 696 ins, del, cnIns, seg, cbs = cnReadCheckpoint(t, pkgcatalog.MO_TABLES_ID, location, fs) 697 checkCNCheckpointData(context.Background(), t, pkgcatalog.MO_TABLES_ID, ins, del, cnIns, seg, start, end, c) 698 for _, cb := range cbs { 699 if cb != nil { 700 cb() 701 } 702 } 703 ins, del, cnIns, seg, cbs = cnReadCheckpoint(t, pkgcatalog.MO_COLUMNS_ID, location, fs) 704 checkCNCheckpointData(context.Background(), t, pkgcatalog.MO_COLUMNS_ID, ins, del, cnIns, seg, start, end, c) 705 for _, cb := range cbs { 706 if cb != nil { 707 cb() 708 } 709 } 710 711 p.TableFn = func(te *catalog.TableEntry) error { 712 ins, del, cnIns, seg, cbs := cnReadCheckpoint(t, te.ID, location, fs) 713 checkCNCheckpointData(context.Background(), t, te.ID, ins, del, cnIns, seg, start, end, c) 714 for _, cb := range cbs { 715 if cb != nil { 716 cb() 717 } 718 } 719 return nil 720 } 721 } 722 723 func (e *TestEngine) CheckReadCNCheckpoint() { 724 tids := []uint64{1, 2, 3} 725 p := &catalog.LoopProcessor{} 726 p.TableFn = func(te *catalog.TableEntry) error { 727 tids = append(tids, te.ID) 728 return nil 729 } 730 err := e.Catalog.RecurLoop(p) 731 assert.NoError(e.t, err) 732 ckps := e.BGCheckpointRunner.GetAllIncrementalCheckpoints() 733 for _, ckp := range ckps { 734 for _, tid := range tids { 735 ins, del, cnIns, seg, cbs := cnReadCheckpointWithVersion(e.t, tid, ckp.GetLocation(), e.Opts.Fs, ckp.GetVersion()) 736 ctx := context.Background() 737 ctx = context.WithValue(ctx, CtxOldVersion{}, int(ckp.GetVersion())) 738 checkCNCheckpointData(ctx, e.t, tid, ins, del, cnIns, seg, ckp.GetStart(), ckp.GetEnd(), e.Catalog) 739 for _, cb := range cbs { 740 if cb != nil { 741 cb() 742 } 743 } 744 } 745 } 746 } 747 748 func (e *TestEngine) CheckCollectDeleteInRange() { 749 txn, rel := e.GetRelation() 750 ForEachObject(rel, func(obj handle.Object) error { 751 meta := obj.GetMeta().(*catalog.ObjectEntry) 752 deleteBat, _, err := meta.GetObjectData().CollectDeleteInRange( 753 context.Background(), types.TS{}, txn.GetStartTS(), false, common.DefaultAllocator, 754 ) 755 assert.NoError(e.t, err) 756 pkDef := e.schema.GetPrimaryKey() 757 deleteRowIDs := deleteBat.GetVectorByName(catalog.AttrRowID) 758 deletePKs := deleteBat.GetVectorByName(catalog.AttrPKVal) 759 blkCnt := obj.BlkCnt() 760 pkVectors := make([]*containers.ColumnView, blkCnt) 761 rowIDVectors := make([]*containers.ColumnView, blkCnt) 762 for i := uint16(0); i < uint16(blkCnt); i++ { 763 pkVectors[i], err = meta.GetObjectData().GetColumnDataById(context.Background(), txn, e.schema, i, pkDef.Idx, common.DefaultAllocator) 764 assert.NoError(e.t, err) 765 rowIDVectors[i], err = meta.GetObjectData().GetColumnDataById(context.Background(), txn, e.schema, i, e.schema.PhyAddrKey.Idx, common.DefaultAllocator) 766 assert.NoError(e.t, err) 767 } 768 for i := 0; i < deleteBat.Length(); i++ { 769 rowID := deleteRowIDs.Get(i).(types.Rowid) 770 offset := rowID.GetRowOffset() 771 _, blkOffset := rowID.BorrowBlockID().Offsets() 772 appendRowID := rowIDVectors[blkOffset].GetData().Get(int(offset)).(types.Rowid) 773 e.t.Logf("delete rowID %v pk %v, append rowID %v pk %v", rowID.String(), deletePKs.Get(i), appendRowID.String(), pkVectors[blkOffset].GetData().Get(int(offset))) 774 assert.Equal(e.t, pkVectors[blkOffset].GetData().Get(int(offset)), deletePKs.Get(i)) 775 } 776 return nil 777 }) 778 err := txn.Commit(context.Background()) 779 assert.NoError(e.t, err) 780 } 781 782 func (e *TestEngine) CheckObjectInfo(onlyCheckName bool) { 783 p := &catalog.LoopProcessor{} 784 p.ObjectFn = func(se *catalog.ObjectEntry) error { 785 se.LoopChainLocked(func(node *catalog.MVCCNode[*catalog.ObjectMVCCNode]) bool { 786 if se.GetTable().GetDB().ID == pkgcatalog.MO_CATALOG_ID { 787 return true 788 } 789 flushed := true 790 if se.IsAppendable() && !node.HasDropCommitted() { 791 flushed = false 792 } 793 if onlyCheckName || !flushed { 794 assert.Equal(e.t, objectio.BuildObjectNameWithObjectID(&se.ID), 795 node.BaseNode.ObjectStats.ObjectLocation().Name(), 796 "load %v, get %v", 797 se.ID.String(), 798 node.BaseNode.ObjectStats.String()) 799 if flushed { 800 stats, err := se.LoadObjectInfoWithTxnTS(node.Start) 801 assert.NoError(e.t, err) 802 assert.Equal(e.t, stats.ObjectLocation().Extent(), 803 node.BaseNode.ObjectStats.ObjectLocation().Extent(), 804 "load %v, get %v", 805 stats.String(), 806 node.BaseNode.ObjectStats.String()) 807 808 } 809 } else { 810 stats, err := se.LoadObjectInfoWithTxnTS(node.Start) 811 assert.NoError(e.t, err) 812 assert.Equal(e.t, stats, node.BaseNode.ObjectStats, "load %v, get %v", stats.String(), node.BaseNode.ObjectStats.String()) 813 } 814 return true 815 }) 816 return nil 817 } 818 err := e.Catalog.RecurLoop(p) 819 assert.NoError(e.t, err) 820 }