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  }