github.com/matrixorigin/matrixone@v0.7.0/pkg/util/trace/impl/motrace/buffer_pipe.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 motrace 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "github.com/matrixorigin/matrixone/pkg/util/trace" 22 "reflect" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/matrixorigin/matrixone/pkg/common/moerr" 28 "github.com/matrixorigin/matrixone/pkg/common/mpool" 29 "github.com/matrixorigin/matrixone/pkg/logutil" 30 bp "github.com/matrixorigin/matrixone/pkg/util/batchpipe" 31 "github.com/matrixorigin/matrixone/pkg/util/export/table" 32 ) 33 34 const defaultClock = 15 * time.Second 35 36 var errorFormatter atomic.Value 37 var logStackFormatter atomic.Value 38 39 func init() { 40 errorFormatter.Store("%+v") 41 logStackFormatter.Store("%+v") 42 } 43 44 type IBuffer2SqlItem interface { 45 bp.HasName 46 Size() int64 47 Free() 48 } 49 50 var _ PipeImpl = (*batchETLHandler)(nil) 51 52 type batchETLHandler struct { 53 defaultOpts []BufferOption 54 } 55 56 func NewBufferPipe2CSVWorker(opt ...BufferOption) bp.PipeImpl[bp.HasName, any] { 57 return &batchETLHandler{opt} 58 } 59 60 // NewItemBuffer implement batchpipe.PipeImpl 61 func (t batchETLHandler) NewItemBuffer(name string) bp.ItemBuffer[bp.HasName, any] { 62 var opts []BufferOption 63 var f genBatchFunc = genETLData 64 logutil.Debugf("NewItemBuffer name: %s", name) 65 switch name { 66 case MOStatementType, SingleStatementTable.GetName(): 67 case MOErrorType: 68 case MOSpanType: 69 case MOLogType: 70 case MORawLogType: 71 default: 72 logutil.Warnf("batchETLHandler handle new type: %s", name) 73 } 74 opts = append(opts, BufferWithGenBatchFunc(f), BufferWithType(name)) 75 opts = append(opts, t.defaultOpts...) 76 return newItemBuffer(opts...) 77 } 78 79 // NewItemBatchHandler implement batchpipe.PipeImpl 80 func (t batchETLHandler) NewItemBatchHandler(ctx context.Context) func(b any) { 81 82 handle := func(b any) { 83 req, ok := b.(table.WriteRequest) // see genETLData 84 if !ok { 85 panic(moerr.NewInternalError(ctx, "batchETLHandler meet unknown type: %v", reflect.ValueOf(b).Type())) 86 } 87 if _, err := req.Handle(); err != nil { 88 logutil.Error(fmt.Sprintf("[Trace] failed to write. err: %v", err), logutil.NoReportFiled()) 89 } 90 } 91 92 var f = func(batch any) { 93 _, span := trace.Start(DefaultContext(), "batchETLHandler") 94 defer span.End() 95 switch b := batch.(type) { 96 case table.WriteRequest: 97 handle(b) 98 case table.ExportRequests: 99 for _, req := range b { 100 handle(req) 101 } 102 default: 103 panic(moerr.NewNotSupported(ctx, "unknown batch type: %v", reflect.ValueOf(b).Type())) 104 } 105 } 106 return f 107 } 108 109 type WriteFactoryConfig struct { 110 Account string 111 Ts time.Time 112 PathBuilder table.PathBuilder 113 } 114 115 func genETLData(ctx context.Context, in []IBuffer2SqlItem, buf *bytes.Buffer, factory table.WriterFactory) any { 116 buf.Reset() 117 if len(in) == 0 { 118 return table.NewRowRequest(nil) 119 } 120 121 ts := time.Now() 122 writerMap := make(map[string]table.RowWriter, 2) 123 writeValues := func(item table.RowField) { 124 row := item.GetTable().GetRow(ctx) 125 item.FillRow(ctx, row) 126 account := row.GetAccount() 127 w, exist := writerMap[account] 128 if !exist { 129 if factory == nil { 130 factory = GetTracerProvider().writerFactory 131 } 132 w = factory(ctx, account, row.Table, ts) 133 writerMap[row.GetAccount()] = w 134 } 135 w.WriteRow(row) 136 } 137 138 for _, i := range in { 139 item, ok := i.(table.RowField) 140 if !ok { 141 panic("not MalCsv, dont support output CSV") 142 } 143 writeValues(item) 144 } 145 146 reqs := make(table.ExportRequests, 0, len(writerMap)) 147 for _, ww := range writerMap { 148 reqs = append(reqs, table.NewRowRequest(ww)) 149 } 150 151 return reqs 152 } 153 154 var _ bp.ItemBuffer[bp.HasName, any] = &itemBuffer{} 155 156 // buffer catch item, like trace/log/error, buffer 157 type itemBuffer struct { 158 bp.Reminder // see BufferWithReminder 159 buf []IBuffer2SqlItem 160 mux sync.Mutex 161 bufferType string // see BufferWithType 162 size int64 // default: 1 MB 163 sizeThreshold int64 // see BufferWithSizeThreshold 164 165 filterItemFunc filterItemFunc 166 genBatchFunc genBatchFunc 167 } 168 169 type filterItemFunc func(IBuffer2SqlItem) 170 type genBatchFunc func(context.Context, []IBuffer2SqlItem, *bytes.Buffer, table.WriterFactory) any 171 172 var noopFilterItemFunc = func(IBuffer2SqlItem) {} 173 var noopGenBatchSQL = genBatchFunc(func(context.Context, []IBuffer2SqlItem, *bytes.Buffer, table.WriterFactory) any { return "" }) 174 175 func newItemBuffer(opts ...BufferOption) *itemBuffer { 176 b := &itemBuffer{ 177 Reminder: bp.NewConstantClock(defaultClock), 178 buf: make([]IBuffer2SqlItem, 0, 10240), 179 sizeThreshold: 10 * mpool.MB, 180 filterItemFunc: noopFilterItemFunc, 181 genBatchFunc: noopGenBatchSQL, 182 } 183 for _, opt := range opts { 184 opt.apply(b) 185 } 186 logutil.Debugf("newItemBuffer, Reminder next: %v", b.Reminder.RemindNextAfter()) 187 if b.genBatchFunc == nil || b.filterItemFunc == nil || b.Reminder == nil { 188 logutil.Debug("newItemBuffer meet nil elem") 189 return nil 190 } 191 return b 192 } 193 194 func (b *itemBuffer) Add(i bp.HasName) { 195 b.mux.Lock() 196 defer b.mux.Unlock() 197 if item, ok := i.(IBuffer2SqlItem); !ok { 198 panic("not implement interface IBuffer2SqlItem") 199 } else { 200 b.filterItemFunc(item) 201 b.buf = append(b.buf, item) 202 atomic.AddInt64(&b.size, item.Size()) 203 } 204 } 205 206 func (b *itemBuffer) Reset() { 207 b.mux.Lock() 208 defer b.mux.Unlock() 209 for idx, i := range b.buf { 210 i.Free() 211 b.buf[idx] = nil 212 } 213 b.buf = b.buf[0:0] 214 b.size = 0 215 } 216 217 func (b *itemBuffer) IsEmpty() bool { 218 b.mux.Lock() 219 defer b.mux.Unlock() 220 return b.isEmpty() 221 } 222 223 func (b *itemBuffer) isEmpty() bool { 224 return len(b.buf) == 0 225 } 226 227 func (b *itemBuffer) ShouldFlush() bool { 228 b.mux.Lock() 229 defer b.mux.Unlock() 230 return b.size > b.sizeThreshold 231 } 232 233 func (b *itemBuffer) Size() int64 { 234 b.mux.Lock() 235 defer b.mux.Unlock() 236 return b.size 237 } 238 239 func (b *itemBuffer) GetBufferType() string { 240 return b.bufferType 241 } 242 243 func (b *itemBuffer) GetBatch(ctx context.Context, buf *bytes.Buffer) any { 244 ctx, span := trace.Start(ctx, "GenBatch") 245 defer span.End() 246 b.mux.Lock() 247 defer b.mux.Unlock() 248 249 if b.isEmpty() { 250 return nil 251 } 252 return b.genBatchFunc(ctx, b.buf, buf, nil) 253 } 254 255 type BufferOption interface { 256 apply(*itemBuffer) 257 } 258 259 type buffer2SqlOptionFunc func(*itemBuffer) 260 261 func (f buffer2SqlOptionFunc) apply(b *itemBuffer) { 262 f(b) 263 } 264 265 func BufferWithReminder(reminder bp.Reminder) BufferOption { 266 return buffer2SqlOptionFunc(func(b *itemBuffer) { 267 b.Reminder = reminder 268 }) 269 } 270 271 func BufferWithType(name string) BufferOption { 272 return buffer2SqlOptionFunc(func(b *itemBuffer) { 273 b.bufferType = name 274 }) 275 } 276 277 func BufferWithSizeThreshold(size int64) BufferOption { 278 return buffer2SqlOptionFunc(func(b *itemBuffer) { 279 b.sizeThreshold = size 280 }) 281 } 282 283 func BufferWithFilterItemFunc(f filterItemFunc) BufferOption { 284 return buffer2SqlOptionFunc(func(b *itemBuffer) { 285 b.filterItemFunc = f 286 }) 287 } 288 289 func BufferWithGenBatchFunc(f genBatchFunc) BufferOption { 290 return buffer2SqlOptionFunc(func(b *itemBuffer) { 291 b.genBatchFunc = f 292 }) 293 }