github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/simulator/mcp/mcp_test.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 15 16 import ( 17 "math/rand" 18 "sync" 19 "testing" 20 21 "github.com/pingcap/tiflow/dm/pkg/log" 22 "github.com/stretchr/testify/suite" 23 ) 24 25 type testMCPSuite struct { 26 suite.Suite 27 mcp *ModificationCandidatePool 28 } 29 30 func (s *testMCPSuite) SetupSuite() { 31 s.Require().Nil(log.InitLogger(&log.Config{})) 32 } 33 34 func (s *testMCPSuite) SetupTest() { 35 mcp := NewModificationCandidatePool(8192) 36 for i := 0; i < 4096; i++ { 37 mcp.keyPool = append(mcp.keyPool, &UniqueKey{ 38 rowID: i, 39 value: map[string]interface{}{ 40 "id": i, 41 }, 42 }) 43 } 44 s.mcp = mcp 45 } 46 47 func (s *testMCPSuite) TestNextUK() { 48 allHitRowIDs := map[int]int{} 49 repeatCnt := 20 50 for i := 0; i < repeatCnt; i++ { 51 theUK := s.mcp.NextUK() 52 s.Require().NotNil(theUK, "the picked UK should not be nil") 53 theRowID := theUK.GetRowID() 54 if _, ok := allHitRowIDs[theRowID]; !ok { 55 allHitRowIDs[theRowID] = 0 56 } 57 allHitRowIDs[theRowID]++ 58 s.T().Logf("next UK: %v", theUK) 59 } 60 totalOccurredTimes := 0 61 totalOccurredRowIDs := 0 62 for _, times := range allHitRowIDs { 63 totalOccurredRowIDs++ 64 totalOccurredTimes += times 65 } 66 s.Greater(totalOccurredRowIDs, 1, "there should be more than 1 occurred row IDs") 67 s.Equal(repeatCnt, totalOccurredTimes, "total occurred UKs should equal the iteration count") 68 } 69 70 func (s *testMCPSuite) TestParallelNextUK() { 71 allHitRowIDs := map[int]int{} 72 repeatCnt := 20 73 workerCnt := 5 74 rowIDCh := make(chan int, workerCnt) 75 var wg sync.WaitGroup 76 wg.Add(workerCnt) 77 for i := 0; i < workerCnt; i++ { 78 go func() { 79 defer wg.Done() 80 for i := 0; i < repeatCnt; i++ { 81 theUK := s.mcp.NextUK() 82 if theUK != nil { 83 rowIDCh <- theUK.GetRowID() 84 } 85 } 86 }() 87 } 88 collectionFinishCh := func() <-chan struct{} { 89 ch := make(chan struct{}) 90 go func() { 91 defer close(ch) 92 for rowID := range rowIDCh { 93 if _, ok := allHitRowIDs[rowID]; !ok { 94 allHitRowIDs[rowID] = 0 95 } 96 allHitRowIDs[rowID]++ 97 } 98 }() 99 return ch 100 }() 101 wg.Wait() 102 close(rowIDCh) 103 <-collectionFinishCh 104 105 totalOccurredTimes := 0 106 totalOccurredRowIDs := 0 107 for _, times := range allHitRowIDs { 108 totalOccurredRowIDs++ 109 totalOccurredTimes += times 110 } 111 s.Greater(totalOccurredRowIDs, 1, "there should be more than 1 occurred row IDs") 112 s.Equal(repeatCnt*workerCnt, totalOccurredTimes, "total occurred UKs should equal the iteration count") 113 } 114 115 func (s *testMCPSuite) TestMCPAddDeleteBasic() { 116 var ( 117 curPoolSize int 118 repeatCnt int 119 err error 120 ) 121 curPoolSize = len(s.mcp.keyPool) 122 startPoolSize := curPoolSize 123 repeatCnt = 5 124 for i := 0; i < repeatCnt; i++ { 125 theUK := NewUniqueKey(-1, map[string]interface{}{ 126 "id": rand.Int(), 127 }) 128 err = s.mcp.AddUK(theUK) 129 s.Require().Nil(err) 130 s.Equal(curPoolSize+i+1, len(s.mcp.keyPool), "key pool size is not equal") 131 s.Equal(curPoolSize+i, s.mcp.keyPool[curPoolSize+i].GetRowID(), "the new added UK's row ID is abnormal") 132 s.Equal(curPoolSize+i, theUK.GetRowID(), "the input UK's row ID is not changed") 133 s.T().Logf("new added UK: %v\n", s.mcp.keyPool[curPoolSize+i]) 134 } 135 // test delete from bottom 136 curPoolSize = len(s.mcp.keyPool) 137 for i := 0; i < repeatCnt; i++ { 138 theUK := s.mcp.keyPool[curPoolSize-i-1] 139 err = s.mcp.DeleteUK(NewUniqueKey(curPoolSize-i-1, nil)) 140 s.Require().Nil(err) 141 s.Equal(-1, theUK.GetRowID(), "the deleted UK's row ID is not right") 142 s.Equal(curPoolSize-i-1, len(s.mcp.keyPool), "key pool size is not equal") 143 } 144 curPoolSize = len(s.mcp.keyPool) 145 s.Equal(startPoolSize, curPoolSize, "the MCP size is not right after adding & deleting") 146 // test delete from top 147 for i := 0; i < repeatCnt; i++ { 148 theDelUK := s.mcp.keyPool[i] 149 err = s.mcp.DeleteUK(NewUniqueKey(i, nil)) 150 s.Require().Nil(err) 151 theSwappedUK := s.mcp.keyPool[i] 152 swappedUKVal := theSwappedUK.GetValue() 153 s.Equal(i, theSwappedUK.GetRowID(), "the swapped UK's row ID is abnormal") 154 s.Equal(curPoolSize-i-1, swappedUKVal["id"], "the swapped UK's value is abnormal") 155 s.Equal(-1, theDelUK.GetRowID(), "the deleted UK's row ID is not right") 156 s.T().Logf("new UK after delete on the index %d: %v\n", i, s.mcp.keyPool[i]) 157 } 158 curPoolSize = len(s.mcp.keyPool) 159 // test delete at random position 160 for i := 0; i < repeatCnt; i++ { 161 theDelUK := s.mcp.NextUK() 162 deleteRowID := theDelUK.GetRowID() 163 err = s.mcp.DeleteUK(theDelUK) 164 s.Require().Nil(err) 165 theSwappedUK := s.mcp.keyPool[deleteRowID] 166 swappedUKVal := theSwappedUK.GetValue() 167 s.Equal(deleteRowID, theSwappedUK.GetRowID(), "the swapped UK's row ID is abnormal") 168 s.Equal(-1, theDelUK.GetRowID(), "the deleted UK's row ID is not right") 169 s.Equal(curPoolSize-i-1, swappedUKVal["id"], "the swapped UK's value is abnormal") 170 s.T().Logf("new UK after delete on the index %d: %v\n", deleteRowID, s.mcp.keyPool[deleteRowID]) 171 } 172 // check whether all the row ID is right 173 curPoolSize = len(s.mcp.keyPool) 174 for i := 0; i < curPoolSize; i++ { 175 theUK := s.mcp.keyPool[i] 176 s.Require().Equal(i, theUK.GetRowID(), "this UK element in the MCP has a wrong row ID") 177 } 178 } 179 180 func (s *testMCPSuite) TestMCPAddDeleteInParallel() { 181 beforeLen := s.mcp.Len() 182 pendingCh := make(chan struct{}) 183 ch1 := func() <-chan error { 184 ch := make(chan error) 185 go func() { 186 var err error 187 <-pendingCh 188 defer func() { 189 ch <- err 190 }() 191 for i := 0; i < 5; i++ { 192 theUK := NewUniqueKey(-1, map[string]interface{}{ 193 "id": rand.Int(), 194 }) 195 err = s.mcp.AddUK(theUK) 196 if err != nil { 197 return 198 } 199 s.T().Logf("new added UK: %v\n", theUK) 200 } 201 }() 202 return ch 203 }() 204 ch2 := func() <-chan error { 205 ch := make(chan error) 206 go func() { 207 var err error 208 <-pendingCh 209 defer func() { 210 ch <- err 211 }() 212 for i := 0; i < 5; i++ { 213 theDelUK := s.mcp.NextUK() 214 deletedRowID := theDelUK.rowID 215 err = s.mcp.DeleteUK(theDelUK) 216 if err != nil { 217 return 218 } 219 s.mcp.RLock() 220 theSwappedUK := s.mcp.keyPool[deletedRowID] 221 s.mcp.RUnlock() 222 s.T().Logf("deletedUK: %v, swapped UK: %v\n", theDelUK, theSwappedUK) 223 } 224 }() 225 return ch 226 }() 227 close(pendingCh) 228 err1 := <-ch1 229 err2 := <-ch2 230 s.Require().Nil(err1) 231 s.Require().Nil(err2) 232 afterLen := s.mcp.Len() 233 s.Equal(beforeLen, afterLen, "the key pool size has changed after the parallel modification") 234 } 235 236 func TestMCPSuite(t *testing.T) { 237 suite.Run(t, &testMCPSuite{}) 238 }