github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/logtail/mgr.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  	"context"
    19  	"fmt"
    20  	"sync"
    21  	"sync/atomic"
    22  	"time"
    23  
    24  	"github.com/matrixorigin/matrixone/pkg/container/types"
    25  	"github.com/matrixorigin/matrixone/pkg/logutil"
    26  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    27  	"github.com/matrixorigin/matrixone/pkg/pb/logtail"
    28  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog"
    30  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db/dbutils"
    31  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    32  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logstore/sm"
    33  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnbase"
    34  	"go.uber.org/zap"
    35  )
    36  
    37  const (
    38  	LogtailHeartbeatDuration = time.Millisecond * 2
    39  )
    40  
    41  func MockCallback(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error {
    42  	defer closeCB()
    43  	if len(tails) == 0 {
    44  		return nil
    45  	}
    46  	s := fmt.Sprintf("get logtail\nfrom %v, to %v, tails cnt %d", from, to, len(tails))
    47  	for _, tail := range tails {
    48  		s = fmt.Sprintf("%s\nts %v, dbid %d, tid %d,entries cnt %d", s, tail.Ts, tail.Table.DbId, tail.Table.TbId, len(tail.Commands))
    49  		for _, entry := range tail.Commands {
    50  			s = fmt.Sprintf("%s\n    db name %s, table name %s, insert %v, batch length %d\n    %v",
    51  				s, entry.DatabaseName, entry.TableName, entry.EntryType == api.Entry_Insert, entry.Bat.Vecs[0].Len, entry.Bat.Attrs)
    52  			for i, vec := range entry.Bat.Vecs {
    53  				s = fmt.Sprintf("%s\n        %v, type %v, len %d", s, entry.Bat.Attrs[i], vec.Type, vec.Len)
    54  			}
    55  		}
    56  	}
    57  	logutil.Infof(s)
    58  	return nil
    59  }
    60  
    61  type callback struct {
    62  	cb func(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error
    63  }
    64  
    65  func (cb *callback) call(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error {
    66  	// for debug
    67  	// MockCallback(from,to,tails...)
    68  	return cb.cb(from, to, closeCB, tails...)
    69  }
    70  
    71  // Logtail manager holds sorted txn handles. Its main jobs:
    72  //
    73  // - Insert new txn handle
    74  // - Efficiently iterate over arbitrary range of txn handles on a snapshot
    75  // - Truncate unneceessary txn handles according to GC timestamp
    76  type Manager struct {
    77  	txnbase.NoopCommitListener
    78  	table     *TxnTable
    79  	rt        *dbutils.Runtime
    80  	truncated types.TS
    81  	nowClock  func() types.TS // nowClock is from TxnManager
    82  
    83  	previousSaveTS      types.TS
    84  	logtailCallback     atomic.Pointer[callback]
    85  	collectLogtailQueue sm.Queue
    86  	waitCommitQueue     sm.Queue
    87  	eventOnce           sync.Once
    88  
    89  	nextCompactTS types.TS
    90  }
    91  
    92  func NewManager(rt *dbutils.Runtime, blockSize int, nowClock func() types.TS) *Manager {
    93  	mgr := &Manager{
    94  		rt: rt,
    95  		table: NewTxnTable(
    96  			blockSize,
    97  			nowClock,
    98  		),
    99  		nowClock: nowClock,
   100  	}
   101  	mgr.collectLogtailQueue = sm.NewSafeQueue(10000, 100, mgr.onCollectTxnLogtails)
   102  	mgr.waitCommitQueue = sm.NewSafeQueue(10000, 100, mgr.onWaitTxnCommit)
   103  
   104  	return mgr
   105  }
   106  
   107  type txnWithLogtails struct {
   108  	txn     txnif.AsyncTxn
   109  	tails   *[]logtail.TableLogtail
   110  	closeCB func()
   111  }
   112  
   113  func (mgr *Manager) onCollectTxnLogtails(items ...any) {
   114  	for _, item := range items {
   115  		txn := item.(txnif.AsyncTxn)
   116  		if txn.IsReplay() {
   117  			continue
   118  		}
   119  		builder := NewTxnLogtailRespBuilder(mgr.rt)
   120  		entries, closeCB := builder.CollectLogtail(txn)
   121  		txn.GetStore().DoneWaitEvent(1)
   122  		txnWithLogtails := &txnWithLogtails{
   123  			txn:     txn,
   124  			tails:   entries,
   125  			closeCB: closeCB,
   126  		}
   127  		mgr.waitCommitQueue.Enqueue(txnWithLogtails)
   128  	}
   129  }
   130  func (mgr *Manager) onWaitTxnCommit(items ...any) {
   131  	for _, item := range items {
   132  		txn := item.(*txnWithLogtails)
   133  		state := txn.txn.GetTxnState(true)
   134  		if state != txnif.TxnStateCommitted {
   135  			if state != txnif.TxnStateRollbacked {
   136  				panic(fmt.Sprintf("wrong state %v", state))
   137  			}
   138  			continue
   139  		}
   140  		mgr.generateLogtailWithTxn(txn)
   141  	}
   142  }
   143  func (mgr *Manager) Stop() {
   144  	mgr.collectLogtailQueue.Stop()
   145  	mgr.waitCommitQueue.Stop()
   146  }
   147  func (mgr *Manager) Start() {
   148  	mgr.waitCommitQueue.Start()
   149  	mgr.collectLogtailQueue.Start()
   150  }
   151  
   152  func (mgr *Manager) generateLogtailWithTxn(txn *txnWithLogtails) {
   153  	callback := mgr.logtailCallback.Load()
   154  	if callback != nil {
   155  		to := txn.txn.GetPrepareTS()
   156  		var from types.TS
   157  		if mgr.previousSaveTS.IsEmpty() {
   158  			from = to
   159  		} else {
   160  			from = mgr.previousSaveTS
   161  		}
   162  		mgr.previousSaveTS = to
   163  		// Send ts in order to initialize waterline of logtail service
   164  		mgr.eventOnce.Do(func() {
   165  			logutil.Infof("init waterline to %v", from.ToString())
   166  			callback.call(from.ToTimestamp(), from.ToTimestamp(), txn.closeCB)
   167  		})
   168  		callback.call(from.ToTimestamp(), to.ToTimestamp(), txn.closeCB, *txn.tails...)
   169  	} else {
   170  		txn.closeCB()
   171  	}
   172  }
   173  
   174  // OnEndPrePrepare is a listener for TxnManager. When a txn completes PrePrepare,
   175  // add it to the logtail manager
   176  func (mgr *Manager) OnEndPrePrepare(txn txnif.AsyncTxn) {
   177  	if txn.GetStore().GetTransactionType() == txnif.TxnType_Heartbeat {
   178  		return
   179  	}
   180  	mgr.table.AddTxn(txn)
   181  }
   182  func (mgr *Manager) OnEndPrepareWAL(txn txnif.AsyncTxn) {
   183  	txn.GetStore().AddWaitEvent(1)
   184  	mgr.collectLogtailQueue.Enqueue(txn)
   185  }
   186  
   187  // GetReader get a snapshot of all txn prepared between from and to.
   188  func (mgr *Manager) GetReader(from, to types.TS) *Reader {
   189  	return &Reader{
   190  		from:  from,
   191  		to:    to,
   192  		table: mgr.table,
   193  	}
   194  }
   195  
   196  func (mgr *Manager) GCByTS(ctx context.Context, ts types.TS) {
   197  	if ts.Equal(&mgr.truncated) {
   198  		return
   199  	}
   200  	mgr.truncated = ts
   201  	cnt := mgr.table.TruncateByTimeStamp(ts)
   202  	logutil.Info("[logtail] GC", zap.String("ts", ts.ToString()), zap.Int("deleted", cnt))
   203  }
   204  
   205  func (mgr *Manager) TryCompactTable() {
   206  	mgr.nextCompactTS = mgr.table.TryCompact(mgr.nextCompactTS, mgr.rt)
   207  }
   208  
   209  func (mgr *Manager) GetTableOperator(
   210  	from, to types.TS,
   211  	catalog *catalog.Catalog,
   212  	dbID, tableID uint64,
   213  	scope Scope,
   214  	visitor catalog.Processor,
   215  ) *BoundTableOperator {
   216  	reader := mgr.GetReader(from, to)
   217  	return NewBoundTableOperator(
   218  		catalog,
   219  		reader,
   220  		scope,
   221  		dbID,
   222  		tableID,
   223  		visitor,
   224  	)
   225  }
   226  
   227  func (mgr *Manager) RegisterCallback(cb func(from, to timestamp.Timestamp, closeCB func(), tails ...logtail.TableLogtail) error) error {
   228  	callbackFn := &callback{
   229  		cb: cb,
   230  	}
   231  	mgr.logtailCallback.Store(callbackFn)
   232  	return nil
   233  }