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 }