github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/logtail/collector.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  	"bytes"
    19  	"fmt"
    20  	"sync"
    21  	"sync/atomic"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    24  	"github.com/matrixorigin/matrixone/pkg/container/types"
    25  	"github.com/matrixorigin/matrixone/pkg/logutil"
    26  	"github.com/matrixorigin/matrixone/pkg/txn/clock"
    27  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog"
    28  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common"
    29  	"github.com/tidwall/btree"
    30  )
    31  
    32  type Collector interface {
    33  	String() string
    34  	Run()
    35  	ScanInRange(from, to types.TS) (*DirtyTreeEntry, int)
    36  	ScanInRangePruned(from, to types.TS) *DirtyTreeEntry
    37  	GetAndRefreshMerged() *DirtyTreeEntry
    38  	Merge() *DirtyTreeEntry
    39  	GetMaxLSN(from, to types.TS) uint64
    40  	Init(maxts types.TS)
    41  }
    42  
    43  type DirtyEntryInterceptor = catalog.Processor
    44  
    45  type DirtyTreeEntry struct {
    46  	sync.RWMutex
    47  	start, end types.TS
    48  	tree       *common.Tree
    49  }
    50  
    51  func NewEmptyDirtyTreeEntry() *DirtyTreeEntry {
    52  	return &DirtyTreeEntry{
    53  		tree: common.NewTree(),
    54  	}
    55  }
    56  
    57  func NewDirtyTreeEntry(start, end types.TS, tree *common.Tree) *DirtyTreeEntry {
    58  	entry := NewEmptyDirtyTreeEntry()
    59  	entry.start = start
    60  	entry.end = end
    61  	entry.tree = tree
    62  	return entry
    63  }
    64  
    65  func (entry *DirtyTreeEntry) Merge(o *DirtyTreeEntry) {
    66  	if entry.start.Greater(o.start) {
    67  		entry.start = o.start
    68  	}
    69  	if entry.end.Less(o.end) {
    70  		entry.end = o.end
    71  	}
    72  	entry.tree.Merge(o.tree)
    73  }
    74  
    75  func (entry *DirtyTreeEntry) IsEmpty() bool {
    76  	return entry.tree.IsEmpty()
    77  }
    78  
    79  func (entry *DirtyTreeEntry) GetTimeRange() (from, to types.TS) {
    80  	return entry.start, entry.end
    81  }
    82  
    83  func (entry *DirtyTreeEntry) GetTree() (tree *common.Tree) {
    84  	return entry.tree
    85  }
    86  
    87  func (entry *DirtyTreeEntry) String() string {
    88  	var buf bytes.Buffer
    89  	_, _ = buf.WriteString(
    90  		fmt.Sprintf("DirtyTreeEntry[%s=>%s]\n",
    91  			entry.start.ToString(),
    92  			entry.end.ToString()))
    93  	_, _ = buf.WriteString(entry.tree.String())
    94  	return buf.String()
    95  }
    96  
    97  type dirtyCollector struct {
    98  	// sourcer
    99  	sourcer *Manager
   100  
   101  	// context
   102  	catalog     *catalog.Catalog
   103  	clock       *types.TsAlloctor
   104  	interceptor DirtyEntryInterceptor
   105  
   106  	// storage
   107  	storage struct {
   108  		sync.RWMutex
   109  		entries *btree.BTreeG[*DirtyTreeEntry]
   110  		maxTs   types.TS
   111  	}
   112  	merged atomic.Pointer[DirtyTreeEntry]
   113  }
   114  
   115  func NewDirtyCollector(
   116  	sourcer *Manager,
   117  	clock clock.Clock,
   118  	catalog *catalog.Catalog,
   119  	interceptor DirtyEntryInterceptor) *dirtyCollector {
   120  	collector := &dirtyCollector{
   121  		sourcer:     sourcer,
   122  		catalog:     catalog,
   123  		interceptor: interceptor,
   124  		clock:       types.NewTsAlloctor(clock),
   125  	}
   126  	collector.storage.entries = btree.NewBTreeGOptions(
   127  		func(a, b *DirtyTreeEntry) bool {
   128  			return a.start.Less(b.start) && a.end.Less(b.end)
   129  		}, btree.Options{
   130  			NoLocks: true,
   131  		})
   132  
   133  	collector.merged.Store(NewEmptyDirtyTreeEntry())
   134  	return collector
   135  }
   136  func (d *dirtyCollector) Init(maxts types.TS) {
   137  	d.storage.maxTs = maxts
   138  }
   139  func (d *dirtyCollector) Run() {
   140  	from, to := d.findRange()
   141  
   142  	// stale range found, skip this run
   143  	if to.IsEmpty() {
   144  		return
   145  	}
   146  
   147  	d.rangeScanAndUpdate(from, to)
   148  	d.cleanupStorage()
   149  	d.GetAndRefreshMerged()
   150  }
   151  
   152  func (d *dirtyCollector) ScanInRangePruned(from, to types.TS) (
   153  	tree *DirtyTreeEntry) {
   154  	tree, _ = d.ScanInRange(from, to)
   155  	if err := d.tryCompactTree(d.interceptor, tree.tree); err != nil {
   156  		panic(err)
   157  	}
   158  	return
   159  }
   160  
   161  func (d *dirtyCollector) GetMaxLSN(from, to types.TS) uint64 {
   162  	reader := d.sourcer.GetReader(from, to)
   163  	return reader.GetMaxLSN()
   164  }
   165  func (d *dirtyCollector) ScanInRange(from, to types.TS) (
   166  	entry *DirtyTreeEntry, count int) {
   167  	reader := d.sourcer.GetReader(from, to)
   168  	tree, count := reader.GetDirty()
   169  
   170  	// make a entry
   171  	entry = &DirtyTreeEntry{
   172  		start: from,
   173  		end:   to,
   174  		tree:  tree,
   175  	}
   176  	return
   177  }
   178  
   179  // DirtyCount returns unflushed table, segment, block count
   180  func (d *dirtyCollector) DirtyCount() (tblCnt, segCnt, blkCnt int) {
   181  	merged := d.GetAndRefreshMerged()
   182  	tblCnt = merged.tree.TableCount()
   183  	for _, tblTree := range merged.tree.Tables {
   184  		segCnt += len(tblTree.Segs)
   185  		for _, segTree := range tblTree.Segs {
   186  			blkCnt += len(segTree.Blks)
   187  		}
   188  	}
   189  	return
   190  }
   191  
   192  func (d *dirtyCollector) String() string {
   193  	merged := d.GetAndRefreshMerged()
   194  	return merged.tree.String()
   195  }
   196  
   197  func (d *dirtyCollector) GetAndRefreshMerged() (merged *DirtyTreeEntry) {
   198  	merged = d.merged.Load()
   199  	d.storage.RLock()
   200  	maxTs := d.storage.maxTs
   201  	d.storage.RUnlock()
   202  	if maxTs.LessEq(merged.end) {
   203  		return
   204  	}
   205  	merged = d.Merge()
   206  	d.tryUpdateMerged(merged)
   207  	return
   208  }
   209  
   210  func (d *dirtyCollector) Merge() *DirtyTreeEntry {
   211  	// get storage snapshot and work on it
   212  	snapshot, maxTs := d.getStorageSnapshot()
   213  
   214  	merged := NewEmptyDirtyTreeEntry()
   215  	merged.end = maxTs
   216  
   217  	// scan base on the snapshot
   218  	// merge all trees of the entry
   219  	snapshot.Scan(func(entry *DirtyTreeEntry) bool {
   220  		entry.RLock()
   221  		defer entry.RUnlock()
   222  		merged.tree.Merge(entry.tree)
   223  		return true
   224  	})
   225  
   226  	return merged
   227  }
   228  
   229  func (d *dirtyCollector) tryUpdateMerged(merged *DirtyTreeEntry) (updated bool) {
   230  	var old *DirtyTreeEntry
   231  	for {
   232  		old = d.merged.Load()
   233  		if old.end.GreaterEq(merged.end) {
   234  			break
   235  		}
   236  		if d.merged.CompareAndSwap(old, merged) {
   237  			updated = true
   238  			break
   239  		}
   240  	}
   241  	return
   242  }
   243  
   244  func (d *dirtyCollector) findRange() (from, to types.TS) {
   245  	now := d.clock.Alloc()
   246  	d.storage.RLock()
   247  	defer d.storage.RUnlock()
   248  	if now.LessEq(d.storage.maxTs) {
   249  		return
   250  	}
   251  	from, to = d.storage.maxTs.Next(), now
   252  	return
   253  }
   254  
   255  func (d *dirtyCollector) rangeScanAndUpdate(from, to types.TS) (updated bool) {
   256  	entry, _ := d.ScanInRange(from, to)
   257  
   258  	// try to store the entry
   259  	updated = d.tryStoreEntry(entry)
   260  	return
   261  }
   262  
   263  func (d *dirtyCollector) tryStoreEntry(entry *DirtyTreeEntry) (ok bool) {
   264  	ok = true
   265  	d.storage.Lock()
   266  	defer d.storage.Unlock()
   267  
   268  	// storage was updated before
   269  	if !entry.start.Equal(d.storage.maxTs.Next()) {
   270  		ok = false
   271  		return
   272  	}
   273  
   274  	// update storage maxTs
   275  	d.storage.maxTs = entry.end
   276  
   277  	// don't store empty entry
   278  	if entry.tree.IsEmpty() {
   279  		return
   280  	}
   281  
   282  	d.storage.entries.Set(entry)
   283  	return
   284  }
   285  
   286  func (d *dirtyCollector) getStorageSnapshot() (ss *btree.BTreeG[*DirtyTreeEntry], ts types.TS) {
   287  	d.storage.Lock()
   288  	defer d.storage.Unlock()
   289  	ss = d.storage.entries.Copy()
   290  	ts = d.storage.maxTs
   291  	return
   292  }
   293  
   294  // Scan current dirty entries, remove all flushed or not found ones, and drive interceptor on remaining block entries.
   295  func (d *dirtyCollector) cleanupStorage() {
   296  	toDeletes := make([]*DirtyTreeEntry, 0)
   297  
   298  	// get a snapshot of entries
   299  	entries, _ := d.getStorageSnapshot()
   300  
   301  	// scan all entries in the storage
   302  	// try compact the dirty tree for each entry
   303  	// if the dirty tree is empty, delete the specified entry from the storage
   304  	entries.Scan(func(entry *DirtyTreeEntry) bool {
   305  		entry.Lock()
   306  		defer entry.Unlock()
   307  		// dirty blocks within the time range has been flushed
   308  		// exclude the related dirty tree from the foreset
   309  		if entry.tree.IsEmpty() {
   310  			toDeletes = append(toDeletes, entry)
   311  			return true
   312  		}
   313  		if err := d.tryCompactTree(d.interceptor, entry.tree); err != nil {
   314  			logutil.Warnf("error: interceptor on dirty tree: %v", err)
   315  		}
   316  		if entry.tree.IsEmpty() {
   317  			toDeletes = append(toDeletes, entry)
   318  		}
   319  		return true
   320  	})
   321  
   322  	if len(toDeletes) == 0 {
   323  		return
   324  	}
   325  
   326  	// remove entries with empty dirty tree from the storage
   327  	d.storage.Lock()
   328  	defer d.storage.Unlock()
   329  	for _, tree := range toDeletes {
   330  		d.storage.entries.Delete(tree)
   331  	}
   332  }
   333  
   334  // iter the tree and call interceptor to process block. flushed block, empty seg and table will be removed from the tree
   335  func (d *dirtyCollector) tryCompactTree(
   336  	interceptor DirtyEntryInterceptor,
   337  	tree *common.Tree) (err error) {
   338  	var (
   339  		db  *catalog.DBEntry
   340  		tbl *catalog.TableEntry
   341  		seg *catalog.SegmentEntry
   342  		blk *catalog.BlockEntry
   343  	)
   344  	for id, dirtyTable := range tree.Tables {
   345  		// remove empty tables
   346  		if dirtyTable.Compact() {
   347  			tree.Shrink(id)
   348  			continue
   349  		}
   350  
   351  		if db, err = d.catalog.GetDatabaseByID(dirtyTable.DbID); err != nil {
   352  			if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) {
   353  				tree.Shrink(id)
   354  				err = nil
   355  				continue
   356  			}
   357  			break
   358  		}
   359  		if tbl, err = db.GetTableEntryByID(dirtyTable.ID); err != nil {
   360  			if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) {
   361  				tree.Shrink(id)
   362  				err = nil
   363  				continue
   364  			}
   365  			break
   366  		}
   367  
   368  		for id, dirtySeg := range dirtyTable.Segs {
   369  			// remove empty segs
   370  			if dirtySeg.IsEmpty() {
   371  				dirtyTable.Shrink(id)
   372  				continue
   373  			}
   374  			if seg, err = tbl.GetSegmentByID(dirtySeg.ID); err != nil {
   375  				if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) {
   376  					dirtyTable.Shrink(id)
   377  					err = nil
   378  					continue
   379  				}
   380  				return
   381  			}
   382  			for id := range dirtySeg.Blks {
   383  				if blk, err = seg.GetBlockEntryByID(id); err != nil {
   384  					if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) {
   385  						dirtySeg.Shrink(id)
   386  						err = nil
   387  						continue
   388  					}
   389  					return
   390  				}
   391  				if blk.GetBlockData().RunCalibration() == 0 {
   392  					// TODO: may be put it to post replay process
   393  					// FIXME
   394  					if blk.HasPersistedData() {
   395  						blk.GetBlockData().TryUpgrade()
   396  					}
   397  					dirtySeg.Shrink(id)
   398  					continue
   399  				}
   400  				if err = interceptor.OnBlock(blk); err != nil {
   401  					return
   402  				}
   403  			}
   404  		}
   405  	}
   406  	tree.Compact()
   407  	return
   408  }