github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/simulator/mcp/mcp.go (about)

     1  // Copyright 2022 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 mcp defines the Modification Candidate Pool (MCP).
    15  package mcp
    16  
    17  import (
    18  	"math/rand"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/pingcap/errors"
    23  	"github.com/pingcap/tiflow/dm/pkg/log"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  // ModificationCandidatePool is the core container storing all the current unique keys for a table.
    28  type ModificationCandidatePool struct {
    29  	sync.RWMutex
    30  	keyPool  []*UniqueKey
    31  	theRand  *rand.Rand
    32  	randLock sync.Mutex
    33  }
    34  
    35  // NewModificationCandidatePool create a new MCP.
    36  func NewModificationCandidatePool(capcity int) *ModificationCandidatePool {
    37  	theKeyPool := make([]*UniqueKey, 0, capcity)
    38  	theRand := rand.New(rand.NewSource(time.Now().Unix()))
    39  	return &ModificationCandidatePool{
    40  		keyPool: theKeyPool,
    41  		theRand: theRand,
    42  	}
    43  }
    44  
    45  // NextUK randomly picks a unique key in the MCP.
    46  func (mcp *ModificationCandidatePool) NextUK() *UniqueKey {
    47  	mcp.RLock()
    48  	defer mcp.RUnlock()
    49  	if len(mcp.keyPool) == 0 {
    50  		return nil
    51  	}
    52  	mcp.randLock.Lock()
    53  	idx := mcp.theRand.Intn(len(mcp.keyPool))
    54  	mcp.randLock.Unlock()
    55  	return mcp.keyPool[idx] // pass by reference
    56  }
    57  
    58  // Len gets the current length of the MCP.
    59  func (mcp *ModificationCandidatePool) Len() int {
    60  	mcp.RLock()
    61  	defer mcp.RUnlock()
    62  	return len(mcp.keyPool)
    63  }
    64  
    65  // AddUK adds the unique key into the MCP.
    66  // It has side effect: the input UK's row ID will be changed.
    67  func (mcp *ModificationCandidatePool) AddUK(uk *UniqueKey) error {
    68  	mcp.Lock()
    69  	defer mcp.Unlock()
    70  	if len(mcp.keyPool)+1 > cap(mcp.keyPool) {
    71  		return errors.Trace(ErrMCPCapacityFull)
    72  	}
    73  	currentLen := len(mcp.keyPool)
    74  	uk.SetRowID(currentLen)
    75  	mcp.keyPool = append(mcp.keyPool, uk)
    76  	return nil
    77  }
    78  
    79  // DeleteUK deletes the unique key from the MCP.
    80  // It will get the row ID of the UK and delete the UK on that position.
    81  // If the actual value is different from the input UK, the element will still be deleted.
    82  // It has side effect: after the deletion, the input UK's row ID will be set to -1,
    83  // to prevent deleting a dangling UK multiple times.
    84  func (mcp *ModificationCandidatePool) DeleteUK(uk *UniqueKey) error {
    85  	var (
    86  		deletedUK *UniqueKey
    87  		deleteIdx int
    88  	)
    89  	if uk == nil {
    90  		return nil
    91  	}
    92  	mcp.Lock()
    93  	defer mcp.Unlock()
    94  	deleteIdx = uk.GetRowID()
    95  	if deleteIdx < 0 {
    96  		return errors.Trace(ErrDeleteUKNotFound)
    97  	}
    98  	if deleteIdx >= len(mcp.keyPool) {
    99  		log.L().Error("the delete UK row ID > MCP's total length", zap.Int("delete row ID", deleteIdx), zap.Int("current key pool length", len(mcp.keyPool)))
   100  		return errors.Trace(ErrInvalidRowID)
   101  	}
   102  	deletedUK = mcp.keyPool[deleteIdx]
   103  	curLen := len(mcp.keyPool)
   104  	lastUK := mcp.keyPool[curLen-1]
   105  	lastUK.SetRowID(deleteIdx)
   106  	mcp.keyPool[deleteIdx] = lastUK
   107  	mcp.keyPool = mcp.keyPool[:curLen-1]
   108  	deletedUK.SetRowID(-1)
   109  	return nil
   110  }
   111  
   112  // Reset cleans up all the items in the MCP.
   113  func (mcp *ModificationCandidatePool) Reset() {
   114  	mcp.Lock()
   115  	defer mcp.Unlock()
   116  	mcp.keyPool = mcp.keyPool[:0]
   117  }