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  }