github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/sink/common/common.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package common
    15  
    16  import (
    17  	"sort"
    18  	"sync"
    19  	"sync/atomic"
    20  
    21  	"github.com/pingcap/log"
    22  	"github.com/pingcap/ticdc/cdc/model"
    23  	"github.com/pingcap/ticdc/pkg/filter"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  type txnsWithTheSameCommitTs struct {
    28  	txns     map[model.Ts]*model.SingleTableTxn
    29  	commitTs model.Ts
    30  }
    31  
    32  func (t *txnsWithTheSameCommitTs) Append(row *model.RowChangedEvent) {
    33  	if row.CommitTs != t.commitTs {
    34  		log.Panic("unexpected row change event",
    35  			zap.Uint64("commitTs of txn", t.commitTs),
    36  			zap.Any("row", row))
    37  	}
    38  	if t.txns == nil {
    39  		t.txns = make(map[model.Ts]*model.SingleTableTxn)
    40  	}
    41  	txn, exist := t.txns[row.StartTs]
    42  	if !exist {
    43  		txn = &model.SingleTableTxn{
    44  			StartTs:   row.StartTs,
    45  			CommitTs:  row.CommitTs,
    46  			Table:     row.Table,
    47  			ReplicaID: row.ReplicaID,
    48  		}
    49  		t.txns[row.StartTs] = txn
    50  	}
    51  	txn.Append(row)
    52  }
    53  
    54  // UnresolvedTxnCache caches unresolved txns
    55  type UnresolvedTxnCache struct {
    56  	unresolvedTxnsMu sync.Mutex
    57  	unresolvedTxns   map[model.TableID][]*txnsWithTheSameCommitTs
    58  	checkpointTs     uint64
    59  }
    60  
    61  // NewUnresolvedTxnCache returns a new UnresolvedTxnCache
    62  func NewUnresolvedTxnCache() *UnresolvedTxnCache {
    63  	return &UnresolvedTxnCache{
    64  		unresolvedTxns: make(map[model.TableID][]*txnsWithTheSameCommitTs),
    65  	}
    66  }
    67  
    68  // Append adds unresolved rows to cache
    69  // the rows inputed into this function will go through the following handling logic
    70  // 1. group by tableID from one input stream
    71  // 2. for each tableID stream, the callers of this function should **make sure** that the CommitTs of rows is **strictly increasing**
    72  // 3. group by CommitTs, according to CommitTs cut the rows into many group of rows in the same CommitTs
    73  // 4. group by StartTs, cause the StartTs is the unique identifier of the transaction, according to StartTs cut the rows into many txns
    74  func (c *UnresolvedTxnCache) Append(filter *filter.Filter, rows ...*model.RowChangedEvent) int {
    75  	c.unresolvedTxnsMu.Lock()
    76  	defer c.unresolvedTxnsMu.Unlock()
    77  	appendRows := 0
    78  	for _, row := range rows {
    79  		if filter != nil && filter.ShouldIgnoreDMLEvent(row.StartTs, row.Table.Schema, row.Table.Table) {
    80  			log.Info("Row changed event ignored", zap.Uint64("start-ts", row.StartTs))
    81  			continue
    82  		}
    83  		txns := c.unresolvedTxns[row.Table.TableID]
    84  		if len(txns) == 0 || txns[len(txns)-1].commitTs != row.CommitTs {
    85  			// fail-fast check
    86  			if len(txns) != 0 && txns[len(txns)-1].commitTs > row.CommitTs {
    87  				log.Panic("the commitTs of the emit row is less than the received row",
    88  					zap.Stringer("table", row.Table),
    89  					zap.Uint64("emit row startTs", row.StartTs),
    90  					zap.Uint64("emit row commitTs", row.CommitTs),
    91  					zap.Uint64("last received row commitTs", txns[len(txns)-1].commitTs))
    92  			}
    93  			txns = append(txns, &txnsWithTheSameCommitTs{
    94  				commitTs: row.CommitTs,
    95  			})
    96  			c.unresolvedTxns[row.Table.TableID] = txns
    97  		}
    98  		txns[len(txns)-1].Append(row)
    99  		appendRows++
   100  	}
   101  	return appendRows
   102  }
   103  
   104  // Resolved returns resolved txns according to resolvedTs
   105  // The returned map contains many txns grouped by tableID. for each table, the each commitTs of txn in txns slice is strictly increasing
   106  func (c *UnresolvedTxnCache) Resolved(resolvedTs uint64) map[model.TableID][]*model.SingleTableTxn {
   107  	if resolvedTs <= atomic.LoadUint64(&c.checkpointTs) {
   108  		return nil
   109  	}
   110  
   111  	c.unresolvedTxnsMu.Lock()
   112  	defer c.unresolvedTxnsMu.Unlock()
   113  	if len(c.unresolvedTxns) == 0 {
   114  		return nil
   115  	}
   116  
   117  	_, resolvedTxnsMap := splitResolvedTxn(resolvedTs, c.unresolvedTxns)
   118  	return resolvedTxnsMap
   119  }
   120  
   121  // UpdateCheckpoint updates the checkpoint ts
   122  func (c *UnresolvedTxnCache) UpdateCheckpoint(checkpointTs uint64) {
   123  	atomic.StoreUint64(&c.checkpointTs, checkpointTs)
   124  }
   125  
   126  func splitResolvedTxn(
   127  	resolvedTs uint64, unresolvedTxns map[model.TableID][]*txnsWithTheSameCommitTs,
   128  ) (minTs uint64, resolvedRowsMap map[model.TableID][]*model.SingleTableTxn) {
   129  	resolvedRowsMap = make(map[model.TableID][]*model.SingleTableTxn, len(unresolvedTxns))
   130  	minTs = resolvedTs
   131  	for tableID, txns := range unresolvedTxns {
   132  		i := sort.Search(len(txns), func(i int) bool {
   133  			return txns[i].commitTs > resolvedTs
   134  		})
   135  		if i == 0 {
   136  			continue
   137  		}
   138  		var resolvedTxnsWithTheSameCommitTs []*txnsWithTheSameCommitTs
   139  		if i == len(txns) {
   140  			resolvedTxnsWithTheSameCommitTs = txns
   141  			delete(unresolvedTxns, tableID)
   142  		} else {
   143  			resolvedTxnsWithTheSameCommitTs = txns[:i]
   144  			unresolvedTxns[tableID] = txns[i:]
   145  		}
   146  		var txnsLength int
   147  		for _, txns := range resolvedTxnsWithTheSameCommitTs {
   148  			txnsLength += len(txns.txns)
   149  		}
   150  		resolvedTxns := make([]*model.SingleTableTxn, 0, txnsLength)
   151  		for _, txns := range resolvedTxnsWithTheSameCommitTs {
   152  			for _, txn := range txns.txns {
   153  				resolvedTxns = append(resolvedTxns, txn)
   154  			}
   155  		}
   156  		resolvedRowsMap[tableID] = resolvedTxns
   157  		if len(resolvedTxnsWithTheSameCommitTs) > 0 && resolvedTxnsWithTheSameCommitTs[0].commitTs < minTs {
   158  			minTs = resolvedTxnsWithTheSameCommitTs[0].commitTs
   159  		}
   160  	}
   161  	return
   162  }