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 }