github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/trace/service_txn_event.go (about)

     1  // Copyright 2024 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 trace
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"path/filepath"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/fagongzi/util/format"
    25  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    26  	"github.com/matrixorigin/matrixone/pkg/common/reuse"
    27  	"github.com/matrixorigin/matrixone/pkg/common/util"
    28  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    29  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    30  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    31  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    32  	"github.com/matrixorigin/matrixone/pkg/txn/client"
    33  	"github.com/matrixorigin/matrixone/pkg/util/executor"
    34  	"go.uber.org/zap"
    35  )
    36  
    37  func (s *service) TxnCreated(op client.TxnOperator) {
    38  	if s.atomic.closed.Load() {
    39  		return
    40  	}
    41  
    42  	if !s.atomic.txnEventEnabled.Load() &&
    43  		!s.atomic.txnActionEventEnabled.Load() {
    44  		return
    45  	}
    46  
    47  	filters := s.atomic.txnFilters.Load()
    48  	if skipped := filters.filter(op); skipped {
    49  		return
    50  	}
    51  
    52  	register := false
    53  	if s.atomic.txnEventEnabled.Load() {
    54  		register = true
    55  		s.txnC <- event{
    56  			csv: newTxnCreated(op.Txn()),
    57  		}
    58  	}
    59  
    60  	if s.atomic.txnActionEventEnabled.Load() {
    61  		register = true
    62  		s.doAddTxnAction(
    63  			op.Txn().ID,
    64  			client.OpenEvent.Name,
    65  			0,
    66  			0,
    67  			0,
    68  			"",
    69  			nil)
    70  	}
    71  
    72  	if register {
    73  		op.AppendEventCallback(client.WaitActiveEvent, s.handleTxnActive)
    74  		op.AppendEventCallback(client.UpdateSnapshotEvent, s.handleTxnUpdateSnapshot)
    75  		op.AppendEventCallback(client.CommitEvent, s.handleTxnCommit)
    76  		op.AppendEventCallback(client.RollbackEvent, s.handleTxnRollback)
    77  
    78  		op.AppendEventCallback(client.CommitResponseEvent, s.handleTxnActionEvent)
    79  		op.AppendEventCallback(client.CommitWaitApplyEvent, s.handleTxnActionEvent)
    80  		op.AppendEventCallback(client.UnlockEvent, s.handleTxnActionEvent)
    81  		op.AppendEventCallback(client.RangesEvent, s.handleTxnActionEvent)
    82  		op.AppendEventCallback(client.BuildPlanEvent, s.handleTxnActionEvent)
    83  		op.AppendEventCallback(client.ExecuteSQLEvent, s.handleTxnActionEvent)
    84  		op.AppendEventCallback(client.CompileEvent, s.handleTxnActionEvent)
    85  		op.AppendEventCallback(client.TableScanEvent, s.handleTxnActionEvent)
    86  		op.AppendEventCallback(client.WorkspaceWriteEvent, s.handleTxnActionEvent)
    87  		op.AppendEventCallback(client.WorkspaceAdjustEvent, s.handleTxnActionEvent)
    88  	}
    89  }
    90  
    91  func (s *service) TxnExecSQL(
    92  	op client.TxnOperator,
    93  	sql string) {
    94  	if !s.Enabled(FeatureTraceTxn) {
    95  		return
    96  	}
    97  
    98  	if s.atomic.closed.Load() {
    99  		return
   100  	}
   101  
   102  	filters := s.atomic.txnFilters.Load()
   103  	if skipped := filters.filter(op); skipped {
   104  		return
   105  	}
   106  
   107  	sql = truncateSQL(sql)
   108  	s.txnC <- event{
   109  		csv: newTxnInfoEvent(op.Txn(), txnExecuteEvent, sql),
   110  	}
   111  }
   112  
   113  func (s *service) TxnConflictChanged(
   114  	op client.TxnOperator,
   115  	tableID uint64,
   116  	lastCommitAt timestamp.Timestamp,
   117  ) {
   118  	if !s.Enabled(FeatureTraceTxn) {
   119  		return
   120  	}
   121  
   122  	if s.atomic.closed.Load() {
   123  		return
   124  	}
   125  
   126  	filters := s.atomic.txnFilters.Load()
   127  	if skipped := filters.filter(op); skipped {
   128  		return
   129  	}
   130  
   131  	buf := reuse.Alloc[buffer](nil)
   132  	table := buf.writeUint(tableID)
   133  	ts := buf.writeTimestamp(lastCommitAt)
   134  
   135  	idx := buf.buf.GetWriteIndex()
   136  	buf.buf.WriteString("table:")
   137  	buf.buf.WriteString(table)
   138  	buf.buf.WriteString(", new-min-snapshot-ts: ")
   139  	buf.buf.WriteString(ts)
   140  	info := buf.buf.RawSlice(idx, buf.buf.GetWriteIndex())
   141  	s.txnC <- event{
   142  		csv: newTxnInfoEvent(
   143  			op.Txn(),
   144  			txnConflictChanged,
   145  			util.UnsafeBytesToString(info),
   146  		),
   147  	}
   148  	s.txnC <- event{
   149  		buffer: buf,
   150  	}
   151  }
   152  
   153  func (s *service) TxnNoConflictChanged(
   154  	op client.TxnOperator,
   155  	tableID uint64,
   156  	lockedAt, newSnapshotTS timestamp.Timestamp,
   157  ) {
   158  	if !s.Enabled(FeatureTraceTxn) {
   159  		return
   160  	}
   161  
   162  	if s.atomic.closed.Load() {
   163  		return
   164  	}
   165  
   166  	filters := s.atomic.txnFilters.Load()
   167  	if skipped := filters.filter(op); skipped {
   168  		return
   169  	}
   170  
   171  	buf := reuse.Alloc[buffer](nil)
   172  	table := buf.writeUint(tableID)
   173  	locked := buf.writeTimestamp(lockedAt)
   174  	newSnapshot := buf.writeTimestamp(newSnapshotTS)
   175  
   176  	idx := buf.buf.GetWriteIndex()
   177  	buf.buf.WriteString("table:")
   178  	buf.buf.WriteString(table)
   179  	buf.buf.WriteString(", locked-ts: ")
   180  	buf.buf.WriteString(locked)
   181  	buf.buf.WriteString(", new-min-snapshot-ts: ")
   182  	buf.buf.WriteString(newSnapshot)
   183  	info := buf.buf.RawSlice(idx, buf.buf.GetWriteIndex())
   184  	s.txnC <- event{
   185  		csv: newTxnInfoEvent(
   186  			op.Txn(),
   187  			txnNoConflictChanged,
   188  			util.UnsafeBytesToString(info),
   189  		),
   190  	}
   191  	s.txnC <- event{
   192  		buffer: buf,
   193  	}
   194  }
   195  
   196  func (s *service) TxnUpdateSnapshot(
   197  	op client.TxnOperator,
   198  	tableID uint64,
   199  	why string) {
   200  	if !s.Enabled(FeatureTraceTxn) {
   201  		return
   202  	}
   203  
   204  	if s.atomic.closed.Load() {
   205  		return
   206  	}
   207  
   208  	filters := s.atomic.txnFilters.Load()
   209  	if skipped := filters.filter(op); skipped {
   210  		return
   211  	}
   212  
   213  	buf := reuse.Alloc[buffer](nil)
   214  	table := buf.writeUint(tableID)
   215  
   216  	idx := buf.buf.GetWriteIndex()
   217  	buf.buf.WriteString(why)
   218  	buf.buf.WriteString(" table:")
   219  	buf.buf.WriteString(table)
   220  	info := buf.buf.RawSlice(idx, buf.buf.GetWriteIndex())
   221  	s.txnC <- event{
   222  		csv: newTxnInfoEvent(
   223  			op.Txn(),
   224  			txnUpdateSnapshotReasonEvent,
   225  			util.UnsafeBytesToString(info),
   226  		),
   227  	}
   228  	s.txnC <- event{
   229  		buffer: buf,
   230  	}
   231  }
   232  
   233  func (s *service) TxnCommit(
   234  	op client.TxnOperator,
   235  	entries []*api.Entry) {
   236  	if !s.Enabled(FeatureTraceTxn) {
   237  		return
   238  	}
   239  
   240  	if s.atomic.closed.Load() {
   241  		return
   242  	}
   243  
   244  	txnFilters := s.atomic.txnFilters.Load()
   245  	if skipped := txnFilters.filter(op); skipped {
   246  		return
   247  	}
   248  
   249  	ts := time.Now().UnixNano()
   250  	buf := reuse.Alloc[buffer](nil)
   251  	var entryData *EntryData
   252  	defer func() {
   253  		entryData.close()
   254  	}()
   255  
   256  	tableFilters := s.atomic.tableFilters.Load()
   257  
   258  	n := 0
   259  	for _, entry := range entries {
   260  		if entryData == nil {
   261  			entryData = newEntryData(entry, -1, ts)
   262  		} else {
   263  			entryData.reset()
   264  			entryData.init(entry, -1, ts)
   265  		}
   266  
   267  		if skipped := tableFilters.filter(entryData); skipped {
   268  			continue
   269  		}
   270  
   271  		entryData.createCommit(
   272  			op.Txn().ID,
   273  			buf,
   274  			func(e dataEvent) {
   275  				s.entryC <- event{
   276  					csv: e,
   277  				}
   278  			},
   279  			&s.atomic.complexPKTables)
   280  		n++
   281  	}
   282  
   283  	if n == 0 {
   284  		buf.close()
   285  		return
   286  	}
   287  	s.entryC <- event{
   288  		buffer: buf,
   289  	}
   290  }
   291  
   292  func (s *service) TxnRead(
   293  	op client.TxnOperator,
   294  	snapshotTS timestamp.Timestamp,
   295  	tableID uint64,
   296  	columns []string,
   297  	bat *batch.Batch) {
   298  	if !s.Enabled(FeatureTraceData) {
   299  		return
   300  	}
   301  
   302  	if s.atomic.closed.Load() {
   303  		return
   304  	}
   305  
   306  	filters := s.atomic.txnFilters.Load()
   307  	if skipped := filters.filter(op); skipped {
   308  		return
   309  	}
   310  
   311  	entryData := newReadEntryData(tableID, snapshotTS, bat, columns, time.Now().UnixNano())
   312  	defer func() {
   313  		entryData.close()
   314  	}()
   315  
   316  	tableFilters := s.atomic.tableFilters.Load()
   317  	if skipped := tableFilters.filter(entryData); skipped {
   318  		return
   319  	}
   320  
   321  	buf := reuse.Alloc[buffer](nil)
   322  	entryData.createRead(
   323  		op.Txn().ID,
   324  		buf,
   325  		func(e dataEvent) {
   326  			s.entryC <- event{
   327  				csv: e,
   328  			}
   329  		},
   330  		&s.atomic.complexPKTables)
   331  	s.entryC <- event{
   332  		buffer: buf,
   333  	}
   334  }
   335  
   336  func (s *service) TxnReadBlock(
   337  	op client.TxnOperator,
   338  	tableID uint64,
   339  	block []byte) {
   340  	if !s.Enabled(FeatureTraceTxn) {
   341  		return
   342  	}
   343  
   344  	if s.atomic.closed.Load() {
   345  		return
   346  	}
   347  
   348  	filters := s.atomic.txnFilters.Load()
   349  	if skipped := filters.filter(op); skipped {
   350  		return
   351  	}
   352  
   353  	entryData := newTableOnlyEntryData(tableID)
   354  	defer func() {
   355  		entryData.close()
   356  	}()
   357  
   358  	tableFilters := s.atomic.tableFilters.Load()
   359  	if skipped := tableFilters.filter(entryData); skipped {
   360  		return
   361  	}
   362  
   363  	buf := reuse.Alloc[buffer](nil)
   364  	s.entryC <- event{
   365  		csv: newReadBlockEvent(
   366  			time.Now().UnixNano(),
   367  			op.Txn().ID,
   368  			tableID,
   369  			buf.writeHexWithBytes(block),
   370  		),
   371  	}
   372  	s.entryC <- event{
   373  		buffer: buf,
   374  	}
   375  }
   376  
   377  func (s *service) TxnWrite(
   378  	op client.TxnOperator,
   379  	tableID uint64,
   380  	typ string,
   381  	bat *batch.Batch,
   382  ) {
   383  	if !s.Enabled(FeatureTraceTxnWorkspace) {
   384  		return
   385  	}
   386  
   387  	if s.atomic.closed.Load() {
   388  		return
   389  	}
   390  
   391  	filters := s.atomic.txnFilters.Load()
   392  	if skipped := filters.filter(op); skipped {
   393  		return
   394  	}
   395  
   396  	entryData := newWriteEntryData(tableID, bat, time.Now().UnixNano())
   397  	defer func() {
   398  		entryData.close()
   399  	}()
   400  
   401  	tableFilters := s.atomic.tableFilters.Load()
   402  	if skipped := tableFilters.filter(entryData); skipped {
   403  		return
   404  	}
   405  
   406  	buf := reuse.Alloc[buffer](nil)
   407  	entryData.createWrite(
   408  		op.Txn().ID,
   409  		buf,
   410  		typ,
   411  		func(e dataEvent) {
   412  			s.entryC <- event{
   413  				csv: e,
   414  			}
   415  		},
   416  		&s.atomic.complexPKTables)
   417  	s.entryC <- event{
   418  		buffer: buf,
   419  	}
   420  }
   421  
   422  func (s *service) TxnAdjustWorkspace(
   423  	op client.TxnOperator,
   424  	adjustCount int,
   425  	writes func() (tableID uint64, typ string, bat *batch.Batch, more bool),
   426  ) {
   427  	if !s.Enabled(FeatureTraceTxnWorkspace) {
   428  		return
   429  	}
   430  
   431  	if s.atomic.closed.Load() {
   432  		return
   433  	}
   434  
   435  	filters := s.atomic.txnFilters.Load()
   436  	if skipped := filters.filter(op); skipped {
   437  		return
   438  	}
   439  
   440  	at := time.Now().UnixNano()
   441  
   442  	offsetCount := 0
   443  	for {
   444  		tableID, typ, bat, more := writes()
   445  		if !more {
   446  			return
   447  		}
   448  
   449  		func() {
   450  			entryData := newWorkspaceEntryData(tableID, bat, at)
   451  			defer func() {
   452  				entryData.close()
   453  			}()
   454  
   455  			tableFilters := s.atomic.tableFilters.Load()
   456  			if skipped := tableFilters.filter(entryData); skipped {
   457  				return
   458  			}
   459  
   460  			buf := reuse.Alloc[buffer](nil)
   461  			entryData.createWorkspace(
   462  				op.Txn().ID,
   463  				buf,
   464  				typ,
   465  				adjustCount,
   466  				offsetCount,
   467  				func(e dataEvent) {
   468  					s.entryC <- event{
   469  						csv: e,
   470  					}
   471  				},
   472  				&s.atomic.complexPKTables)
   473  			s.entryC <- event{
   474  				buffer: buf,
   475  			}
   476  		}()
   477  		offsetCount++
   478  	}
   479  }
   480  
   481  func (s *service) TxnEventEnabled() bool {
   482  	return s.atomic.txnEventEnabled.Load()
   483  }
   484  
   485  func (s *service) handleTxnActive(e client.TxnEvent) {
   486  	if s.atomic.closed.Load() {
   487  		return
   488  	}
   489  
   490  	if !e.CostEvent && s.atomic.txnEventEnabled.Load() {
   491  		s.txnC <- event{
   492  			csv: newTxnActive(e.Txn),
   493  		}
   494  	}
   495  
   496  	if s.atomic.txnActionEventEnabled.Load() {
   497  		s.doTxnEventAction(e)
   498  	}
   499  }
   500  
   501  func (s *service) handleTxnUpdateSnapshot(e client.TxnEvent) {
   502  	if s.atomic.closed.Load() {
   503  		return
   504  	}
   505  
   506  	if e.CostEvent && s.atomic.txnEventEnabled.Load() {
   507  		s.txnC <- event{
   508  			csv: newTxnSnapshotUpdated(e.Txn),
   509  		}
   510  	}
   511  
   512  	if s.atomic.txnActionEventEnabled.Load() {
   513  		s.doTxnEventAction(e)
   514  	}
   515  }
   516  
   517  func (s *service) handleTxnCommit(e client.TxnEvent) {
   518  	if s.atomic.closed.Load() {
   519  		return
   520  	}
   521  
   522  	if e.CostEvent && s.atomic.txnEventEnabled.Load() {
   523  		s.txnC <- event{
   524  			csv: newTxnClosed(e.Txn),
   525  		}
   526  		if e.Err != nil {
   527  			s.doAddTxnError(e.Txn.ID, e.Err)
   528  		}
   529  	}
   530  
   531  	if s.atomic.txnActionEventEnabled.Load() {
   532  		s.doTxnEventAction(e)
   533  	}
   534  }
   535  
   536  func (s *service) handleTxnRollback(e client.TxnEvent) {
   537  	if s.atomic.closed.Load() {
   538  		return
   539  	}
   540  
   541  	if e.CostEvent && s.atomic.txnEventEnabled.Load() {
   542  		s.txnC <- event{
   543  			csv: newTxnClosed(e.Txn),
   544  		}
   545  		if e.Err != nil {
   546  			s.doAddTxnError(e.Txn.ID, e.Err)
   547  		}
   548  	}
   549  
   550  	if s.atomic.txnActionEventEnabled.Load() {
   551  		s.doTxnEventAction(e)
   552  	}
   553  }
   554  
   555  func (s *service) handleTxnActionEvent(event client.TxnEvent) {
   556  	if s.atomic.closed.Load() {
   557  		return
   558  	}
   559  
   560  	if s.atomic.txnActionEventEnabled.Load() {
   561  		s.doTxnEventAction(event)
   562  	}
   563  }
   564  
   565  func (s *service) TxnError(
   566  	op client.TxnOperator,
   567  	value error) {
   568  	if s.atomic.closed.Load() {
   569  		return
   570  	}
   571  
   572  	if op.TxnOptions().TraceDisabled() {
   573  		return
   574  	}
   575  
   576  	if !s.atomic.txnEventEnabled.Load() {
   577  		return
   578  	}
   579  
   580  	s.doAddTxnError(op.Txn().ID, value)
   581  }
   582  
   583  func (s *service) doAddTxnError(
   584  	txnID []byte,
   585  	value error) {
   586  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
   587  	defer cancel()
   588  
   589  	ts := time.Now().UnixNano()
   590  	msg := value.Error()
   591  	if me, ok := value.(*moerr.Error); ok {
   592  		msg += ": " + me.Detail()
   593  	}
   594  
   595  	sql := fmt.Sprintf("insert into %s (ts, txn_id, error_info) values (%d, '%x', '%s')",
   596  		EventErrorTable,
   597  		ts,
   598  		txnID,
   599  		escape(msg))
   600  
   601  	now, _ := s.clock.Now()
   602  	err := s.executor.ExecTxn(
   603  		ctx,
   604  		func(txn executor.TxnExecutor) error {
   605  			res, err := txn.Exec(sql,
   606  				executor.StatementOption{})
   607  			if err != nil {
   608  				return err
   609  			}
   610  			res.Close()
   611  			return nil
   612  		},
   613  		executor.Options{}.
   614  			WithDatabase(DebugDB).
   615  			WithMinCommittedTS(now).
   616  			WithWaitCommittedLogApplied().
   617  			WithDisableTrace())
   618  	if err != nil {
   619  		s.logger.Error("exec txn error trace failed",
   620  			zap.String("sql", sql),
   621  			zap.Error(err))
   622  	}
   623  }
   624  
   625  func (s *service) TxnStatementStart(
   626  	op client.TxnOperator,
   627  	sql string,
   628  	seq uint64,
   629  ) {
   630  	if !s.Enabled(FeatureTraceTxnAction) &&
   631  		!s.Enabled(FeatureTraceTxn) {
   632  		return
   633  	}
   634  
   635  	s.TxnExecSQL(op, sql)
   636  	s.AddTxnDurationAction(
   637  		op,
   638  		client.ExecuteSQLEvent,
   639  		seq,
   640  		0,
   641  		0,
   642  		nil)
   643  }
   644  
   645  func (s *service) TxnStatementCompleted(
   646  	op client.TxnOperator,
   647  	sql string,
   648  	cost time.Duration,
   649  	seq uint64,
   650  	affectRows int,
   651  	err error,
   652  ) {
   653  	if !s.Enabled(FeatureTraceTxnAction) &&
   654  		!s.Enabled(FeatureTraceTxn) &&
   655  		!s.Enabled(FeatureTraceStatement) {
   656  		return
   657  	}
   658  
   659  	buf := reuse.Alloc[buffer](nil)
   660  	if err == nil {
   661  		err = infoErr{
   662  			info: buf.writeInt(int64(affectRows)),
   663  		}
   664  	}
   665  
   666  	s.AddTxnDurationAction(
   667  		op,
   668  		client.ExecuteSQLEvent,
   669  		seq,
   670  		0,
   671  		cost,
   672  		err)
   673  
   674  	s.AddStatement(
   675  		op,
   676  		sql,
   677  		cost)
   678  
   679  	s.txnActionC <- event{
   680  		buffer: buf,
   681  	}
   682  }
   683  
   684  func (s *service) AddTxnDurationAction(
   685  	op client.TxnOperator,
   686  	eventType client.EventType,
   687  	seq uint64,
   688  	tableID uint64,
   689  	value time.Duration,
   690  	err error,
   691  ) {
   692  	s.AddTxnAction(
   693  		op,
   694  		eventType,
   695  		seq,
   696  		tableID,
   697  		value.Microseconds(),
   698  		"us",
   699  		err)
   700  }
   701  
   702  func (s *service) AddTxnAction(
   703  	op client.TxnOperator,
   704  	eventType client.EventType,
   705  	seq uint64,
   706  	tableID uint64,
   707  	value int64,
   708  	unit string,
   709  	err error,
   710  ) {
   711  	if !s.Enabled(FeatureTraceTxnAction) {
   712  		return
   713  	}
   714  
   715  	if s.atomic.closed.Load() {
   716  		return
   717  	}
   718  
   719  	filters := s.atomic.txnFilters.Load()
   720  	if skipped := filters.filter(op); skipped {
   721  		return
   722  	}
   723  
   724  	s.doAddTxnAction(
   725  		op.Txn().ID,
   726  		eventType.Name,
   727  		seq,
   728  		tableID,
   729  		value,
   730  		unit,
   731  		err)
   732  }
   733  
   734  func (s *service) AddTxnActionInfo(
   735  	op client.TxnOperator,
   736  	eventType client.EventType,
   737  	seq uint64,
   738  	tableID uint64,
   739  	value func(Writer),
   740  ) {
   741  	if !s.Enabled(FeatureTraceTxnAction) {
   742  		return
   743  	}
   744  
   745  	if s.atomic.closed.Load() {
   746  		return
   747  	}
   748  
   749  	filters := s.atomic.txnFilters.Load()
   750  	if skipped := filters.filter(op); skipped {
   751  		return
   752  	}
   753  
   754  	buf := reuse.Alloc[buffer](nil)
   755  	w := writer{
   756  		buf: buf.buf,
   757  		dst: buf.alloc(1024),
   758  		idx: buf.buf.GetWriteIndex(),
   759  	}
   760  	value(w)
   761  
   762  	s.txnActionC <- event{
   763  		csv: actionEvent{
   764  			ts:        time.Now().UnixNano(),
   765  			action:    eventType.Name,
   766  			actionSeq: seq,
   767  			tableID:   tableID,
   768  			value:     0,
   769  			unit:      w.data(),
   770  			txnID:     op.Txn().ID,
   771  			err:       "",
   772  		},
   773  	}
   774  	s.txnActionC <- event{
   775  		buffer: buf,
   776  	}
   777  }
   778  
   779  func (s *service) doTxnEventAction(event client.TxnEvent) {
   780  	unit := ""
   781  	if event.CostEvent {
   782  		unit = "us"
   783  	}
   784  	s.doAddTxnAction(
   785  		event.Txn.ID,
   786  		event.Event.Name,
   787  		event.Sequence,
   788  		0,
   789  		event.Cost.Microseconds(),
   790  		unit,
   791  		event.Err)
   792  }
   793  
   794  func (s *service) doAddTxnAction(
   795  	txnID []byte,
   796  	action string,
   797  	actionSequence uint64,
   798  	tableID uint64,
   799  	value int64,
   800  	unit string,
   801  	err error,
   802  ) {
   803  	e := ""
   804  	if err != nil {
   805  		e = err.Error()
   806  	}
   807  	s.txnActionC <- event{
   808  		csv: actionEvent{
   809  			ts:        time.Now().UnixNano(),
   810  			action:    action,
   811  			actionSeq: actionSequence,
   812  			tableID:   tableID,
   813  			value:     int64(value),
   814  			unit:      unit,
   815  			txnID:     txnID,
   816  			err:       e,
   817  		},
   818  	}
   819  }
   820  
   821  func (s *service) AddTxnFilter(method, value string) error {
   822  	switch method {
   823  	case sessionMethod, connectionMethod, tenantMethod, userMethod:
   824  	default:
   825  		return moerr.NewNotSupportedNoCtx("method %s not support", method)
   826  	}
   827  
   828  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   829  	defer cancel()
   830  
   831  	now, _ := s.clock.Now()
   832  	return s.executor.ExecTxn(
   833  		ctx,
   834  		func(txn executor.TxnExecutor) error {
   835  			r, err := txn.Exec(addTxnFilterSQL(method, value), executor.StatementOption{})
   836  			if err != nil {
   837  				return err
   838  			}
   839  			r.Close()
   840  			return nil
   841  		},
   842  		executor.Options{}.
   843  			WithDatabase(DebugDB).
   844  			WithMinCommittedTS(now).
   845  			WithWaitCommittedLogApplied().
   846  			WithDisableTrace())
   847  }
   848  
   849  func (s *service) ClearTxnFilters() error {
   850  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   851  	defer cancel()
   852  
   853  	now, _ := s.clock.Now()
   854  	err := s.executor.ExecTxn(
   855  		ctx,
   856  		func(txn executor.TxnExecutor) error {
   857  			txn.Use(DebugDB)
   858  			res, err := txn.Exec(
   859  				fmt.Sprintf("truncate table %s",
   860  					TraceTxnFilterTable),
   861  				executor.StatementOption{})
   862  			if err != nil {
   863  				return err
   864  			}
   865  			res.Close()
   866  			return nil
   867  		},
   868  		executor.Options{}.
   869  			WithDisableTrace().
   870  			WithMinCommittedTS(now).
   871  			WithWaitCommittedLogApplied())
   872  	if err != nil {
   873  		return err
   874  	}
   875  
   876  	return s.RefreshTxnFilters()
   877  }
   878  
   879  func (s *service) RefreshTxnFilters() error {
   880  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   881  	defer cancel()
   882  
   883  	var filters []TxnFilter
   884  	var methods []string
   885  	var values []string
   886  	now, _ := s.clock.Now()
   887  	err := s.executor.ExecTxn(
   888  		ctx,
   889  		func(txn executor.TxnExecutor) error {
   890  			txn.Use(DebugDB)
   891  			res, err := txn.Exec(
   892  				fmt.Sprintf("select method, value from %s",
   893  					TraceTxnFilterTable),
   894  				executor.StatementOption{})
   895  			if err != nil {
   896  				return err
   897  			}
   898  			defer res.Close()
   899  
   900  			res.ReadRows(func(rows int, cols []*vector.Vector) bool {
   901  				for i := 0; i < rows; i++ {
   902  					methods = append(methods, cols[0].GetStringAt(i))
   903  					values = append(values, cols[1].GetStringAt(i))
   904  				}
   905  				return true
   906  			})
   907  			return nil
   908  		},
   909  		executor.Options{}.
   910  			WithDisableTrace().
   911  			WithMinCommittedTS(now).
   912  			WithWaitCommittedLogApplied())
   913  	if err != nil {
   914  		return err
   915  	}
   916  
   917  	filters = append(filters, &disableFilter{})
   918  	for i, method := range methods {
   919  		switch method {
   920  		case sessionMethod:
   921  			filters = append(filters, &sessionIDFilter{sessionID: values[i]})
   922  		case connectionMethod:
   923  			params := strings.Split(values[i], ",")
   924  			filters = append(filters,
   925  				&connectionIDFilter{
   926  					sessionID:    params[0],
   927  					connectionID: format.MustParseStringUint32(params[1]),
   928  				})
   929  		case tenantMethod:
   930  			params := strings.Split(values[i], ",")
   931  			filters = append(filters,
   932  				&tenantFilter{
   933  					accountID: format.MustParseStringUint32(params[0]),
   934  					userName:  params[1],
   935  				})
   936  		case userMethod:
   937  			filters = append(filters,
   938  				&userFilter{
   939  					userName: values[i],
   940  				})
   941  		}
   942  	}
   943  
   944  	s.atomic.txnFilters.Store(&txnFilters{filters: filters})
   945  	return nil
   946  }
   947  
   948  func (s *service) handleTxnEvents(ctx context.Context) {
   949  	s.handleEvent(
   950  		ctx,
   951  		s.txnCSVFile,
   952  		8,
   953  		EventTxnTable,
   954  		s.txnC,
   955  	)
   956  }
   957  
   958  func (s *service) handleTxnActionEvents(ctx context.Context) {
   959  	s.handleEvent(
   960  		ctx,
   961  		s.txnActionCSVFile,
   962  		9,
   963  		EventTxnActionTable,
   964  		s.txnActionC,
   965  	)
   966  }
   967  
   968  func (s *service) txnCSVFile() string {
   969  	return filepath.Join(s.dir, fmt.Sprintf("txn-%d.csv", s.seq.Add(1)))
   970  }
   971  
   972  func (s *service) txnActionCSVFile() string {
   973  	return filepath.Join(s.dir, fmt.Sprintf("txn-action-%d.csv", s.seq.Add(1)))
   974  }
   975  
   976  func addTxnFilterSQL(
   977  	method string,
   978  	value string,
   979  ) string {
   980  	return fmt.Sprintf("insert into %s (method, value) values ('%s', '%s')",
   981  		TraceTxnFilterTable,
   982  		method,
   983  		value)
   984  }
   985  
   986  type infoErr struct {
   987  	info string
   988  }
   989  
   990  func (e infoErr) Error() string {
   991  	return e.info
   992  }