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