github.com/matrixorigin/matrixone@v0.7.0/pkg/util/export/batch_processor_test.go (about) 1 // Copyright 2022 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package export 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/matrixorigin/matrixone/pkg/common/moerr" 26 "github.com/matrixorigin/matrixone/pkg/util/stack" 27 28 "github.com/matrixorigin/matrixone/pkg/logutil" 29 "github.com/matrixorigin/matrixone/pkg/util/batchpipe" 30 "github.com/matrixorigin/matrixone/pkg/util/errutil" 31 32 "github.com/google/gops/agent" 33 "github.com/stretchr/testify/require" 34 "go.uber.org/zap/zapcore" 35 ) 36 37 func init() { 38 time.Local = time.FixedZone("CST", 0) // set time-zone +0000 39 logutil.SetupMOLogger(&logutil.LogConfig{ 40 Level: zapcore.DebugLevel.String(), 41 Format: "console", 42 Filename: "", 43 MaxSize: 512, 44 MaxDays: 0, 45 MaxBackups: 0, 46 47 DisableStore: true, 48 }) 49 if err := agent.Listen(agent.Options{}); err != nil { 50 logutil.Errorf("listen gops agent failed: %s", err) 51 return 52 } 53 } 54 55 const NumType = "Num" 56 57 var _ batchpipe.HasName = (*Num)(nil) 58 var _ batchpipe.ItemBuffer[batchpipe.HasName, any] = &dummyBuffer{} 59 var _ batchpipe.PipeImpl[batchpipe.HasName, any] = &dummyPipeImpl{} 60 61 type Num int64 62 63 func newDummy(v int64) *Num { 64 n := Num(v) 65 return &n 66 } 67 68 func (n Num) GetName() string { return NumType } 69 70 var signalFunc = func() {} 71 72 type dummyBuffer struct { 73 batchpipe.Reminder 74 arr []batchpipe.HasName 75 mux sync.Mutex 76 signal func() 77 ctx context.Context 78 } 79 80 func (s *dummyBuffer) Add(item batchpipe.HasName) { 81 s.mux.Lock() 82 defer s.mux.Unlock() 83 ctx := s.ctx 84 s.arr = append(s.arr, item) 85 if s.signal != nil { 86 val := int(*item.(*Num)) 87 length := len(s.arr) 88 logutil.Infof("accept: %v, len: %d", *item.(*Num), length) 89 if (val <= 3 && val != length) && (val-3) != length { 90 panic(moerr.NewInternalError(ctx, "len not rignt, elem: %d, len: %d", val, length)) 91 } 92 s.signal() 93 } 94 } 95 func (s *dummyBuffer) Reset() { 96 s.mux.Lock() 97 defer s.mux.Unlock() 98 logutil.Infof("buffer reset, stack: %+v", stack.Callers(0)) 99 s.arr = s.arr[0:0] 100 } 101 func (s *dummyBuffer) IsEmpty() bool { 102 s.mux.Lock() 103 defer s.mux.Unlock() 104 return len(s.arr) == 0 105 } 106 func (s *dummyBuffer) ShouldFlush() bool { 107 s.mux.Lock() 108 defer s.mux.Unlock() 109 length := len(s.arr) 110 should := length >= 3 111 if should { 112 logutil.Infof("buffer shouldFlush: %v", should) 113 } 114 return should 115 } 116 func (s *dummyBuffer) GetBatch(ctx context.Context, buf *bytes.Buffer) any { 117 s.mux.Lock() 118 defer s.mux.Unlock() 119 if len(s.arr) == 0 { 120 return "" 121 } 122 123 logutil.Infof("GetBatch, len: %d", len(s.arr)) 124 buf.Reset() 125 for _, item := range s.arr { 126 s, ok := item.(*Num) 127 if !ok { 128 panic("Not Num type") 129 } 130 buf.WriteString("(") 131 buf.WriteString(fmt.Sprintf("%d", *s)) 132 buf.WriteString("),") 133 } 134 logutil.Infof("GetBatch: %s", buf.String()) 135 return string(buf.Next(buf.Len() - 1)) 136 } 137 138 type dummyPipeImpl struct { 139 ch chan string 140 duration time.Duration 141 } 142 143 func (n *dummyPipeImpl) NewItemBuffer(string) batchpipe.ItemBuffer[batchpipe.HasName, any] { 144 return &dummyBuffer{Reminder: batchpipe.NewConstantClock(n.duration), signal: signalFunc, ctx: context.Background()} 145 } 146 147 func (n *dummyPipeImpl) NewItemBatchHandler(ctx context.Context) func(any) { 148 return func(batch any) { 149 n.ch <- batch.(string) 150 } 151 } 152 153 var MOCollectorMux sync.Mutex 154 155 func TestNewMOCollector(t *testing.T) { 156 MOCollectorMux.Lock() 157 defer MOCollectorMux.Unlock() 158 ctx := context.Background() 159 ch := make(chan string, 3) 160 errutil.SetErrorReporter(func(ctx context.Context, err error, i int) { 161 t.Logf("TestNewMOCollector::ErrorReport: %+v", err) 162 }) 163 var signalC = make(chan struct{}, 16) 164 var acceptSignal = func() { <-signalC } 165 signalFunc = func() { signalC <- struct{}{} } 166 167 collector := NewMOCollector(ctx) 168 collector.Register(newDummy(0), &dummyPipeImpl{ch: ch, duration: time.Hour}) 169 collector.Start() 170 171 collector.Collect(ctx, newDummy(1)) 172 acceptSignal() 173 collector.Collect(ctx, newDummy(2)) 174 acceptSignal() 175 collector.Collect(ctx, newDummy(3)) 176 acceptSignal() 177 got := <-ch 178 require.Equal(t, `(1),(2),(3)`, got) 179 collector.Collect(ctx, newDummy(4)) 180 acceptSignal() 181 collector.Collect(ctx, newDummy(5)) 182 acceptSignal() 183 collector.Stop(true) 184 logutil.GetGlobalLogger().Sync() 185 got = <-ch 186 require.Equal(t, `(4),(5)`, got) 187 for i := len(ch); i > 0; i-- { 188 got = <-ch 189 t.Logf("left ch: %s", got) 190 } 191 }