github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/causality/internal/slots_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 internal
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestSlotsTrivial(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	const count = 1000
    29  	slots := NewSlots(8)
    30  	nodes := make([]*Node, 0, 1000)
    31  
    32  	for i := 0; i < count; i++ {
    33  		node := newNodeForTest(1, 2, 3, 4, 5)
    34  		slots.Add(node)
    35  		nodes = append(nodes, node)
    36  	}
    37  
    38  	for i := 0; i < count; i++ {
    39  		slots.Remove(nodes[i])
    40  	}
    41  
    42  	require.Equal(t, 0, len(slots.slots[1].nodes))
    43  	require.Equal(t, 0, len(slots.slots[2].nodes))
    44  	require.Equal(t, 0, len(slots.slots[3].nodes))
    45  	require.Equal(t, 0, len(slots.slots[4].nodes))
    46  	require.Equal(t, 0, len(slots.slots[5].nodes))
    47  }
    48  
    49  func TestSlotsConcurrentOps(t *testing.T) {
    50  	t.Parallel()
    51  
    52  	const N = 256
    53  	slots := NewSlots(8)
    54  	freeNodeChan := make(chan *Node, N)
    55  	inuseNodeChan := make(chan *Node, N)
    56  	for i := 0; i < N; i++ {
    57  		freeNodeChan <- newNodeForTest(1, 9, 17, 25, 33)
    58  	}
    59  
    60  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
    61  	defer cancel()
    62  
    63  	// test concurrent add and remove won't panic
    64  	var wg sync.WaitGroup
    65  	wg.Add(1)
    66  	go func() {
    67  		defer wg.Done()
    68  		for {
    69  			select {
    70  			case <-ctx.Done():
    71  				return
    72  			case node := <-freeNodeChan:
    73  				// keys belong to the same slot after hash, since slot num is 8
    74  				slots.Add(node)
    75  				inuseNodeChan <- node
    76  			}
    77  		}
    78  	}()
    79  
    80  	wg.Add(1)
    81  	go func() {
    82  		defer wg.Done()
    83  		for {
    84  			select {
    85  			case <-ctx.Done():
    86  				return
    87  			case node := <-inuseNodeChan:
    88  				// keys belong to the same slot after hash, since slot num is 8
    89  				slots.Remove(node)
    90  				freeNodeChan <- newNodeForTest(1, 9, 17, 25, 33)
    91  			}
    92  		}
    93  	}()
    94  
    95  	wg.Wait()
    96  }
    97  
    98  func TestSortAndDedupHash(t *testing.T) {
    99  	// If a transaction contains multiple rows, these rows may generate the same hash
   100  	// in some rare cases. We should dedup these hashes to avoid unnecessary self cyclic
   101  	// dependency in the causality dependency graph.
   102  	t.Parallel()
   103  	testCases := []struct {
   104  		hashes   []uint64
   105  		expected []uint64
   106  	}{{
   107  		// No duplicate hashes
   108  		hashes:   []uint64{1, 2, 3, 4, 5},
   109  		expected: []uint64{1, 2, 3, 4, 5},
   110  	}, {
   111  		// Duplicate hashes
   112  		hashes:   []uint64{1, 2, 3, 4, 5, 1, 2, 3, 4, 5},
   113  		expected: []uint64{1, 2, 3, 4, 5},
   114  	}, {
   115  		// Has hash value larger than slots count, should sort by `hash % numSlots` first.
   116  		hashes:   []uint64{4, 9, 9, 3},
   117  		expected: []uint64{9, 3, 4},
   118  	}}
   119  
   120  	for _, tc := range testCases {
   121  		require.Equal(t, tc.expected, sortAndDedupHashes(tc.hashes, 8))
   122  	}
   123  }