github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/puller/mock_puller.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 puller
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/pingcap/check"
    22  	"github.com/pingcap/kvproto/pkg/kvrpcpb"
    23  	"github.com/pingcap/log"
    24  	timodel "github.com/pingcap/parser/model"
    25  	"github.com/pingcap/ticdc/cdc/model"
    26  	"github.com/pingcap/ticdc/pkg/regionspan"
    27  	"github.com/pingcap/tidb/domain"
    28  	tidbkv "github.com/pingcap/tidb/kv"
    29  	"github.com/pingcap/tidb/session"
    30  	"github.com/pingcap/tidb/store/mockstore"
    31  	"github.com/pingcap/tidb/store/mockstore/mocktikv"
    32  	"github.com/pingcap/tidb/util/testkit"
    33  	"go.uber.org/zap"
    34  )
    35  
    36  type mvccListener struct {
    37  	mocktikv.MVCCStore
    38  	mu           sync.RWMutex
    39  	postPrewrite func(req *kvrpcpb.PrewriteRequest, result []error)
    40  	postCommit   func(keys [][]byte, startTs, commitTs uint64, result error)
    41  	postRollback func(keys [][]byte, startTs uint64, result error)
    42  }
    43  
    44  func newMVCCListener(store mocktikv.MVCCStore) *mvccListener {
    45  	return &mvccListener{
    46  		MVCCStore:    store,
    47  		postPrewrite: func(_ *kvrpcpb.PrewriteRequest, _ []error) {},
    48  		postCommit:   func(_ [][]byte, _, _ uint64, _ error) {},
    49  		postRollback: func(_ [][]byte, _ uint64, _ error) {},
    50  	}
    51  }
    52  
    53  // Prewrite implements the MVCCStore interface
    54  func (l *mvccListener) Prewrite(req *kvrpcpb.PrewriteRequest) []error {
    55  	l.mu.RLock()
    56  	defer l.mu.RUnlock()
    57  	result := l.MVCCStore.Prewrite(req)
    58  	log.Debug("mvccListener Prewrite", zap.Reflect("req", req), zap.Reflect("result", result))
    59  	l.postPrewrite(req, result)
    60  	return result
    61  }
    62  
    63  // Commit implements the MVCCStore interface
    64  func (l *mvccListener) Commit(keys [][]byte, startTs, commitTs uint64) error {
    65  	l.mu.RLock()
    66  	defer l.mu.RUnlock()
    67  	result := l.MVCCStore.Commit(keys, startTs, commitTs)
    68  	log.Debug("mvccListener Commit", zap.Reflect("keys", keys),
    69  		zap.Uint64("startTs", startTs),
    70  		zap.Uint64("commitTs", commitTs),
    71  		zap.Reflect("result", result))
    72  	l.postCommit(keys, startTs, commitTs, result)
    73  	return result
    74  }
    75  
    76  // Rollback implements the MVCCStore interface
    77  func (l *mvccListener) Rollback(keys [][]byte, startTs uint64) error {
    78  	l.mu.RLock()
    79  	defer l.mu.RUnlock()
    80  	result := l.MVCCStore.Rollback(keys, startTs)
    81  	log.Debug("mvccListener Commit", zap.Reflect("keys", keys),
    82  		zap.Uint64("startTs", startTs),
    83  		zap.Reflect("result", result))
    84  	l.postRollback(keys, startTs, result)
    85  	return result
    86  }
    87  
    88  func (l *mvccListener) registerPostPrewrite(fn func(req *kvrpcpb.PrewriteRequest, result []error)) {
    89  	l.mu.Lock()
    90  	defer l.mu.Unlock()
    91  	l.postPrewrite = fn
    92  }
    93  
    94  func (l *mvccListener) registerPostCommit(fn func(keys [][]byte, startTs, commitTs uint64, result error)) {
    95  	l.mu.Lock()
    96  	defer l.mu.Unlock()
    97  	l.postCommit = fn
    98  }
    99  
   100  func (l *mvccListener) registerPostRollback(fn func(keys [][]byte, startTs uint64, result error)) {
   101  	l.mu.Lock()
   102  	defer l.mu.Unlock()
   103  	l.postRollback = fn
   104  }
   105  
   106  // MockPullerManager keeps track of transactions for mock pullers
   107  type MockPullerManager struct {
   108  	mvccStore mocktikv.MVCCStore
   109  	store     tidbkv.Storage
   110  	domain    *domain.Domain
   111  
   112  	txnMap  map[uint64]*kvrpcpb.PrewriteRequest
   113  	tidbKit *testkit.TestKit
   114  
   115  	rawKVEntries []*model.RawKVEntry
   116  
   117  	txnMapMu       sync.Mutex
   118  	rawKVEntriesMu sync.RWMutex
   119  	closeCh        chan struct{}
   120  
   121  	c *check.C
   122  }
   123  
   124  var _ Puller = &mockPuller{}
   125  
   126  type mockPuller struct {
   127  	pm          *MockPullerManager
   128  	spans       []regionspan.ComparableSpan
   129  	resolvedTs  uint64
   130  	startTs     uint64
   131  	rawKVOffset int
   132  }
   133  
   134  func (p *mockPuller) Output() <-chan *model.RawKVEntry {
   135  	panic("implement me")
   136  }
   137  
   138  func (p *mockPuller) SortedOutput(ctx context.Context) <-chan *model.RawKVEntry {
   139  	output := make(chan *model.RawKVEntry, 16)
   140  	go func() {
   141  		for {
   142  			p.pm.rawKVEntriesMu.RLock()
   143  			for ; p.rawKVOffset < len(p.pm.rawKVEntries); p.rawKVOffset++ {
   144  				rawKV := p.pm.rawKVEntries[p.rawKVOffset]
   145  				if rawKV.StartTs < p.startTs {
   146  					continue
   147  				}
   148  				p.resolvedTs = rawKV.StartTs
   149  				if !regionspan.KeyInSpans(rawKV.Key, p.spans) {
   150  					continue
   151  				}
   152  				select {
   153  				case <-ctx.Done():
   154  					return
   155  				case output <- rawKV:
   156  				}
   157  			}
   158  			p.pm.rawKVEntriesMu.RUnlock()
   159  			time.Sleep(time.Millisecond * 100)
   160  		}
   161  	}()
   162  	return output
   163  }
   164  
   165  func (p *mockPuller) Run(ctx context.Context) error {
   166  	// Do nothing
   167  	select {
   168  	case <-ctx.Done():
   169  		return ctx.Err()
   170  	case <-p.pm.closeCh:
   171  		return nil
   172  	}
   173  }
   174  
   175  func (p *mockPuller) GetResolvedTs() uint64 {
   176  	return p.resolvedTs
   177  }
   178  
   179  func (p *mockPuller) IsInitialized() bool {
   180  	return false
   181  }
   182  
   183  // NewMockPullerManager creates and sets up a mock puller manager
   184  func NewMockPullerManager(c *check.C, newRowFormat bool) *MockPullerManager {
   185  	m := &MockPullerManager{
   186  		txnMap:  make(map[uint64]*kvrpcpb.PrewriteRequest),
   187  		closeCh: make(chan struct{}),
   188  		c:       c,
   189  	}
   190  	m.setUp(newRowFormat)
   191  	return m
   192  }
   193  
   194  func (m *MockPullerManager) setUp(newRowFormat bool) {
   195  	// avoid to print too many logs
   196  	logLevel := log.GetLevel()
   197  	log.SetLevel(zap.FatalLevel)
   198  	defer log.SetLevel(logLevel)
   199  
   200  	mvccListener := newMVCCListener(mocktikv.MustNewMVCCStore())
   201  
   202  	m.mvccStore = mvccListener
   203  	store, err := mockstore.NewMockStore()
   204  	if err != nil {
   205  		log.Panic("create mock puller failed", zap.Error(err))
   206  	}
   207  	m.store = store
   208  
   209  	session.SetSchemaLease(0)
   210  	session.DisableStats4Test()
   211  	m.domain, err = session.BootstrapSession(m.store)
   212  	if err != nil {
   213  		log.Panic("create mock puller failed", zap.Error(err))
   214  	}
   215  
   216  	m.domain.SetStatsUpdating(true)
   217  
   218  	m.tidbKit = testkit.NewTestKit(m.c, m.store)
   219  	m.MustExec("use test;")
   220  	m.tidbKit.Se.GetSessionVars().RowEncoder.Enable = newRowFormat
   221  
   222  	mvccListener.registerPostPrewrite(m.postPrewrite)
   223  	mvccListener.registerPostCommit(m.postCommit)
   224  	mvccListener.registerPostRollback(m.postRollback)
   225  }
   226  
   227  // TearDown release all resources in a mock puller manager
   228  func (m *MockPullerManager) TearDown() {
   229  	m.mvccStore.Close() //nolint:errcheck
   230  	m.store.Close()     //nolint:errcheck
   231  	m.domain.Close()
   232  }
   233  
   234  // CreatePuller returns a mock puller with the specified start ts and spans
   235  func (m *MockPullerManager) CreatePuller(startTs uint64, spans []regionspan.ComparableSpan) Puller {
   236  	return &mockPuller{
   237  		spans:   spans,
   238  		pm:      m,
   239  		startTs: startTs,
   240  	}
   241  }
   242  
   243  // MustExec delegates to TestKit.MustExec
   244  func (m *MockPullerManager) MustExec(sql string, args ...interface{}) {
   245  	m.tidbKit.MustExec(sql, args...)
   246  }
   247  
   248  // GetTableInfo queries the info schema with the table name and returns the TableInfo
   249  func (m *MockPullerManager) GetTableInfo(schemaName, tableName string) *model.TableInfo {
   250  	is := m.domain.InfoSchema()
   251  	tbl, err := is.TableByName(timodel.NewCIStr(schemaName), timodel.NewCIStr(tableName))
   252  	m.c.Assert(err, check.IsNil)
   253  	dbInfo, exist := is.SchemaByTable(tbl.Meta())
   254  	m.c.Assert(exist, check.IsTrue)
   255  	return model.WrapTableInfo(dbInfo.ID, dbInfo.Name.O, 0, tbl.Meta())
   256  }
   257  
   258  func (m *MockPullerManager) postPrewrite(req *kvrpcpb.PrewriteRequest, result []error) {
   259  	m.txnMapMu.Lock()
   260  	defer m.txnMapMu.Unlock()
   261  	if anyError(result) {
   262  		return
   263  	}
   264  	m.txnMap[req.StartVersion] = req
   265  }
   266  
   267  func (m *MockPullerManager) postCommit(keys [][]byte, startTs, commitTs uint64, result error) {
   268  	m.txnMapMu.Lock()
   269  	if result != nil {
   270  		return
   271  	}
   272  	prewrite, exist := m.txnMap[startTs]
   273  	if !exist {
   274  		log.Panic("txn not found", zap.Uint64("startTs", startTs))
   275  	}
   276  	delete(m.txnMap, startTs)
   277  	m.txnMapMu.Unlock()
   278  
   279  	entries := prewrite2RawKV(prewrite, commitTs)
   280  	m.rawKVEntriesMu.Lock()
   281  	defer m.rawKVEntriesMu.Unlock()
   282  	m.rawKVEntries = append(m.rawKVEntries, entries...)
   283  }
   284  
   285  func (m *MockPullerManager) postRollback(keys [][]byte, startTs uint64, result error) {
   286  	m.txnMapMu.Lock()
   287  	defer m.txnMapMu.Unlock()
   288  	if result != nil {
   289  		return
   290  	}
   291  	delete(m.txnMap, startTs)
   292  }
   293  
   294  func prewrite2RawKV(req *kvrpcpb.PrewriteRequest, commitTs uint64) []*model.RawKVEntry {
   295  	var putEntries []*model.RawKVEntry
   296  	var deleteEntries []*model.RawKVEntry
   297  	for _, mut := range req.Mutations {
   298  		switch mut.Op {
   299  		case kvrpcpb.Op_Put, kvrpcpb.Op_Insert:
   300  			rawKV := &model.RawKVEntry{
   301  				StartTs: req.GetStartVersion(),
   302  				CRTs:    commitTs,
   303  				Key:     mut.Key,
   304  				Value:   mut.Value,
   305  				OpType:  model.OpTypePut,
   306  			}
   307  			putEntries = append(putEntries, rawKV)
   308  		case kvrpcpb.Op_Del:
   309  			rawKV := &model.RawKVEntry{
   310  				StartTs: req.GetStartVersion(),
   311  				CRTs:    commitTs,
   312  				Key:     mut.Key,
   313  				Value:   mut.Value,
   314  				OpType:  model.OpTypeResolved,
   315  			}
   316  			deleteEntries = append(deleteEntries, rawKV)
   317  		default:
   318  			continue
   319  		}
   320  	}
   321  	entries := append(deleteEntries, putEntries...)
   322  	return append(entries, &model.RawKVEntry{CRTs: commitTs, OpType: model.OpTypeResolved})
   323  }
   324  
   325  func anyError(errs []error) bool {
   326  	for _, err := range errs {
   327  		if err != nil {
   328  			return true
   329  		}
   330  	}
   331  	return false
   332  }