github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/catalog/segment.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  	"encoding/binary"
    20  	"fmt"
    21  	"io"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    24  	"github.com/matrixorigin/matrixone/pkg/container/types"
    25  
    26  	"github.com/matrixorigin/matrixone/pkg/logutil"
    27  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common"
    28  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/data"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    30  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks"
    31  )
    32  
    33  type SegmentDataFactory = func(meta *SegmentEntry) data.Segment
    34  
    35  func compareSegmentFn(a, b *SegmentEntry) int {
    36  	return a.MetaBaseEntry.DoCompre(b.MetaBaseEntry)
    37  }
    38  
    39  type SegmentEntry struct {
    40  	*MetaBaseEntry
    41  	table   *TableEntry
    42  	entries map[uint64]*common.GenericDLNode[*BlockEntry]
    43  	//link.head and tail is nil when new a segmentEntry object.
    44  	link    *common.GenericSortedDList[*BlockEntry]
    45  	state   EntryState
    46  	sorted  bool
    47  	segData data.Segment
    48  }
    49  
    50  func NewSegmentEntry(table *TableEntry, txn txnif.AsyncTxn, state EntryState, dataFactory SegmentDataFactory) *SegmentEntry {
    51  	id := table.GetDB().catalog.NextSegment()
    52  	e := &SegmentEntry{
    53  		MetaBaseEntry: NewMetaBaseEntry(id),
    54  		table:         table,
    55  		link:          common.NewGenericSortedDList(compareBlockFn),
    56  		entries:       make(map[uint64]*common.GenericDLNode[*BlockEntry]),
    57  		state:         state,
    58  	}
    59  	e.CreateWithTxn(txn)
    60  	if dataFactory != nil {
    61  		e.segData = dataFactory(e)
    62  	}
    63  	return e
    64  }
    65  
    66  func NewReplaySegmentEntry() *SegmentEntry {
    67  	e := &SegmentEntry{
    68  		MetaBaseEntry: NewReplayMetaBaseEntry(),
    69  		link:          common.NewGenericSortedDList(compareBlockFn),
    70  		entries:       make(map[uint64]*common.GenericDLNode[*BlockEntry]),
    71  	}
    72  	return e
    73  }
    74  
    75  func NewStandaloneSegment(table *TableEntry, id uint64, ts types.TS) *SegmentEntry {
    76  	e := &SegmentEntry{
    77  		MetaBaseEntry: NewMetaBaseEntry(id),
    78  		table:         table,
    79  		link:          common.NewGenericSortedDList(compareBlockFn),
    80  		entries:       make(map[uint64]*common.GenericDLNode[*BlockEntry]),
    81  		state:         ES_Appendable,
    82  	}
    83  	e.CreateWithTS(ts)
    84  	return e
    85  }
    86  
    87  func NewSysSegmentEntry(table *TableEntry, id uint64) *SegmentEntry {
    88  	e := &SegmentEntry{
    89  		MetaBaseEntry: NewMetaBaseEntry(id),
    90  		table:         table,
    91  		link:          common.NewGenericSortedDList(compareBlockFn),
    92  		entries:       make(map[uint64]*common.GenericDLNode[*BlockEntry]),
    93  		state:         ES_Appendable,
    94  	}
    95  	e.CreateWithTS(types.SystemDBTS)
    96  	var bid uint64
    97  	if table.schema.Name == SystemTableSchema.Name {
    98  		bid = SystemBlock_Table_ID
    99  	} else if table.schema.Name == SystemDBSchema.Name {
   100  		bid = SystemBlock_DB_ID
   101  	} else if table.schema.Name == SystemColumnSchema.Name {
   102  		bid = SystemBlock_Columns_ID
   103  	} else {
   104  		panic("not supported")
   105  	}
   106  	block := NewSysBlockEntry(e, bid)
   107  	e.AddEntryLocked(block)
   108  	return e
   109  }
   110  
   111  func (entry *SegmentEntry) GetBlockEntryByID(id uint64) (blk *BlockEntry, err error) {
   112  	entry.RLock()
   113  	defer entry.RUnlock()
   114  	return entry.GetBlockEntryByIDLocked(id)
   115  }
   116  
   117  // XXX API like this, why do we need the error?   Isn't blk is nil enough?
   118  func (entry *SegmentEntry) GetBlockEntryByIDLocked(id uint64) (blk *BlockEntry, err error) {
   119  	node := entry.entries[id]
   120  	if node == nil {
   121  		err = moerr.GetOkExpectedEOB()
   122  		return
   123  	}
   124  	blk = node.GetPayload()
   125  	return
   126  }
   127  
   128  func (entry *SegmentEntry) MakeCommand(id uint32) (cmd txnif.TxnCmd, err error) {
   129  	cmdType := CmdUpdateSegment
   130  	entry.RLock()
   131  	defer entry.RUnlock()
   132  	return newSegmentCmd(id, cmdType, entry), nil
   133  }
   134  
   135  func (entry *SegmentEntry) Set1PC() {
   136  	entry.GetLatestNodeLocked().Set1PC()
   137  }
   138  func (entry *SegmentEntry) Is1PC() bool {
   139  	return entry.GetLatestNodeLocked().Is1PC()
   140  }
   141  func (entry *SegmentEntry) PPString(level common.PPLevel, depth int, prefix string) string {
   142  	var w bytes.Buffer
   143  	_, _ = w.WriteString(fmt.Sprintf("%s%s%s", common.RepeatStr("\t", depth), prefix, entry.StringWithLevel(level)))
   144  	if level == common.PPL0 {
   145  		return w.String()
   146  	}
   147  	it := entry.MakeBlockIt(true)
   148  	for it.Valid() {
   149  		block := it.Get().GetPayload()
   150  		block.RLock()
   151  		_ = w.WriteByte('\n')
   152  		_, _ = w.WriteString(block.PPString(level, depth+1, prefix))
   153  		block.RUnlock()
   154  		it.Next()
   155  	}
   156  	return w.String()
   157  }
   158  
   159  func (entry *SegmentEntry) StringLocked() string {
   160  	return entry.StringWithLevelLocked(common.PPL1)
   161  }
   162  
   163  func (entry *SegmentEntry) Repr() string {
   164  	id := entry.AsCommonID()
   165  	sorted := "-US"
   166  	if entry.sorted {
   167  		sorted = "-S"
   168  	}
   169  	return fmt.Sprintf("[%s%s]SEG[%s]", entry.state.Repr(), sorted, id.String())
   170  }
   171  
   172  func (entry *SegmentEntry) String() string {
   173  	entry.RLock()
   174  	defer entry.RUnlock()
   175  	return entry.StringLocked()
   176  }
   177  
   178  func (entry *SegmentEntry) StringWithLevel(level common.PPLevel) string {
   179  	entry.RLock()
   180  	defer entry.RUnlock()
   181  	return entry.StringWithLevelLocked(level)
   182  }
   183  
   184  func (entry *SegmentEntry) StringWithLevelLocked(level common.PPLevel) string {
   185  	sorted := "-US"
   186  	if entry.sorted {
   187  		sorted = "-S"
   188  	}
   189  	if level <= common.PPL1 {
   190  		return fmt.Sprintf("[%s%s]SEG[%d][C@%s,D@%s]",
   191  			entry.state.Repr(), sorted, entry.ID, entry.GetCreatedAt().ToString(), entry.GetDeleteAt().ToString())
   192  	}
   193  	return fmt.Sprintf("[%s%s]SEG%s", entry.state.Repr(), sorted, entry.MetaBaseEntry.StringLocked())
   194  }
   195  
   196  func (entry *SegmentEntry) BlockCnt() int {
   197  	return len(entry.entries)
   198  }
   199  
   200  func (entry *SegmentEntry) IsAppendable() bool {
   201  	return entry.state == ES_Appendable
   202  }
   203  
   204  func (entry *SegmentEntry) SetSorted() {
   205  	// modifing segment interface to supporte a borned sorted seg is verbose
   206  	// use Lock instead, the contention won't be intense
   207  	entry.Lock()
   208  	defer entry.Unlock()
   209  	entry.sorted = true
   210  }
   211  
   212  func (entry *SegmentEntry) IsSorted() bool {
   213  	entry.RLock()
   214  	defer entry.RUnlock()
   215  	return entry.sorted
   216  }
   217  
   218  func (entry *SegmentEntry) GetTable() *TableEntry {
   219  	return entry.table
   220  }
   221  
   222  func (entry *SegmentEntry) GetAppendableBlockCnt() int {
   223  	cnt := 0
   224  	it := entry.MakeBlockIt(true)
   225  	for it.Valid() {
   226  		if it.Get().GetPayload().IsAppendable() {
   227  			cnt++
   228  		}
   229  		it.Next()
   230  	}
   231  	return cnt
   232  }
   233  
   234  // GetNonAppendableBlockCnt Non-appendable segment only can contain non-appendable blocks;
   235  // Appendable segment can contain both of appendable blocks and non-appendable blocks
   236  func (entry *SegmentEntry) GetNonAppendableBlockCnt() int {
   237  	cnt := 0
   238  	it := entry.MakeBlockIt(true)
   239  	for it.Valid() {
   240  		if !it.Get().GetPayload().IsAppendable() {
   241  			cnt++
   242  		}
   243  		it.Next()
   244  	}
   245  	return cnt
   246  }
   247  
   248  func (entry *SegmentEntry) GetAppendableBlock() (blk *BlockEntry) {
   249  	it := entry.MakeBlockIt(false)
   250  	for it.Valid() {
   251  		itBlk := it.Get().GetPayload()
   252  		if itBlk.IsAppendable() {
   253  			blk = itBlk
   254  			break
   255  		}
   256  		it.Next()
   257  	}
   258  	return
   259  }
   260  func (entry *SegmentEntry) LastAppendableBlock() (blk *BlockEntry) {
   261  	it := entry.MakeBlockIt(false)
   262  	for it.Valid() {
   263  		itBlk := it.Get().GetPayload()
   264  		dropped := itBlk.HasDropCommitted()
   265  		if itBlk.IsAppendable() && !dropped {
   266  			blk = itBlk
   267  			break
   268  		}
   269  		it.Next()
   270  	}
   271  	return
   272  }
   273  
   274  func (entry *SegmentEntry) CreateBlock(txn txnif.AsyncTxn, state EntryState, dataFactory BlockDataFactory) (created *BlockEntry, err error) {
   275  	entry.Lock()
   276  	defer entry.Unlock()
   277  	created = NewBlockEntry(entry, txn, state, dataFactory)
   278  	entry.AddEntryLocked(created)
   279  	return
   280  }
   281  
   282  func (entry *SegmentEntry) CreateBlockWithMeta(
   283  	txn txnif.AsyncTxn,
   284  	state EntryState,
   285  	dataFactory BlockDataFactory,
   286  	metaLoc string,
   287  	deltaLoc string) (created *BlockEntry, err error) {
   288  	entry.Lock()
   289  	defer entry.Unlock()
   290  	created = NewBlockEntryWithMeta(entry, txn, state, dataFactory, metaLoc, deltaLoc)
   291  	entry.AddEntryLocked(created)
   292  	return
   293  }
   294  
   295  func (entry *SegmentEntry) DropBlockEntry(id uint64, txn txnif.AsyncTxn) (deleted *BlockEntry, err error) {
   296  	blk, err := entry.GetBlockEntryByID(id)
   297  	if err != nil {
   298  		return
   299  	}
   300  	blk.Lock()
   301  	defer blk.Unlock()
   302  	needWait, waitTxn := blk.NeedWaitCommitting(txn.GetStartTS())
   303  	if needWait {
   304  		blk.Unlock()
   305  		waitTxn.GetTxnState(true)
   306  		blk.Lock()
   307  	}
   308  	var isNewNode bool
   309  	isNewNode, err = blk.DropEntryLocked(txn)
   310  	if err == nil && isNewNode {
   311  		deleted = blk
   312  	}
   313  	return
   314  }
   315  
   316  func (entry *SegmentEntry) MakeBlockIt(reverse bool) *common.GenericSortedDListIt[*BlockEntry] {
   317  	entry.RLock()
   318  	defer entry.RUnlock()
   319  	return common.NewGenericSortedDListIt(entry.RWMutex, entry.link, reverse)
   320  }
   321  
   322  func (entry *SegmentEntry) AddEntryLocked(block *BlockEntry) {
   323  	n := entry.link.Insert(block)
   324  	entry.entries[block.GetID()] = n
   325  }
   326  
   327  func (entry *SegmentEntry) AsCommonID() *common.ID {
   328  	return &common.ID{
   329  		TableID:   entry.GetTable().GetID(),
   330  		SegmentID: entry.GetID(),
   331  	}
   332  }
   333  
   334  func (entry *SegmentEntry) GetCatalog() *Catalog { return entry.table.db.catalog }
   335  
   336  func (entry *SegmentEntry) InitData(factory DataFactory) {
   337  	if factory == nil {
   338  		return
   339  	}
   340  	dataFactory := factory.MakeSegmentFactory()
   341  	entry.segData = dataFactory(entry)
   342  }
   343  func (entry *SegmentEntry) GetSegmentData() data.Segment { return entry.segData }
   344  
   345  func (entry *SegmentEntry) deleteEntryLocked(block *BlockEntry) error {
   346  	if n, ok := entry.entries[block.GetID()]; !ok {
   347  		return moerr.GetOkExpectedEOB()
   348  	} else {
   349  		entry.link.Delete(n)
   350  		delete(entry.entries, block.GetID())
   351  	}
   352  	// block.blkData.Close()
   353  	// block.blkData = nil
   354  	return nil
   355  }
   356  func (entry *SegmentEntry) Close() {
   357  	blks := entry.getAllBlks()
   358  	entry.Lock()
   359  	defer entry.Unlock()
   360  	for _, blk := range blks {
   361  		err := entry.deleteEntryLocked(blk)
   362  		if err != nil {
   363  			panic(err)
   364  		}
   365  	}
   366  }
   367  
   368  func (entry *SegmentEntry) getAllBlks() []*BlockEntry {
   369  	blks := make([]*BlockEntry, 0)
   370  	it := entry.MakeBlockIt(false)
   371  	for it.Valid() {
   372  		blks = append(blks, it.Get().GetPayload())
   373  		it.Next()
   374  	}
   375  	return blks
   376  }
   377  func (entry *SegmentEntry) RemoveEntry(block *BlockEntry) (err error) {
   378  	logutil.Debug("[Catalog]", common.OperationField("remove"),
   379  		common.OperandField(block.String()))
   380  	entry.Lock()
   381  	defer entry.Unlock()
   382  	return entry.deleteEntryLocked(block)
   383  }
   384  
   385  func (entry *SegmentEntry) PrepareRollback() (err error) {
   386  	var isEmpty bool
   387  	if isEmpty, err = entry.MetaBaseEntry.PrepareRollback(); err != nil {
   388  		return
   389  	}
   390  	if isEmpty {
   391  		if err = entry.GetTable().RemoveEntry(entry); err != nil {
   392  			return
   393  		}
   394  	}
   395  	return
   396  }
   397  
   398  func (entry *SegmentEntry) WriteTo(w io.Writer) (n int64, err error) {
   399  	sn := int64(0)
   400  	if sn, err = entry.MetaBaseEntry.WriteAllTo(w); err != nil {
   401  		return
   402  	}
   403  	if err = binary.Write(w, binary.BigEndian, entry.state); err != nil {
   404  		return
   405  	}
   406  	n = sn + 1
   407  	if err = binary.Write(w, binary.BigEndian, entry.sorted); err != nil {
   408  		return
   409  	}
   410  	n = sn + 1
   411  	return
   412  }
   413  
   414  func (entry *SegmentEntry) ReadFrom(r io.Reader) (n int64, err error) {
   415  	if n, err = entry.MetaBaseEntry.ReadAllFrom(r); err != nil {
   416  		return
   417  	}
   418  	if err = binary.Read(r, binary.BigEndian, &entry.state); err != nil {
   419  		return
   420  	}
   421  	n += 1
   422  	if err = binary.Read(r, binary.BigEndian, &entry.sorted); err != nil {
   423  		return
   424  	}
   425  	n += 1
   426  	return
   427  }
   428  
   429  func (entry *SegmentEntry) GetScheduler() tasks.TaskScheduler {
   430  	return entry.GetTable().GetCatalog().GetScheduler()
   431  }
   432  
   433  func (entry *SegmentEntry) CollectBlockEntries(commitFilter func(be *MetaBaseEntry) bool, blockFilter func(be *BlockEntry) bool) []*BlockEntry {
   434  	blks := make([]*BlockEntry, 0)
   435  	blkIt := entry.MakeBlockIt(true)
   436  	for blkIt.Valid() {
   437  		blk := blkIt.Get().GetPayload()
   438  		blk.RLock()
   439  		if commitFilter != nil && blockFilter != nil {
   440  			if commitFilter(blk.MetaBaseEntry) && blockFilter(blk) {
   441  				blks = append(blks, blk)
   442  			}
   443  		} else if blockFilter != nil {
   444  			if blockFilter(blk) {
   445  				blks = append(blks, blk)
   446  			}
   447  		} else if commitFilter != nil {
   448  			if commitFilter(blk.MetaBaseEntry) {
   449  				blks = append(blks, blk)
   450  			}
   451  		}
   452  		blk.RUnlock()
   453  		blkIt.Next()
   454  	}
   455  	return blks
   456  }
   457  
   458  // IsActive is coarse API: no consistency check
   459  func (entry *SegmentEntry) IsActive() bool {
   460  	table := entry.GetTable()
   461  	if !table.IsActive() {
   462  		return false
   463  	}
   464  	return !entry.HasDropCommitted()
   465  }
   466  
   467  func (entry *SegmentEntry) TreeMaxDropCommitEntry() BaseEntry {
   468  	table := entry.GetTable()
   469  	db := table.GetDB()
   470  	if db.HasDropCommittedLocked() {
   471  		return db.DBBaseEntry
   472  	}
   473  	if table.HasDropCommittedLocked() {
   474  		return table.TableBaseEntry
   475  	}
   476  	if entry.HasDropCommittedLocked() {
   477  		return entry.MetaBaseEntry
   478  	}
   479  	return nil
   480  }
   481  
   482  // GetTerminationTS is coarse API: no consistency check
   483  func (entry *SegmentEntry) GetTerminationTS() (ts types.TS, terminated bool) {
   484  	tableEntry := entry.GetTable()
   485  	dbEntry := tableEntry.GetDB()
   486  
   487  	dbEntry.RLock()
   488  	terminated, ts = dbEntry.TryGetTerminatedTS(true)
   489  	if terminated {
   490  		dbEntry.RUnlock()
   491  		return
   492  	}
   493  	dbEntry.RUnlock()
   494  
   495  	tableEntry.RLock()
   496  	terminated, ts = tableEntry.TryGetTerminatedTS(true)
   497  	tableEntry.RUnlock()
   498  	return
   499  }