github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/logtail/table.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/container/types"
    24  	"github.com/matrixorigin/matrixone/pkg/logutil"
    25  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    26  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/model"
    27  	"go.uber.org/zap"
    28  )
    29  
    30  type RowT = *txnRow
    31  type BlockT = *txnBlock
    32  
    33  type summary struct {
    34  	hasCatalogChanges bool
    35  	// TODO
    36  	// table ids
    37  	// maxLsn
    38  }
    39  
    40  type txnRow struct {
    41  	txnif.AsyncTxn
    42  }
    43  
    44  func (row *txnRow) Length() int             { return 1 }
    45  func (row *txnRow) Window(_, _ int) *txnRow { return nil }
    46  
    47  type txnBlock struct {
    48  	sync.RWMutex
    49  	bornTS  types.TS
    50  	rows    []*txnRow
    51  	summary atomic.Pointer[summary]
    52  }
    53  
    54  func (blk *txnBlock) Length() int {
    55  	blk.RLock()
    56  	defer blk.RUnlock()
    57  	return len(blk.rows)
    58  }
    59  
    60  func (blk *txnBlock) IsAppendable() bool {
    61  	return blk != nil
    62  }
    63  
    64  func (blk *txnBlock) Append(row *txnRow) (err error) {
    65  	blk.Lock()
    66  	defer blk.Unlock()
    67  	blk.rows = append(blk.rows, row)
    68  	return
    69  }
    70  
    71  func (blk *txnBlock) Close() {
    72  	blk.Lock()
    73  	defer blk.Unlock()
    74  	blk.bornTS = types.TS{}
    75  	blk.rows = make([]*txnRow, 0)
    76  }
    77  
    78  func (blk *txnBlock) trySumary() {
    79  	summary := new(summary)
    80  	for _, row := range blk.rows {
    81  		if row.GetMemo().HasCatalogChanges() {
    82  			summary.hasCatalogChanges = true
    83  			break
    84  		}
    85  	}
    86  	blk.summary.CompareAndSwap(nil, summary)
    87  }
    88  
    89  func (blk *txnBlock) ForeachRowInBetween(
    90  	from, to types.TS,
    91  	rowOp func(row RowT) (goNext bool),
    92  ) (outOfRange bool, readRows int) {
    93  	var rows []*txnRow
    94  	if blk.summary.Load() == nil {
    95  		blk.RLock()
    96  		rows = blk.rows[:len(blk.rows)]
    97  		capacity := cap(blk.rows)
    98  		blk.RUnlock()
    99  		if capacity == len(rows) && blk.summary.Load() == nil {
   100  			blk.trySumary()
   101  		}
   102  	} else {
   103  		rows = blk.rows
   104  	}
   105  	for _, row := range rows {
   106  		readRows += 1
   107  		ts := row.GetPrepareTS()
   108  		if ts.IsEmpty() || ts.Greater(to) {
   109  			outOfRange = true
   110  			return
   111  		}
   112  		if ts.Less(from) {
   113  			continue
   114  		}
   115  
   116  		if !rowOp(row) {
   117  			outOfRange = true
   118  			return
   119  		}
   120  	}
   121  	return
   122  }
   123  
   124  func (blk *txnBlock) String() string {
   125  	length := blk.Length()
   126  	var buf bytes.Buffer
   127  	_, _ = buf.WriteString(
   128  		fmt.Sprintf("TXNBLK-[%s][Len=%d]", blk.bornTS.ToString(), length))
   129  	return buf.String()
   130  }
   131  
   132  type TxnTable struct {
   133  	*model.AOT[BlockT, RowT]
   134  }
   135  
   136  func blockCompareFn(a, b BlockT) bool {
   137  	return a.bornTS.Less(b.bornTS)
   138  }
   139  
   140  func timeBasedTruncateFactory(ts types.TS) func(b BlockT) bool {
   141  	return func(b BlockT) bool {
   142  		return b.bornTS.GreaterEq(ts)
   143  	}
   144  }
   145  
   146  func NewTxnTable(blockSize int, clock *types.TsAlloctor) *TxnTable {
   147  	factory := func(row RowT) BlockT {
   148  		ts := row.GetPrepareTS()
   149  		if ts == txnif.UncommitTS {
   150  			ts = clock.Alloc()
   151  		}
   152  		return &txnBlock{
   153  			bornTS: ts,
   154  			rows:   make([]*txnRow, 0, blockSize),
   155  		}
   156  	}
   157  	return &TxnTable{
   158  		AOT: model.NewAOT(
   159  			blockSize,
   160  			factory,
   161  			blockCompareFn,
   162  		),
   163  	}
   164  }
   165  
   166  func (table *TxnTable) AddTxn(txn txnif.AsyncTxn) (err error) {
   167  	row := &txnRow{
   168  		AsyncTxn: txn,
   169  	}
   170  	err = table.Append(row)
   171  	return
   172  }
   173  
   174  func (table *TxnTable) TruncateByTimeStamp(ts types.TS) (cnt int) {
   175  	filter := timeBasedTruncateFactory(ts)
   176  	return table.Truncate(filter)
   177  }
   178  
   179  func (table *TxnTable) ForeachRowInBetween(
   180  	from, to types.TS,
   181  	skipBlkOp func(blk BlockT) bool,
   182  	rowOp func(row RowT) (goNext bool),
   183  ) (readRows int) {
   184  	snapshot := table.Snapshot()
   185  	pivot := &txnBlock{bornTS: from}
   186  	outOfLeft := true
   187  	snapshot.Descend(pivot, func(blk BlockT) bool {
   188  		pivot.bornTS = blk.bornTS
   189  		outOfLeft = false
   190  		return false
   191  	})
   192  
   193  	// from is smaller than the very first block and it is not special like 0-0, 0-1, 1-0
   194  	if outOfLeft && from.Greater(types.BuildTS(1, 1)) {
   195  		minTs := types.TS{}
   196  		snapshot.Ascend(&txnBlock{}, func(blk *txnBlock) bool {
   197  			minTs = blk.bornTS
   198  			return false
   199  		})
   200  		logutil.Warn("[logtail] fetch with too small ts", zap.String("ts", from.ToString()), zap.String("minTs", minTs.ToString()))
   201  	}
   202  	snapshot.Ascend(pivot, func(blk BlockT) bool {
   203  		if blk.bornTS.Greater(to) {
   204  			return false
   205  		}
   206  
   207  		if skipBlkOp != nil && skipBlkOp(blk) {
   208  			return blk.rows[len(blk.rows)-1].GetPrepareTS().LessEq(to)
   209  		}
   210  		outOfRange, cnt := blk.ForeachRowInBetween(
   211  			from,
   212  			to,
   213  			rowOp,
   214  		)
   215  		readRows += cnt
   216  
   217  		return !outOfRange
   218  	})
   219  	return
   220  }