github.com/matrixorigin/matrixone@v1.2.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/db/dbutils"
    26  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    27  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/model"
    28  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnbase"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  type RowT = *txnRow
    33  type BlockT = *txnBlock
    34  
    35  type smallTxn struct {
    36  	memo      *txnif.TxnMemo
    37  	startTS   types.TS
    38  	prepareTS types.TS
    39  	state     txnif.TxnState
    40  	lsn       uint64
    41  }
    42  
    43  func (txn *smallTxn) GetMemo() *txnif.TxnMemo {
    44  	return txn.memo
    45  }
    46  
    47  func (txn *smallTxn) GetTxnState(_ bool) txnif.TxnState {
    48  	return txn.state
    49  }
    50  
    51  func (txn *smallTxn) GetStartTS() types.TS {
    52  	return txn.startTS
    53  }
    54  
    55  func (txn *smallTxn) GetPrepareTS() types.TS {
    56  	return txn.prepareTS
    57  }
    58  
    59  func (txn *smallTxn) GetCommitTS() types.TS {
    60  	return txn.prepareTS
    61  }
    62  
    63  func (txn *smallTxn) GetLSN() uint64 {
    64  	return txn.lsn
    65  }
    66  
    67  type summary struct {
    68  	hasCatalogChanges bool
    69  	// TODO
    70  	// table ids
    71  	// maxLsn
    72  }
    73  
    74  type txnRow struct {
    75  	source atomic.Pointer[txnbase.Txn]
    76  	packed atomic.Pointer[smallTxn]
    77  }
    78  
    79  func (row *txnRow) GetLSN() uint64 {
    80  	if txn := row.source.Load(); txn != nil {
    81  		return txn.GetLSN()
    82  	}
    83  	txn := row.packed.Load()
    84  	return txn.GetLSN()
    85  }
    86  
    87  func (row *txnRow) GetMemo() *txnif.TxnMemo {
    88  	if txn := row.source.Load(); txn != nil {
    89  		return txn.GetMemo()
    90  	}
    91  	txn := row.packed.Load()
    92  	return txn.GetMemo()
    93  }
    94  
    95  func (row *txnRow) GetTxnState(waitIfcommitting bool) txnif.TxnState {
    96  	if txn := row.source.Load(); txn != nil {
    97  		return txn.GetTxnState(waitIfcommitting)
    98  	}
    99  	txn := row.packed.Load()
   100  	return txn.GetTxnState(waitIfcommitting)
   101  }
   102  
   103  func (row *txnRow) GetStartTS() types.TS {
   104  	if txn := row.source.Load(); txn != nil {
   105  		return txn.GetStartTS()
   106  	}
   107  	txn := row.packed.Load()
   108  	return txn.GetStartTS()
   109  }
   110  
   111  func (row *txnRow) GetPrepareTS() types.TS {
   112  	if txn := row.source.Load(); txn != nil {
   113  		return txn.GetPrepareTS()
   114  	}
   115  	txn := row.packed.Load()
   116  	return txn.GetPrepareTS()
   117  }
   118  
   119  func (row *txnRow) GetCommitTS() types.TS {
   120  	if txn := row.source.Load(); txn != nil {
   121  		return txn.GetCommitTS()
   122  	}
   123  	txn := row.packed.Load()
   124  	return txn.GetCommitTS()
   125  }
   126  
   127  func (row *txnRow) Length() int             { return 1 }
   128  func (row *txnRow) Window(_, _ int) *txnRow { return nil }
   129  
   130  func (row *txnRow) IsCompacted() bool {
   131  	return row.packed.Load() != nil
   132  }
   133  
   134  func (row *txnRow) TryCompact() (compacted, changed bool) {
   135  	if txn := row.source.Load(); txn == nil {
   136  		return true, false
   137  	} else {
   138  		state := txn.GetTxnState(false)
   139  		if state == txnif.TxnStateCommitted || state == txnif.TxnStateRollbacked {
   140  			packed := &smallTxn{
   141  				memo:      txn.GetMemo(),
   142  				startTS:   txn.GetStartTS(),
   143  				prepareTS: txn.GetPrepareTS(),
   144  				state:     state,
   145  				lsn:       txn.GetLSN(),
   146  			}
   147  			row.packed.Store(packed)
   148  			row.source.Store(nil)
   149  			return true, true
   150  		}
   151  	}
   152  	return false, false
   153  }
   154  
   155  type txnBlock struct {
   156  	sync.RWMutex
   157  	bornTS  types.TS
   158  	rows    []*txnRow
   159  	summary atomic.Pointer[summary]
   160  }
   161  
   162  func (blk *txnBlock) Length() int {
   163  	blk.RLock()
   164  	defer blk.RUnlock()
   165  	return len(blk.rows)
   166  }
   167  
   168  func (blk *txnBlock) TryCompact() (all bool, changed bool) {
   169  	blk.RLock()
   170  	defer blk.RUnlock()
   171  	if len(blk.rows) < cap(blk.rows) {
   172  		return false, false
   173  	}
   174  	if blk.rows[len(blk.rows)-1].IsCompacted() {
   175  		return true, false
   176  	}
   177  	all = true
   178  	for _, row := range blk.rows {
   179  		if compacted, _ := row.TryCompact(); !compacted {
   180  			all = false
   181  			break
   182  		}
   183  	}
   184  	if all {
   185  		changed = true
   186  	}
   187  	return
   188  }
   189  
   190  func (blk *txnBlock) IsAppendable() bool {
   191  	return blk != nil
   192  }
   193  
   194  func (blk *txnBlock) Append(row *txnRow) (err error) {
   195  	blk.Lock()
   196  	defer blk.Unlock()
   197  	blk.rows = append(blk.rows, row)
   198  	return
   199  }
   200  
   201  func (blk *txnBlock) Close() {
   202  	blk.Lock()
   203  	defer blk.Unlock()
   204  	blk.bornTS = types.TS{}
   205  	blk.rows = make([]*txnRow, 0)
   206  }
   207  
   208  func (blk *txnBlock) trySumary() {
   209  	summary := new(summary)
   210  	for _, row := range blk.rows {
   211  		if row.GetMemo().HasCatalogChanges() {
   212  			summary.hasCatalogChanges = true
   213  			break
   214  		}
   215  	}
   216  	blk.summary.CompareAndSwap(nil, summary)
   217  }
   218  
   219  func (blk *txnBlock) ForeachRowInBetween(
   220  	from, to types.TS,
   221  	rowOp func(row RowT) (goNext bool),
   222  ) (outOfRange bool, readRows int) {
   223  	var rows []*txnRow
   224  	if blk.summary.Load() == nil {
   225  		blk.RLock()
   226  		rows = blk.rows[:len(blk.rows)]
   227  		capacity := cap(blk.rows)
   228  		blk.RUnlock()
   229  		if capacity == len(rows) && blk.summary.Load() == nil {
   230  			blk.trySumary()
   231  		}
   232  	} else {
   233  		rows = blk.rows
   234  	}
   235  	for _, row := range rows {
   236  		readRows += 1
   237  		ts := row.GetPrepareTS()
   238  		if ts.IsEmpty() || ts.Greater(&to) {
   239  			outOfRange = true
   240  			return
   241  		}
   242  		if ts.Less(&from) {
   243  			continue
   244  		}
   245  
   246  		if !rowOp(row) {
   247  			outOfRange = true
   248  			return
   249  		}
   250  	}
   251  	return
   252  }
   253  
   254  func (blk *txnBlock) String() string {
   255  	length := blk.Length()
   256  	var buf bytes.Buffer
   257  	_, _ = buf.WriteString(
   258  		fmt.Sprintf("TXNBLK-[%s][Len=%d]", blk.bornTS.ToString(), length))
   259  	return buf.String()
   260  }
   261  
   262  type TxnTable struct {
   263  	*model.AOT[BlockT, RowT]
   264  }
   265  
   266  func (blk *txnBlock) Less(b BlockT) bool {
   267  	return blk.bornTS.Less(&b.bornTS)
   268  }
   269  
   270  func timeBasedTruncateFactory(ts types.TS) func(b BlockT) bool {
   271  	return func(b BlockT) bool {
   272  		return b.bornTS.GreaterEq(&ts)
   273  	}
   274  }
   275  
   276  func NewTxnTable(blockSize int, nowClock func() types.TS) *TxnTable {
   277  	factory := func(row RowT) BlockT {
   278  		ts := row.GetPrepareTS()
   279  		if ts == txnif.UncommitTS {
   280  			ts = nowClock()
   281  		}
   282  		return &txnBlock{
   283  			bornTS: ts,
   284  			rows:   make([]*txnRow, 0, blockSize),
   285  		}
   286  	}
   287  	return &TxnTable{
   288  		AOT: model.NewAOT(
   289  			blockSize,
   290  			factory,
   291  			(*txnBlock).Less,
   292  		),
   293  	}
   294  }
   295  
   296  func (table *TxnTable) TryCompact(from types.TS, rt *dbutils.Runtime) (to types.TS) {
   297  	snapshot := table.Snapshot()
   298  	snapshot.Ascend(
   299  		&txnBlock{bornTS: from},
   300  		func(blk BlockT) bool {
   301  			allCompacted, changed := blk.TryCompact()
   302  			if changed {
   303  				rt.Logtail.CompactStats.Add(1)
   304  			}
   305  			to = blk.bornTS
   306  			return allCompacted
   307  		})
   308  	return
   309  }
   310  
   311  func (table *TxnTable) AddTxn(txn txnif.AsyncTxn) (err error) {
   312  	row := &txnRow{}
   313  	row.source.Store(txn.GetBase().(*txnbase.Txn))
   314  	err = table.Append(row)
   315  	return
   316  }
   317  
   318  func (table *TxnTable) TruncateByTimeStamp(ts types.TS) (cnt int) {
   319  	filter := timeBasedTruncateFactory(ts)
   320  	return table.Truncate(filter)
   321  }
   322  
   323  func (table *TxnTable) ForeachRowInBetween(
   324  	from, to types.TS,
   325  	skipBlkOp func(blk BlockT) bool,
   326  	rowOp func(row RowT) (goNext bool),
   327  ) (readRows int) {
   328  	snapshot := table.Snapshot()
   329  	pivot := &txnBlock{bornTS: from}
   330  	outOfLeft := true
   331  	snapshot.Descend(pivot, func(blk BlockT) bool {
   332  		pivot.bornTS = blk.bornTS
   333  		outOfLeft = false
   334  		return false
   335  	})
   336  
   337  	// from is smaller than the very first block and it is not special like 0-0, 0-1, 1-0
   338  	ts := types.BuildTS(1, 1)
   339  	if outOfLeft && from.Greater(&ts) {
   340  		minTs := types.TS{}
   341  		snapshot.Ascend(&txnBlock{}, func(blk *txnBlock) bool {
   342  			minTs = blk.bornTS
   343  			return false
   344  		})
   345  		logutil.Info("[logtail] fetch with too small ts", zap.String("ts", from.ToString()), zap.String("minTs", minTs.ToString()))
   346  	}
   347  	snapshot.Ascend(pivot, func(blk BlockT) bool {
   348  		if blk.bornTS.Greater(&to) {
   349  			return false
   350  		}
   351  
   352  		if skipBlkOp != nil && skipBlkOp(blk) {
   353  			prepareTS := blk.rows[len(blk.rows)-1].GetPrepareTS()
   354  			return prepareTS.LessEq(&to)
   355  		}
   356  		outOfRange, cnt := blk.ForeachRowInBetween(
   357  			from,
   358  			to,
   359  			rowOp,
   360  		)
   361  		readRows += cnt
   362  
   363  		return !outOfRange
   364  	})
   365  	return
   366  }