github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/logtail/txn_handle.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 logtail 16 17 import ( 18 "context" 19 "fmt" 20 "sort" 21 "time" 22 23 v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" 24 25 "github.com/RoaringBitmap/roaring" 26 pkgcatalog "github.com/matrixorigin/matrixone/pkg/catalog" 27 "github.com/matrixorigin/matrixone/pkg/container/types" 28 "github.com/matrixorigin/matrixone/pkg/logutil" 29 "github.com/matrixorigin/matrixone/pkg/objectio" 30 "github.com/matrixorigin/matrixone/pkg/pb/api" 31 "github.com/matrixorigin/matrixone/pkg/pb/logtail" 32 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" 33 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 34 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers" 35 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/dbutils" 36 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 37 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tables/updates" 38 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnimpl" 39 ) 40 41 const ( 42 blkMetaInsBatch int8 = iota 43 blkMetaDelBatch 44 dataInsBatch 45 dataDelBatch 46 dbInsBatch 47 dbDelBatch 48 dbSpecialDeleteBatch 49 tblInsBatch 50 tblDelBatch 51 tblSpecialDeleteBatch 52 columnInsBatch 53 columnDelBatch 54 objectInfoBatch 55 batchTotalNum 56 ) 57 58 type TxnLogtailRespBuilder struct { 59 rt *dbutils.Runtime 60 currDBName, currTableName string 61 currDBID, currTableID uint64 62 txn txnif.AsyncTxn 63 64 batches []*containers.Batch 65 66 logtails *[]logtail.TableLogtail 67 currentLogtail *logtail.TableLogtail 68 69 batchToClose []*containers.Batch 70 insertBatch *roaring.Bitmap 71 } 72 73 func NewTxnLogtailRespBuilder(rt *dbutils.Runtime) *TxnLogtailRespBuilder { 74 logtails := make([]logtail.TableLogtail, 0) 75 return &TxnLogtailRespBuilder{ 76 rt: rt, 77 batches: make([]*containers.Batch, batchTotalNum), 78 logtails: &logtails, 79 batchToClose: make([]*containers.Batch, 0), 80 insertBatch: roaring.New(), 81 } 82 } 83 84 func (b *TxnLogtailRespBuilder) Close() { 85 for _, bat := range b.batchToClose { 86 bat.Close() 87 } 88 } 89 90 func (b *TxnLogtailRespBuilder) CollectLogtail(txn txnif.AsyncTxn) (*[]logtail.TableLogtail, func()) { 91 now := time.Now() 92 defer func() { 93 v2.LogTailCollectDurationHistogram.Observe(time.Since(now).Seconds()) 94 }() 95 96 b.txn = txn 97 txn.GetStore().ObserveTxn( 98 b.visitDatabase, 99 b.visitTable, 100 b.rotateTable, 101 b.visitDeltaloc, 102 b.visitObject, 103 b.visitAppend, 104 b.visitDelete) 105 b.BuildResp() 106 logtails := b.logtails 107 newlogtails := make([]logtail.TableLogtail, 0) 108 b.logtails = &newlogtails 109 return logtails, b.Close 110 } 111 112 func (b *TxnLogtailRespBuilder) visitObject(iobj any) { 113 obj := iobj.(*catalog.ObjectEntry) 114 node := obj.GetLatestNodeLocked() 115 if obj.IsAppendable() && node.BaseNode.IsEmpty() { 116 return 117 } 118 if !node.DeletedAt.Equal(&txnif.UncommitTS) { 119 if b.batches[objectInfoBatch] == nil { 120 b.batches[objectInfoBatch] = makeRespBatchFromSchema(ObjectInfoSchema, common.LogtailAllocator) 121 } 122 visitObject(b.batches[objectInfoBatch], obj, node, true, b.txn.GetPrepareTS()) 123 return 124 } 125 126 if b.batches[objectInfoBatch] == nil { 127 b.batches[objectInfoBatch] = makeRespBatchFromSchema(ObjectInfoSchema, common.LogtailAllocator) 128 } 129 visitObject(b.batches[objectInfoBatch], obj, node, true, b.txn.GetPrepareTS()) 130 } 131 132 func (b *TxnLogtailRespBuilder) visitDeltaloc(ideltalocChain any) { 133 deltalocChain := ideltalocChain.(*updates.DeltalocChain) 134 node := deltalocChain.GetLatestNodeLocked() 135 if node.BaseNode.DeltaLoc.IsEmpty() { 136 return 137 } 138 if b.batches[blkMetaInsBatch] == nil { 139 b.batches[blkMetaInsBatch] = makeRespBatchFromSchema(BlkMetaSchema, common.LogtailAllocator) 140 } 141 commitTS := b.txn.GetPrepareTS() 142 createAt := node.CreatedAt 143 if createAt.Equal(&txnif.UncommitTS) { 144 createAt = b.txn.GetPrepareTS() 145 } 146 updates.VisitDeltaloc(b.batches[blkMetaInsBatch], nil, deltalocChain.GetMeta(), deltalocChain.GetBlockID(), node, commitTS, createAt) 147 } 148 149 func (b *TxnLogtailRespBuilder) visitAppend(ibat any) { 150 src := ibat.(*containers.BatchWithVersion) 151 // sort by seqnums 152 sort.Sort(src) 153 mybat := containers.NewBatchWithCapacity(int(src.NextSeqnum) + 2) 154 mybat.AddVector( 155 catalog.AttrRowID, 156 src.GetVectorByName(catalog.AttrRowID).CloneWindowWithPool(0, src.Length(), b.rt.VectorPool.Small), 157 ) 158 tsType := types.T_TS.ToType() 159 commitVec := b.rt.VectorPool.Small.GetVector(&tsType) 160 commitVec.PreExtend(src.Length()) 161 for i := 0; i < src.Length(); i++ { 162 commitVec.Append(b.txn.GetPrepareTS(), false) 163 } 164 mybat.AddVector(catalog.AttrCommitTs, commitVec) 165 166 for i, seqnum := range src.Seqnums { 167 if seqnum >= objectio.SEQNUM_UPPER { 168 continue 169 } 170 for len(mybat.Vecs) < 2+int(seqnum) { 171 mybat.AppendPlaceholder() 172 } 173 vec := src.Vecs[i].CloneWindowWithPool(0, src.Length(), b.rt.VectorPool.Small) 174 mybat.AddVector(src.Attrs[i], vec) 175 } 176 177 if b.batches[dataInsBatch] == nil { 178 b.batches[dataInsBatch] = mybat 179 } else { 180 b.batches[dataInsBatch].Extend(mybat) 181 mybat.Close() 182 } 183 } 184 185 func (b *TxnLogtailRespBuilder) visitDelete(ctx context.Context, vnode txnif.DeleteNode) { 186 if vnode.IsPersistedDeletedNode() { 187 return 188 } 189 if b.batches[dataDelBatch] == nil { 190 b.batches[dataDelBatch] = makeRespBatchFromSchema(DelSchema, common.LogtailAllocator) 191 } 192 node := vnode.(*updates.DeleteNode) 193 meta := node.GetMeta() 194 schema := meta.GetSchema() 195 pkDef := schema.GetPrimaryKey() 196 deletes := node.GetRowMaskRefLocked() 197 blkID := node.GetBlockID() 198 199 batch := b.batches[dataDelBatch] 200 rowIDVec := batch.GetVectorByName(catalog.AttrRowID) 201 commitTSVec := batch.GetVectorByName(catalog.AttrCommitTs) 202 var pkVec containers.Vector 203 if len(batch.Vecs) == 2 { 204 pkVec = containers.MakeVector(pkDef.Type, common.LogtailAllocator) 205 batch.AddVector(catalog.AttrPKVal, pkVec) 206 } else { 207 pkVec = batch.GetVectorByName(catalog.AttrPKVal) 208 } 209 210 it := deletes.Iterator() 211 rowid2PK := node.DeletedPK() 212 for it.HasNext() { 213 del := it.Next() 214 rowid := objectio.NewRowid(blkID, del) 215 rowIDVec.Append(*rowid, false) 216 commitTSVec.Append(b.txn.GetPrepareTS(), false) 217 218 v, ok := rowid2PK[del] 219 if ok { 220 pkVec.Extend(v) 221 } 222 //if !ok { 223 // panic(fmt.Sprintf("rowid %d 's pk not found in rowid2PK.\n", del)) 224 //} 225 //pkVec.Extend(v) 226 } 227 228 //_ = meta.GetObjectData().Foreach( 229 // ctx, 230 // schema, 231 // pkDef.Idx, 232 // func(v any, isNull bool, row int) error { 233 // pkVec.Append(v, false) 234 // return nil 235 // }, 236 // &dels, 237 //) 238 } 239 240 func (b *TxnLogtailRespBuilder) visitTable(itbl any) { 241 tbl := itbl.(*catalog.TableEntry) 242 node := tbl.GetLatestNodeLocked() 243 // delete table 244 if node.DeletedAt.Equal(&txnif.UncommitTS) { 245 if b.batches[columnDelBatch] == nil { 246 b.batches[columnDelBatch] = makeRespBatchFromSchema(ColumnDelSchema, common.LogtailAllocator) 247 } 248 for _, usercol := range node.BaseNode.Schema.ColDefs { 249 b.batches[columnDelBatch].GetVectorByName(catalog.AttrRowID).Append(objectio.HackBytes2Rowid([]byte(fmt.Sprintf("%d-%s", tbl.ID, usercol.Name))), false) 250 b.batches[columnDelBatch].GetVectorByName(catalog.AttrCommitTs).Append(b.txn.GetPrepareTS(), false) 251 b.batches[columnDelBatch].GetVectorByName(pkgcatalog.SystemColAttr_UniqName).Append([]byte(fmt.Sprintf("%d-%s", tbl.GetID(), usercol.Name)), false) 252 } 253 if b.batches[tblDelBatch] == nil { 254 b.batches[tblDelBatch] = makeRespBatchFromSchema(TblDelSchema, common.LogtailAllocator) 255 b.batches[tblSpecialDeleteBatch] = makeRespBatchFromSchema(TBLSpecialDeleteSchema, common.LogtailAllocator) 256 } 257 catalogEntry2Batch(b.batches[tblDelBatch], tbl, node, TblDelSchema, txnimpl.FillTableRow, objectio.HackU64ToRowid(tbl.GetID()), b.txn.GetPrepareTS()) 258 catalogEntry2Batch(b.batches[tblSpecialDeleteBatch], tbl, node, TBLSpecialDeleteSchema, txnimpl.FillTableRow, objectio.HackU64ToRowid(tbl.GetID()), b.txn.GetPrepareTS()) 259 } 260 // create table 261 if node.CreatedAt.Equal(&txnif.UncommitTS) { 262 if b.batches[columnInsBatch] == nil { 263 b.batches[columnInsBatch] = makeRespBatchFromSchema(catalog.SystemColumnSchema, common.LogtailAllocator) 264 } 265 for _, syscol := range catalog.SystemColumnSchema.ColDefs { 266 txnimpl.FillColumnRow(tbl, node, syscol.Name, b.batches[columnInsBatch].GetVectorByName(syscol.Name)) 267 } 268 for _, usercol := range node.BaseNode.Schema.ColDefs { 269 b.batches[columnInsBatch].GetVectorByName(catalog.AttrRowID).Append(objectio.HackBytes2Rowid([]byte(fmt.Sprintf("%d-%s", tbl.ID, usercol.Name))), false) 270 b.batches[columnInsBatch].GetVectorByName(catalog.AttrCommitTs).Append(b.txn.GetPrepareTS(), false) 271 } 272 if b.batches[tblInsBatch] == nil { 273 b.batches[tblInsBatch] = makeRespBatchFromSchema(catalog.SystemTableSchema, common.LogtailAllocator) 274 } 275 catalogEntry2Batch(b.batches[tblInsBatch], tbl, node, catalog.SystemTableSchema, txnimpl.FillTableRow, objectio.HackU64ToRowid(tbl.GetID()), b.txn.GetPrepareTS()) 276 } 277 // alter table 278 if !node.CreatedAt.Equal(&txnif.UncommitTS) && !node.DeletedAt.Equal(&txnif.UncommitTS) { 279 if b.batches[columnInsBatch] == nil { 280 b.batches[columnInsBatch] = makeRespBatchFromSchema(catalog.SystemColumnSchema, common.LogtailAllocator) 281 } 282 if b.batches[columnDelBatch] == nil { 283 b.batches[columnDelBatch] = makeRespBatchFromSchema(ColumnDelSchema, common.LogtailAllocator) 284 } 285 for _, syscol := range catalog.SystemColumnSchema.ColDefs { 286 txnimpl.FillColumnRow(tbl, node, syscol.Name, b.batches[columnInsBatch].GetVectorByName(syscol.Name)) 287 } 288 for _, usercol := range node.BaseNode.Schema.ColDefs { 289 b.batches[columnInsBatch].GetVectorByName(catalog.AttrRowID).Append(objectio.HackBytes2Rowid([]byte(fmt.Sprintf("%d-%s", tbl.ID, usercol.Name))), false) 290 b.batches[columnInsBatch].GetVectorByName(catalog.AttrCommitTs).Append(b.txn.GetPrepareTS(), false) 291 } 292 for _, name := range node.BaseNode.Schema.Extra.DroppedAttrs { 293 b.batches[columnDelBatch].GetVectorByName(catalog.AttrRowID).Append(objectio.HackBytes2Rowid([]byte(fmt.Sprintf("%d-%s", tbl.ID, name))), false) 294 b.batches[columnDelBatch].GetVectorByName(catalog.AttrCommitTs).Append(b.txn.GetPrepareTS(), false) 295 b.batches[columnDelBatch].GetVectorByName(pkgcatalog.SystemColAttr_UniqName).Append([]byte(fmt.Sprintf("%d-%s", tbl.GetID(), name)), false) 296 } 297 if b.batches[tblInsBatch] == nil { 298 b.batches[tblInsBatch] = makeRespBatchFromSchema(catalog.SystemTableSchema, common.LogtailAllocator) 299 } 300 catalogEntry2Batch(b.batches[tblInsBatch], tbl, node, catalog.SystemTableSchema, txnimpl.FillTableRow, objectio.HackU64ToRowid(tbl.GetID()), b.txn.GetPrepareTS()) 301 } 302 } 303 func (b *TxnLogtailRespBuilder) visitDatabase(idb any) { 304 db := idb.(*catalog.DBEntry) 305 node := db.GetLatestNodeLocked() 306 if node.DeletedAt.Equal(&txnif.UncommitTS) { 307 if b.batches[dbDelBatch] == nil { 308 b.batches[dbDelBatch] = makeRespBatchFromSchema(DBDelSchema, common.LogtailAllocator) 309 b.batches[dbSpecialDeleteBatch] = makeRespBatchFromSchema(DBSpecialDeleteSchema, common.LogtailAllocator) 310 } 311 catalogEntry2Batch(b.batches[dbDelBatch], db, node, DBDelSchema, txnimpl.FillDBRow, objectio.HackU64ToRowid(db.GetID()), b.txn.GetPrepareTS()) 312 catalogEntry2Batch(b.batches[dbSpecialDeleteBatch], db, node, DBSpecialDeleteSchema, txnimpl.FillDBRow, objectio.HackU64ToRowid(db.GetID()), b.txn.GetPrepareTS()) 313 } 314 if node.CreatedAt.Equal(&txnif.UncommitTS) { 315 if b.batches[dbInsBatch] == nil { 316 b.batches[dbInsBatch] = makeRespBatchFromSchema(catalog.SystemDBSchema, common.LogtailAllocator) 317 } 318 catalogEntry2Batch( 319 b.batches[dbInsBatch], 320 db, 321 node, 322 catalog.SystemDBSchema, 323 txnimpl.FillDBRow, 324 objectio.HackU64ToRowid(db.GetID()), 325 b.txn.GetPrepareTS(), 326 ) 327 } 328 } 329 330 func (b *TxnLogtailRespBuilder) buildLogtailEntry(tid, dbid uint64, tableName, dbName string, batchIdx int8, delete bool) { 331 bat := b.batches[batchIdx] 332 if bat == nil || bat.Length() == 0 { 333 return 334 } 335 apiBat, err := containersBatchToProtoBatch(bat) 336 common.DoIfDebugEnabled(func() { 337 logutil.Debugf( 338 "[logtail] from table %d-%s, delete %v, batch length %d @%s", 339 tid, tableName, delete, bat.Length(), b.txn.GetPrepareTS().ToString(), 340 ) 341 }) 342 if err != nil { 343 panic(err) 344 } 345 entryType := api.Entry_Insert 346 if delete { 347 entryType = api.Entry_Delete 348 } 349 if batchIdx == dbSpecialDeleteBatch || batchIdx == tblSpecialDeleteBatch { 350 entryType = api.Entry_SpecialDelete 351 } 352 entry := &api.Entry{ 353 EntryType: entryType, 354 TableId: tid, 355 TableName: tableName, 356 DatabaseId: dbid, 357 DatabaseName: dbName, 358 Bat: apiBat, 359 } 360 ts := b.txn.GetPrepareTS().ToTimestamp() 361 tableID := &api.TableID{ 362 DbId: dbid, 363 TbId: tid, 364 } 365 if b.currentLogtail == nil { 366 b.currentLogtail = &logtail.TableLogtail{ 367 Ts: &ts, 368 Table: tableID, 369 Commands: make([]api.Entry, 0), 370 } 371 } else { 372 if tid != b.currentLogtail.Table.TbId { 373 *b.logtails = append(*b.logtails, *b.currentLogtail) 374 b.currentLogtail = &logtail.TableLogtail{ 375 Ts: &ts, 376 Table: tableID, 377 Commands: make([]api.Entry, 0), 378 } 379 } 380 } 381 b.currentLogtail.Commands = append(b.currentLogtail.Commands, *entry) 382 // specail delete batch and delete batch should be in the same TableLogtail 383 if batchIdx == dbDelBatch || batchIdx == tblDelBatch { 384 var bat2 *containers.Batch 385 if batchIdx == dbDelBatch { 386 bat2 = b.batches[dbSpecialDeleteBatch] 387 } 388 if batchIdx == tblDelBatch { 389 bat2 = b.batches[tblSpecialDeleteBatch] 390 } 391 apiBat2, err := containersBatchToProtoBatch(bat2) 392 if err != nil { 393 panic(err) 394 } 395 entry2 := &api.Entry{ 396 EntryType: api.Entry_SpecialDelete, 397 TableId: tid, 398 TableName: tableName, 399 DatabaseId: dbid, 400 DatabaseName: dbName, 401 Bat: apiBat2, 402 } 403 b.currentLogtail.Commands = append(b.currentLogtail.Commands, *entry2) 404 } 405 } 406 407 func (b *TxnLogtailRespBuilder) rotateTable(dbName, tableName string, dbid, tid uint64) { 408 b.buildLogtailEntry(b.currTableID, b.currDBID, fmt.Sprintf("_%d_meta", b.currTableID), b.currDBName, blkMetaInsBatch, false) 409 if b.batches[blkMetaInsBatch] != nil { 410 b.batchToClose = append(b.batchToClose, b.batches[blkMetaInsBatch]) 411 b.batches[blkMetaInsBatch] = nil 412 } 413 b.buildLogtailEntry(b.currTableID, b.currDBID, fmt.Sprintf("_%d_meta", b.currTableID), b.currDBName, blkMetaDelBatch, true) 414 if b.batches[blkMetaDelBatch] != nil { 415 b.batchToClose = append(b.batchToClose, b.batches[blkMetaDelBatch]) 416 b.batches[blkMetaDelBatch] = nil 417 } 418 419 b.buildLogtailEntry(b.currTableID, b.currDBID, fmt.Sprintf("_%d_obj", b.currTableID), b.currDBName, objectInfoBatch, false) 420 if b.batches[objectInfoBatch] != nil { 421 b.batchToClose = append(b.batchToClose, b.batches[objectInfoBatch]) 422 b.batches[objectInfoBatch] = nil 423 } 424 b.buildLogtailEntry(b.currTableID, b.currDBID, b.currTableName, b.currDBName, dataDelBatch, true) 425 if b.batches[dataDelBatch] != nil { 426 b.batchToClose = append(b.batchToClose, b.batches[dataDelBatch]) 427 b.batches[dataDelBatch] = nil 428 } 429 b.buildLogtailEntry(b.currTableID, b.currDBID, b.currTableName, b.currDBName, dataInsBatch, false) 430 if b.batches[dataInsBatch] != nil { 431 b.insertBatch.Add(uint32(len(b.batchToClose))) 432 b.batchToClose = append(b.batchToClose, b.batches[dataInsBatch]) 433 b.batches[dataInsBatch] = nil 434 } 435 b.currTableID = tid 436 b.currDBID = dbid 437 b.currTableName = tableName 438 b.currDBName = dbName 439 } 440 441 func (b *TxnLogtailRespBuilder) BuildResp() { 442 b.buildLogtailEntry(b.currTableID, b.currDBID, fmt.Sprintf("_%d_meta", b.currTableID), b.currDBName, blkMetaInsBatch, false) 443 b.buildLogtailEntry(b.currTableID, b.currDBID, fmt.Sprintf("_%d_meta", b.currTableID), b.currDBName, blkMetaDelBatch, true) 444 b.buildLogtailEntry(b.currTableID, b.currDBID, fmt.Sprintf("_%d_obj", b.currTableID), b.currDBName, objectInfoBatch, false) 445 b.buildLogtailEntry(b.currTableID, b.currDBID, b.currTableName, b.currDBName, dataDelBatch, true) 446 b.buildLogtailEntry(b.currTableID, b.currDBID, b.currTableName, b.currDBName, dataInsBatch, false) 447 448 b.buildLogtailEntry(pkgcatalog.MO_TABLES_ID, pkgcatalog.MO_CATALOG_ID, pkgcatalog.MO_TABLES, pkgcatalog.MO_CATALOG, tblInsBatch, false) 449 b.buildLogtailEntry(pkgcatalog.MO_TABLES_ID, pkgcatalog.MO_CATALOG_ID, pkgcatalog.MO_TABLES, pkgcatalog.MO_CATALOG, tblDelBatch, true) 450 b.buildLogtailEntry(pkgcatalog.MO_COLUMNS_ID, pkgcatalog.MO_CATALOG_ID, pkgcatalog.MO_COLUMNS, pkgcatalog.MO_CATALOG, columnInsBatch, false) 451 b.buildLogtailEntry(pkgcatalog.MO_COLUMNS_ID, pkgcatalog.MO_CATALOG_ID, pkgcatalog.MO_COLUMNS, pkgcatalog.MO_CATALOG, columnDelBatch, true) 452 b.buildLogtailEntry(pkgcatalog.MO_DATABASE_ID, pkgcatalog.MO_CATALOG_ID, pkgcatalog.MO_DATABASE, pkgcatalog.MO_CATALOG, dbInsBatch, false) 453 b.buildLogtailEntry(pkgcatalog.MO_DATABASE_ID, pkgcatalog.MO_CATALOG_ID, pkgcatalog.MO_DATABASE, pkgcatalog.MO_CATALOG, dbDelBatch, true) 454 for i := range b.batches { 455 if b.batches[i] != nil { 456 if int8(i) == dataInsBatch { 457 b.insertBatch.Add(uint32(len(b.batchToClose))) 458 } 459 b.batchToClose = append(b.batchToClose, b.batches[i]) 460 b.batches[i] = nil 461 } 462 } 463 if b.currentLogtail != nil { 464 *b.logtails = append(*b.logtails, *b.currentLogtail) 465 b.currentLogtail = nil 466 } 467 }