github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/testing_utils/many_sorters_test/many_sorters.go (about) 1 // Copyright 2020 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 main 15 16 import ( 17 "context" 18 "flag" 19 "fmt" 20 "math/rand" 21 "net/http" 22 "os" 23 "strings" 24 "sync/atomic" 25 26 "github.com/pingcap/errors" 27 "github.com/pingcap/failpoint" 28 "github.com/pingcap/log" 29 "github.com/pingcap/ticdc/cdc/model" 30 "github.com/pingcap/ticdc/cdc/puller" 31 pullerSorter "github.com/pingcap/ticdc/cdc/puller/sorter" 32 "github.com/pingcap/ticdc/pkg/config" 33 cerrors "github.com/pingcap/ticdc/pkg/errors" 34 "go.uber.org/zap" 35 "go.uber.org/zap/zapcore" 36 "golang.org/x/sync/errgroup" 37 ) 38 39 var ( 40 sorterDir = flag.String("dir", "./sorter", "temporary directory used for sorting") 41 numSorters = flag.Int("num-sorters", 256, "number of instances of sorters") 42 numEvents = flag.Int("num-events-per-sorter", 10000, "number of events sent to a sorter") 43 percentageResolves = flag.Int("percentage-resolve-events", 70, "percentage of resolved events") 44 ) 45 46 func main() { 47 flag.Parse() 48 err := failpoint.Enable("github.com/pingcap/ticdc/cdc/puller/sorter/sorterDebug", "return(true)") 49 if err != nil { 50 log.Fatal("Could not enable failpoint", zap.Error(err)) 51 } 52 log.SetLevel(zapcore.DebugLevel) 53 54 conf := config.GetDefaultServerConfig() 55 conf.Sorter = &config.SorterConfig{ 56 NumConcurrentWorker: 8, 57 ChunkSizeLimit: 1 * 1024 * 1024 * 1024, 58 MaxMemoryPressure: 60, 59 MaxMemoryConsumption: 16 * 1024 * 1024 * 1024, 60 NumWorkerPoolGoroutine: 16, 61 } 62 config.StoreGlobalServerConfig(conf) 63 64 go func() { 65 _ = http.ListenAndServe("localhost:6060", nil) 66 }() 67 68 err = os.MkdirAll(*sorterDir, 0o755) 69 if err != nil { 70 log.Error("sorter_stress_test:", zap.Error(err)) 71 } 72 73 sorters := make([]puller.EventSorter, *numSorters) 74 ctx0, cancel := context.WithCancel(context.Background()) 75 errg, ctx := errgroup.WithContext(ctx0) 76 77 errg.Go(func() error { 78 return pullerSorter.RunWorkerPool(ctx) 79 }) 80 81 var finishCount int32 82 for i := 0; i < *numSorters; i++ { 83 sorters[i], err = pullerSorter.NewUnifiedSorter(*sorterDir, 84 "test-cf", 85 fmt.Sprintf("test-%d", i), 86 model.TableID(i), 87 "0.0.0.0:0") 88 if err != nil { 89 log.Panic("many_sorters", zap.Error(err)) 90 } 91 finalI := i 92 93 // run sorter 94 errg.Go(func() error { 95 return printError(sorters[finalI].Run(ctx)) 96 }) 97 98 // run producer 99 errg.Go(func() error { 100 for j := 0; j < *numEvents; j++ { 101 select { 102 case <-ctx.Done(): 103 return ctx.Err() 104 default: 105 } 106 107 ev := generateEvent(uint64(finalI), uint64(j<<5)) 108 sorters[finalI].AddEntry(ctx, ev) 109 } 110 sorters[finalI].AddEntry(ctx, model.NewResolvedPolymorphicEvent(uint64(finalI), uint64(((*numEvents)<<5)+1))) 111 return nil 112 }) 113 114 // run consumer 115 errg.Go(func() error { 116 for { 117 var ev *model.PolymorphicEvent 118 select { 119 case <-ctx.Done(): 120 return ctx.Err() 121 case ev = <-sorters[finalI].Output(): 122 } 123 124 if ev.CRTs == uint64(((*numEvents)<<5)+1) { 125 log.Info("Sorter finished", zap.Int("sorter-id", finalI)) 126 if atomic.AddInt32(&finishCount, 1) == int32(*numSorters) { 127 log.Info("Many Sorters test finished, cancelling all goroutines") 128 cancel() 129 } 130 return nil 131 } 132 } 133 }) 134 } 135 136 _ = printError(errg.Wait()) 137 if atomic.LoadInt32(&finishCount) == int32(*numSorters) { 138 log.Info("Test was successful!") 139 } 140 } 141 142 func generateEvent(region uint64, ts uint64) *model.PolymorphicEvent { 143 r := rand.Int() % 100 144 if r < *percentageResolves { 145 return model.NewResolvedPolymorphicEvent(region, ts) 146 } 147 return model.NewPolymorphicEvent(&model.RawKVEntry{ 148 OpType: model.OpTypePut, 149 Key: []byte("keykeykey"), 150 Value: []byte("valuevaluevalue"), 151 OldValue: nil, 152 StartTs: ts - 5, 153 CRTs: ts, 154 RegionID: region, 155 }) 156 } 157 158 // printError is a helper for tracing errors on function returns 159 func printError(err error) error { 160 if err != nil && errors.Cause(err) != context.Canceled && 161 errors.Cause(err) != context.DeadlineExceeded && 162 !strings.Contains(err.Error(), "context canceled") && 163 !strings.Contains(err.Error(), "context deadline exceeded") && 164 cerrors.ErrWorkerPoolHandleCancelled.NotEqual(errors.Cause(err)) { 165 166 log.Warn("Unified Sorter: Error detected", zap.Error(err)) 167 } 168 return err 169 }