github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/catalog/object.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 catalog 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "time" 22 23 "github.com/matrixorigin/matrixone/pkg/container/types" 24 "github.com/matrixorigin/matrixone/pkg/fileservice" 25 "github.com/matrixorigin/matrixone/pkg/objectio" 26 v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" 27 28 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 29 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/data" 30 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 31 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/index" 32 ) 33 34 type ObjectDataFactory = func(meta *ObjectEntry) data.Object 35 type TombstoneFactory = func(meta *ObjectEntry) data.Tombstone 36 type ObjectEntry struct { 37 ID types.Objectid 38 blkCnt int 39 *BaseEntryImpl[*ObjectMVCCNode] 40 table *TableEntry 41 *ObjectNode 42 objData data.Object 43 } 44 45 func (entry *ObjectEntry) GetLoaded() bool { 46 stats := entry.GetObjectStats() 47 return stats.Rows() != 0 48 } 49 50 func (entry *ObjectEntry) GetSortKeyZonemap() index.ZM { 51 stats := entry.GetObjectStats() 52 return stats.SortKeyZoneMap() 53 } 54 55 func (entry *ObjectEntry) SetRemainingRows(rows int) { 56 entry.remainingRows.Append(rows) 57 } 58 59 func (entry *ObjectEntry) GetRemainingRows() int { 60 return entry.remainingRows.V() 61 } 62 63 func (entry *ObjectEntry) GetRows() int { 64 stats := entry.GetObjectStats() 65 return int(stats.Rows()) 66 } 67 68 func (entry *ObjectEntry) GetOriginSize() int { 69 stats := entry.GetObjectStats() 70 return int(stats.OriginSize()) 71 } 72 73 func (entry *ObjectEntry) GetCompSize() int { 74 stats := entry.GetObjectStats() 75 return int(stats.Size()) 76 } 77 78 func (entry *ObjectEntry) StatsString(zonemapKind common.ZonemapPrintKind) string { 79 zonemapStr := "nil" 80 if z := entry.GetSortKeyZonemap(); z != nil { 81 switch zonemapKind { 82 case common.ZonemapPrintKindNormal: 83 zonemapStr = z.String() 84 case common.ZonemapPrintKindCompose: 85 zonemapStr = z.StringForCompose() 86 case common.ZonemapPrintKindHex: 87 zonemapStr = z.StringForHex() 88 } 89 } 90 return fmt.Sprintf( 91 "loaded:%t, oSize:%s, cSzie:%s rows:%d, remainingRows:%d, zm: %s", 92 entry.GetLoaded(), 93 common.HumanReadableBytes(entry.GetOriginSize()), 94 common.HumanReadableBytes(entry.GetCompSize()), 95 entry.GetRows(), 96 entry.remainingRows.V(), 97 zonemapStr, 98 ) 99 } 100 101 func (entry *ObjectEntry) InMemoryDeletesExisted() bool { 102 tombstone := entry.GetTable().TryGetTombstone(entry.ID) 103 if tombstone != nil { 104 return tombstone.InMemoryDeletesExisted() 105 } 106 return false 107 } 108 109 func NewObjectEntry( 110 table *TableEntry, 111 id *objectio.ObjectId, 112 txn txnif.AsyncTxn, 113 state EntryState, 114 dataFactory ObjectDataFactory, 115 ) *ObjectEntry { 116 e := &ObjectEntry{ 117 ID: *id, 118 BaseEntryImpl: NewBaseEntry( 119 func() *ObjectMVCCNode { return &ObjectMVCCNode{*objectio.NewObjectStats()} }), 120 table: table, 121 ObjectNode: &ObjectNode{ 122 state: state, 123 SortHint: table.GetDB().catalog.NextObject(), 124 }, 125 } 126 e.CreateWithTxn(txn, NewObjectInfoWithObjectID(id)) 127 if dataFactory != nil { 128 e.objData = dataFactory(e) 129 } 130 return e 131 } 132 133 func NewObjectEntryByMetaLocation( 134 table *TableEntry, 135 id *objectio.ObjectId, 136 start, end types.TS, 137 state EntryState, 138 metalocation objectio.Location, 139 dataFactory ObjectDataFactory, 140 ) *ObjectEntry { 141 e := &ObjectEntry{ 142 ID: *id, 143 BaseEntryImpl: NewBaseEntry( 144 func() *ObjectMVCCNode { return &ObjectMVCCNode{*objectio.NewObjectStats()} }), 145 table: table, 146 ObjectNode: &ObjectNode{ 147 state: state, 148 sorted: state == ES_NotAppendable, 149 SortHint: table.GetDB().catalog.NextObject(), 150 }, 151 } 152 e.CreateWithStartAndEnd(start, end, NewObjectInfoWithMetaLocation(metalocation, id)) 153 if dataFactory != nil { 154 e.objData = dataFactory(e) 155 } 156 return e 157 } 158 159 func NewReplayObjectEntry() *ObjectEntry { 160 e := &ObjectEntry{ 161 BaseEntryImpl: NewReplayBaseEntry( 162 func() *ObjectMVCCNode { return &ObjectMVCCNode{*objectio.NewObjectStats()} }), 163 } 164 return e 165 } 166 167 func NewStandaloneObject(table *TableEntry, ts types.TS) *ObjectEntry { 168 e := &ObjectEntry{ 169 ID: *objectio.NewObjectid(), 170 BaseEntryImpl: NewBaseEntry( 171 func() *ObjectMVCCNode { return &ObjectMVCCNode{*objectio.NewObjectStats()} }), 172 table: table, 173 ObjectNode: &ObjectNode{ 174 state: ES_Appendable, 175 IsLocal: true, 176 }, 177 } 178 e.CreateWithTS(ts, &ObjectMVCCNode{*objectio.NewObjectStats()}) 179 return e 180 } 181 182 func NewSysObjectEntry(table *TableEntry, id types.Uuid) *ObjectEntry { 183 e := &ObjectEntry{ 184 BaseEntryImpl: NewBaseEntry( 185 func() *ObjectMVCCNode { return &ObjectMVCCNode{*objectio.NewObjectStats()} }), 186 table: table, 187 ObjectNode: &ObjectNode{ 188 state: ES_Appendable, 189 }, 190 } 191 e.CreateWithTS(types.SystemDBTS, &ObjectMVCCNode{*objectio.NewObjectStats()}) 192 var bid types.Blockid 193 schema := table.GetLastestSchemaLocked() 194 if schema.Name == SystemTableSchema.Name { 195 bid = SystemBlock_Table_ID 196 } else if schema.Name == SystemDBSchema.Name { 197 bid = SystemBlock_DB_ID 198 } else if schema.Name == SystemColumnSchema.Name { 199 bid = SystemBlock_Columns_ID 200 } else { 201 panic("not supported") 202 } 203 e.ID = *bid.Object() 204 return e 205 } 206 207 func (entry *ObjectEntry) GetLocation() objectio.Location { 208 entry.RLock() 209 defer entry.RUnlock() 210 node := entry.GetLatestNodeLocked() 211 location := node.BaseNode.ObjectStats.ObjectLocation() 212 return location 213 } 214 func (entry *ObjectEntry) InitData(factory DataFactory) { 215 if factory == nil { 216 return 217 } 218 dataFactory := factory.MakeObjectFactory() 219 entry.objData = dataFactory(entry) 220 } 221 func (entry *ObjectEntry) HasPersistedData() bool { 222 return entry.ObjectPersisted() 223 } 224 func (entry *ObjectEntry) GetObjectData() data.Object { return entry.objData } 225 func (entry *ObjectEntry) GetObjectStats() (stats objectio.ObjectStats) { 226 entry.RLock() 227 defer entry.RUnlock() 228 entry.LoopChainLocked(func(node *MVCCNode[*ObjectMVCCNode]) bool { 229 if !node.BaseNode.IsEmpty() { 230 stats = node.BaseNode.ObjectStats 231 return false 232 } 233 return true 234 }) 235 return 236 } 237 238 func (entry *ObjectEntry) CheckAndLoad() error { 239 s := entry.GetObjectStats() 240 if s.Rows() == 0 { 241 ins := time.Now() 242 defer func() { 243 v2.GetObjectStatsDurationHistogram.Observe(time.Since(ins).Seconds()) 244 }() 245 _, err := entry.LoadObjectInfoForLastNode() 246 // logutil.Infof("yyyyyy loaded %v %v", common.ShortObjId(entry.ID), err) 247 return err 248 } 249 return nil 250 } 251 252 func (entry *ObjectEntry) NeedPrefetchObjectMetaForObjectInfo(nodes []*MVCCNode[*ObjectMVCCNode]) (needPrefetch bool) { 253 lastNode := nodes[0] 254 for _, n := range nodes { 255 if n.Start.Greater(&lastNode.Start) { 256 lastNode = n 257 } 258 } 259 if !lastNode.BaseNode.IsEmpty() { 260 return 261 } 262 if entry.nodeHasPersistedData(lastNode) { 263 needPrefetch = true 264 } 265 266 return 267 } 268 func (entry *ObjectEntry) nodeHasPersistedData(node *MVCCNode[*ObjectMVCCNode]) bool { 269 if !entry.IsAppendable() { 270 return true 271 } 272 return node.HasDropCommitted() 273 } 274 func (entry *ObjectEntry) SetObjectStatsForPreviousNode(nodes []*MVCCNode[*ObjectMVCCNode]) { 275 if entry.IsAppendable() || len(nodes) <= 1 { 276 return 277 } 278 lastNode := nodes[0] 279 for _, n := range nodes { 280 if n.Start.Greater(&lastNode.Start) { 281 lastNode = n 282 } 283 } 284 stat := lastNode.BaseNode.ObjectStats 285 entry.Lock() 286 for _, n := range nodes { 287 if n.BaseNode.IsEmpty() { 288 n.BaseNode.ObjectStats = *stat.Clone() 289 } 290 } 291 entry.Unlock() 292 } 293 294 func (entry *ObjectEntry) LoadObjectInfoWithTxnTS(startTS types.TS) (objectio.ObjectStats, error) { 295 stats := *objectio.NewObjectStats() 296 297 entry.RLock() 298 entry.LoopChainLocked(func(n *MVCCNode[*ObjectMVCCNode]) bool { 299 if !n.BaseNode.IsEmpty() { 300 stats = *n.BaseNode.ObjectStats.Clone() 301 return false 302 } 303 return true 304 }) 305 entry.RUnlock() 306 if stats.Rows() != 0 { 307 return stats, nil 308 } 309 metaLoc := entry.GetLatestCommittedNodeLocked().BaseNode.ObjectStats.ObjectLocation() 310 311 objMeta, err := objectio.FastLoadObjectMeta(context.Background(), &metaLoc, false, entry.objData.GetFs().Service) 312 if err != nil { 313 return *objectio.NewObjectStats(), err 314 } 315 objectio.SetObjectStatsObjectName(&stats, metaLoc.Name()) 316 objectio.SetObjectStatsExtent(&stats, metaLoc.Extent()) 317 objectDataMeta := objMeta.MustDataMeta() 318 objectio.SetObjectStatsRowCnt(&stats, objectDataMeta.BlockHeader().Rows()) 319 objectio.SetObjectStatsBlkCnt(&stats, objectDataMeta.BlockCount()) 320 objectio.SetObjectStatsSize(&stats, metaLoc.Extent().End()+objectio.FooterSize) 321 schema := entry.table.schema.Load() 322 originSize := uint32(0) 323 for _, col := range schema.ColDefs { 324 if col.IsPhyAddr() { 325 continue 326 } 327 colmata := objectDataMeta.MustGetColumn(uint16(col.SeqNum)) 328 originSize += colmata.Location().OriginSize() 329 } 330 objectio.SetObjectStatsOriginSize(&stats, originSize) 331 if schema.HasSortKey() { 332 col := schema.GetSingleSortKey() 333 objectio.SetObjectStatsSortKeyZoneMap(&stats, objectDataMeta.MustGetColumn(col.SeqNum).ZoneMap()) 334 } 335 return stats, nil 336 } 337 338 func (entry *ObjectEntry) LoadObjectInfoForLastNode() (stats objectio.ObjectStats, err error) { 339 entry.RLock() 340 startTS := entry.GetLatestCommittedNodeLocked().Start 341 entry.RUnlock() 342 343 stats, err = entry.LoadObjectInfoWithTxnTS(startTS) 344 if err == nil { 345 entry.Lock() 346 entry.GetLatestNodeLocked().BaseNode.ObjectStats = stats 347 entry.Unlock() 348 } 349 return stats, nil 350 } 351 352 // for test 353 func (entry *ObjectEntry) GetInMemoryObjectInfo() *ObjectMVCCNode { 354 return entry.BaseEntryImpl.GetLatestCommittedNodeLocked().BaseNode 355 } 356 357 func (entry *ObjectEntry) Less(b *ObjectEntry) int { 358 if entry.SortHint < b.SortHint { 359 return -1 360 } else if entry.SortHint > b.SortHint { 361 return 1 362 } 363 return 0 364 } 365 366 func (entry *ObjectEntry) UpdateObjectInfo(txn txnif.TxnReader, stats *objectio.ObjectStats) (isNewNode bool, err error) { 367 entry.Lock() 368 defer entry.Unlock() 369 needWait, txnToWait := entry.NeedWaitCommittingLocked(txn.GetStartTS()) 370 if needWait { 371 entry.Unlock() 372 txnToWait.GetTxnState(true) 373 entry.Lock() 374 } 375 err = entry.CheckConflictLocked(txn) 376 if err != nil { 377 return 378 } 379 baseNode := NewObjectInfoWithObjectStats(stats) 380 var node *MVCCNode[*ObjectMVCCNode] 381 isNewNode, node = entry.getOrSetUpdateNodeLocked(txn) 382 node.BaseNode.Update(baseNode) 383 return 384 } 385 386 func (entry *ObjectEntry) MakeCommand(id uint32) (cmd txnif.TxnCmd, err error) { 387 cmdType := IOET_WALTxnCommand_Object 388 entry.RLock() 389 defer entry.RUnlock() 390 return newObjectCmd(id, cmdType, entry), nil 391 } 392 393 func (entry *ObjectEntry) Set1PC() { 394 entry.GetLatestNodeLocked().Set1PC() 395 } 396 func (entry *ObjectEntry) Is1PC() bool { 397 return entry.GetLatestNodeLocked().Is1PC() 398 } 399 func (entry *ObjectEntry) PPString(level common.PPLevel, depth int, prefix string) string { 400 var w bytes.Buffer 401 _, _ = w.WriteString(fmt.Sprintf("%s%s%s", common.RepeatStr("\t", depth), prefix, entry.StringWithLevel(level))) 402 if level == common.PPL0 { 403 return w.String() 404 } 405 return w.String() 406 } 407 408 func (entry *ObjectEntry) StringLocked() string { 409 return entry.StringWithLevelLocked(common.PPL1) 410 } 411 412 func (entry *ObjectEntry) Repr() string { 413 id := entry.AsCommonID() 414 return fmt.Sprintf("[%s%s]OBJ[%s]", entry.state.Repr(), entry.ObjectNode.String(), id.String()) 415 } 416 417 func (entry *ObjectEntry) String() string { 418 entry.RLock() 419 defer entry.RUnlock() 420 return entry.StringLocked() 421 } 422 423 func (entry *ObjectEntry) StringWithLevel(level common.PPLevel) string { 424 entry.RLock() 425 defer entry.RUnlock() 426 return entry.StringWithLevelLocked(level) 427 } 428 429 func (entry *ObjectEntry) StringWithLevelLocked(level common.PPLevel) string { 430 if level <= common.PPL1 { 431 return fmt.Sprintf("[%s-%s]OBJ[%s][C@%s,D@%s]", 432 entry.state.Repr(), entry.ObjectNode.String(), entry.ID.String(), entry.GetCreatedAtLocked().ToString(), entry.GetDeleteAtLocked().ToString()) 433 } 434 return fmt.Sprintf("[%s-%s]OBJ[%s]%s", entry.state.Repr(), entry.ObjectNode.String(), entry.ID.String(), entry.BaseEntryImpl.StringLocked()) 435 } 436 437 func (entry *ObjectEntry) BlockCnt() int { 438 if entry.IsAppendable() { 439 return 1 440 } 441 cnt := entry.getBlockCntFromStats() 442 if cnt != 0 { 443 return int(cnt) 444 } 445 return entry.blkCnt 446 } 447 448 func (entry *ObjectEntry) getBlockCntFromStats() (blkCnt uint32) { 449 entry.RLock() 450 defer entry.RUnlock() 451 node := entry.GetLatestNodeLocked() 452 if node == nil { 453 return 454 } 455 if node.BaseNode.IsEmpty() { 456 return 457 } 458 return node.BaseNode.ObjectStats.BlkCnt() 459 } 460 461 func (entry *ObjectEntry) tryUpdateBlockCnt(cnt int) { 462 if entry.blkCnt < cnt { 463 entry.blkCnt = cnt 464 } 465 } 466 467 func (entry *ObjectEntry) IsAppendable() bool { 468 return entry.state == ES_Appendable 469 } 470 471 func (entry *ObjectEntry) SetSorted() { 472 // modifing Object interface to supporte a borned sorted obj is verbose 473 // use Lock instead, the contention won't be intense 474 entry.Lock() 475 defer entry.Unlock() 476 entry.sorted = true 477 } 478 479 func (entry *ObjectEntry) IsSorted() bool { 480 entry.RLock() 481 defer entry.RUnlock() 482 return entry.sorted 483 } 484 485 func (entry *ObjectEntry) GetTable() *TableEntry { 486 return entry.table 487 } 488 489 // GetNonAppendableBlockCnt Non-appendable Object only can contain non-appendable blocks; 490 // Appendable Object can contain both of appendable blocks and non-appendable blocks 491 func (entry *ObjectEntry) GetNonAppendableBlockCnt() int { 492 return entry.blkCnt 493 } 494 495 func (entry *ObjectEntry) AsCommonID() *common.ID { 496 id := &common.ID{ 497 DbID: entry.GetTable().GetDB().ID, 498 TableID: entry.GetTable().ID, 499 } 500 id.SetObjectID(&entry.ID) 501 return id 502 } 503 504 func (entry *ObjectEntry) GetCatalog() *Catalog { return entry.table.db.catalog } 505 506 func (entry *ObjectEntry) PrepareRollback() (err error) { 507 var isEmpty bool 508 if isEmpty, err = entry.BaseEntryImpl.PrepareRollback(); err != nil { 509 return 510 } 511 if isEmpty { 512 if err = entry.GetTable().RemoveEntry(entry); err != nil { 513 return 514 } 515 } 516 return 517 } 518 519 // IsActive is coarse API: no consistency check 520 func (entry *ObjectEntry) IsActive() bool { 521 table := entry.GetTable() 522 if !table.IsActive() { 523 return false 524 } 525 return !entry.HasDropCommitted() 526 } 527 528 func (entry *ObjectEntry) TreeMaxDropCommitEntry() BaseEntry { 529 table := entry.GetTable() 530 db := table.GetDB() 531 if db.HasDropCommittedLocked() { 532 return db.BaseEntryImpl 533 } 534 if table.HasDropCommittedLocked() { 535 return table.BaseEntryImpl 536 } 537 if entry.HasDropCommittedLocked() { 538 return entry.BaseEntryImpl 539 } 540 return nil 541 } 542 543 // GetTerminationTS is coarse API: no consistency check 544 func (entry *ObjectEntry) GetTerminationTS() (ts types.TS, terminated bool) { 545 tableEntry := entry.GetTable() 546 dbEntry := tableEntry.GetDB() 547 548 dbEntry.RLock() 549 terminated, ts = dbEntry.TryGetTerminatedTSLocked(true) 550 if terminated { 551 dbEntry.RUnlock() 552 return 553 } 554 dbEntry.RUnlock() 555 556 terminated, ts = tableEntry.TryGetTerminatedTS(true) 557 return 558 } 559 560 func (entry *ObjectEntry) GetSchema() *Schema { 561 return entry.table.GetLastestSchema() 562 } 563 func (entry *ObjectEntry) GetSchemaLocked() *Schema { 564 return entry.table.GetLastestSchemaLocked() 565 } 566 567 // PrepareCompact is performance insensitive 568 // a block can be compacted: 569 // 1. no uncommited node 570 // 2. at least one committed node 571 func (entry *ObjectEntry) PrepareCompact() bool { 572 entry.RLock() 573 defer entry.RUnlock() 574 return entry.PrepareCompactLocked() 575 } 576 577 func (entry *ObjectEntry) PrepareCompactLocked() bool { 578 if entry.HasUncommittedNodeLocked() { 579 return false 580 } 581 if !entry.HasCommittedNodeLocked() { 582 return false 583 } 584 return true 585 } 586 587 // for old flushed objects, stats may be empty 588 func (entry *ObjectEntry) ObjectPersisted() bool { 589 entry.RLock() 590 defer entry.RUnlock() 591 if entry.IsEmptyLocked() { 592 return false 593 } 594 if entry.IsAppendable() { 595 lastNode := entry.GetLatestNodeLocked() 596 return lastNode.HasDropIntent() 597 } else { 598 return true 599 } 600 } 601 602 // PXU TODO: I can't understand this code 603 // aobj has persisted data after it is dropped 604 // obj always has persisted data 605 func (entry *ObjectEntry) HasCommittedPersistedData() bool { 606 entry.RLock() 607 defer entry.RUnlock() 608 if entry.IsAppendable() { 609 lastNode := entry.GetLatestNodeLocked() 610 return lastNode.HasDropCommitted() 611 } else { 612 return entry.HasCommittedNodeLocked() 613 } 614 } 615 func (entry *ObjectEntry) MustGetObjectStats() (objectio.ObjectStats, error) { 616 entry.RLock() 617 baseNode := entry.GetLatestNodeLocked().BaseNode 618 entry.RUnlock() 619 if baseNode.IsEmpty() { 620 return entry.LoadObjectInfoForLastNode() 621 } 622 return baseNode.ObjectStats, nil 623 } 624 625 func (entry *ObjectEntry) GetPKZoneMap( 626 ctx context.Context, 627 fs fileservice.FileService, 628 ) (zm index.ZM, err error) { 629 stats, err := entry.MustGetObjectStats() 630 if err != nil { 631 return 632 } 633 return stats.SortKeyZoneMap(), nil 634 } 635 func MockObjEntryWithTbl(tbl *TableEntry, size uint64) *ObjectEntry { 636 stats := objectio.NewObjectStats() 637 objectio.SetObjectStatsSize(stats, uint32(size)) 638 // to make sure pass the stats empty check 639 objectio.SetObjectStatsRowCnt(stats, uint32(1)) 640 641 e := &ObjectEntry{ 642 BaseEntryImpl: NewBaseEntry( 643 func() *ObjectMVCCNode { return &ObjectMVCCNode{*objectio.NewObjectStats()} }), 644 table: tbl, 645 ObjectNode: &ObjectNode{}, 646 } 647 e.CreateWithTS(types.BuildTS(time.Now().UnixNano(), 0), &ObjectMVCCNode{*stats}) 648 return e 649 }