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  }