github.com/matrixorigin/matrixone@v0.7.0/pkg/util/trace/impl/motrace/report_statement.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  	"errors"
    21  	"fmt"
    22  	"sync"
    23  	"sync/atomic"
    24  	"time"
    25  	"unsafe"
    26  
    27  	"github.com/matrixorigin/matrixone/pkg/util/export/table"
    28  
    29  	"github.com/google/uuid"
    30  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    31  )
    32  
    33  var nilTxnID [16]byte
    34  
    35  // StatementInfo implement export.IBuffer2SqlItem and export.CsvFields
    36  type StatementInfo struct {
    37  	StatementID          [16]byte  `json:"statement_id"`
    38  	TransactionID        [16]byte  `json:"transaction_id"`
    39  	SessionID            [16]byte  `jons:"session_id"`
    40  	Account              string    `json:"account"`
    41  	User                 string    `json:"user"`
    42  	Host                 string    `json:"host"`
    43  	RoleId               uint32    `json:"role_id"`
    44  	Database             string    `json:"database"`
    45  	Statement            string    `json:"statement"`
    46  	StatementFingerprint string    `json:"statement_fingerprint"`
    47  	StatementTag         string    `json:"statement_tag"`
    48  	SqlSourceType        string    `json:"sql_source_type"`
    49  	RequestAt            time.Time `json:"request_at"` // see WithRequestAt
    50  
    51  	StatementType string `json:"statement_type"`
    52  	QueryType     string `json:"query_type"`
    53  
    54  	// after
    55  	Status     StatementInfoStatus `json:"status"`
    56  	Error      error               `json:"error"`
    57  	ResponseAt time.Time           `json:"response_at"`
    58  	Duration   time.Duration       `json:"duration"` // unit: ns
    59  	ExecPlan   any                 `json:"exec_plan"`
    60  	// RowsRead, BytesScan generated from ExecPlan
    61  	RowsRead  int64 `json:"rows_read"`  // see ExecPlan2Json
    62  	BytesScan int64 `json:"bytes_scan"` // see ExecPlan2Json
    63  	// SerializeExecPlan
    64  	SerializeExecPlan SerializeExecPlanFunc // see SetExecPlan, ExecPlan2Json
    65  
    66  	// flow ctrl
    67  	end bool // cooperate with mux
    68  	mux sync.Mutex
    69  	// mark reported
    70  	reported bool
    71  	// mark exported
    72  	exported bool
    73  }
    74  
    75  type Statistic struct {
    76  	RowsRead  int64
    77  	BytesScan int64
    78  }
    79  
    80  func (s *StatementInfo) GetName() string {
    81  	return SingleStatementTable.GetName()
    82  }
    83  
    84  func (s *StatementInfo) Size() int64 {
    85  	return int64(unsafe.Sizeof(s)) + int64(
    86  		len(s.Account)+len(s.User)+len(s.Host)+
    87  			len(s.Database)+len(s.Statement)+len(s.StatementFingerprint)+len(s.StatementTag),
    88  	)
    89  }
    90  
    91  func (s *StatementInfo) Free() {
    92  	s.mux.Lock()
    93  	defer s.mux.Unlock()
    94  	if s.end { // cooperate with s.mux
    95  		s.Statement = ""
    96  		s.StatementFingerprint = ""
    97  		s.StatementTag = ""
    98  		s.ExecPlan = nil
    99  		s.Error = nil
   100  	}
   101  }
   102  
   103  func (s *StatementInfo) GetTable() *table.Table { return SingleStatementTable }
   104  
   105  func (s *StatementInfo) FillRow(ctx context.Context, row *table.Row) {
   106  	s.mux.Lock()
   107  	defer s.mux.Unlock()
   108  	s.exported = true
   109  	row.Reset()
   110  	row.SetColumnVal(stmtIDCol, uuid.UUID(s.StatementID).String())
   111  	row.SetColumnVal(txnIDCol, uuid.UUID(s.TransactionID).String())
   112  	row.SetColumnVal(sesIDCol, uuid.UUID(s.SessionID).String())
   113  	row.SetColumnVal(accountCol, s.Account)
   114  	row.SetColumnVal(roleIdCol, int64(s.RoleId))
   115  	row.SetColumnVal(userCol, s.User)
   116  	row.SetColumnVal(hostCol, s.Host)
   117  	row.SetColumnVal(dbCol, s.Database)
   118  	row.SetColumnVal(stmtCol, s.Statement)
   119  	row.SetColumnVal(stmtTagCol, s.StatementTag)
   120  	row.SetColumnVal(sqlTypeCol, s.SqlSourceType)
   121  	row.SetColumnVal(stmtFgCol, s.StatementFingerprint)
   122  	row.SetColumnVal(nodeUUIDCol, GetNodeResource().NodeUuid)
   123  	row.SetColumnVal(nodeTypeCol, GetNodeResource().NodeType)
   124  	row.SetColumnVal(reqAtCol, s.RequestAt)
   125  	row.SetColumnVal(respAtCol, s.ResponseAt)
   126  	row.SetColumnVal(durationCol, uint64(s.Duration))
   127  	row.SetColumnVal(statusCol, s.Status.String())
   128  	if s.Error != nil {
   129  		var moError *moerr.Error
   130  		errCode := moerr.ErrInfo
   131  		if errors.As(s.Error, &moError) {
   132  			errCode = moError.ErrorCode()
   133  		}
   134  		row.SetColumnVal(errCodeCol, fmt.Sprintf("%d", errCode))
   135  		row.SetColumnVal(errorCol, fmt.Sprintf("%s", s.Error))
   136  	}
   137  	execPlan, stats := s.ExecPlan2Json(ctx)
   138  	row.SetColumnVal(execPlanCol, execPlan)
   139  	row.SetColumnVal(rowsReadCol, s.RowsRead)
   140  	row.SetColumnVal(bytesScanCol, s.BytesScan)
   141  	row.SetColumnVal(statsCol, stats)
   142  	row.SetColumnVal(stmtTypeCol, s.StatementType)
   143  	row.SetColumnVal(queryTypeCol, s.QueryType)
   144  }
   145  
   146  // ExecPlan2Json return ExecPlan Serialized json-str
   147  // and set RowsRead, BytesScan from ExecPlan
   148  //
   149  // please used in s.mux.Lock()
   150  func (s *StatementInfo) ExecPlan2Json(ctx context.Context) (string, string) {
   151  	var jsonByte []byte
   152  	var statsJsonByte []byte
   153  	var stats Statistic
   154  	if s.SerializeExecPlan == nil {
   155  		// use defaultSerializeExecPlan
   156  		if f := getDefaultSerializeExecPlan(); f == nil {
   157  			uuidStr := uuid.UUID(s.StatementID).String()
   158  			return fmt.Sprintf(`{"code":200,"message":"NO ExecPlan Serialize function","steps":null,"success":false,"uuid":%q}`, uuidStr),
   159  				`{"code":200,"message":"NO ExecPlan"}`
   160  		} else {
   161  			jsonByte, statsJsonByte, stats = f(ctx, s.ExecPlan, uuid.UUID(s.StatementID))
   162  			s.RowsRead, s.BytesScan = stats.RowsRead, stats.BytesScan
   163  		}
   164  	} else {
   165  		// use s.SerializeExecPlan
   166  		// get real ExecPlan json-str
   167  		jsonByte, statsJsonByte, stats = s.SerializeExecPlan(ctx, s.ExecPlan, uuid.UUID(s.StatementID))
   168  		s.RowsRead, s.BytesScan = stats.RowsRead, stats.BytesScan
   169  		if queryTime := GetTracerProvider().longQueryTime; queryTime > int64(s.Duration) {
   170  			// get nil ExecPlan json-str
   171  			jsonByte, _, _ = s.SerializeExecPlan(ctx, nil, uuid.UUID(s.StatementID))
   172  		}
   173  	}
   174  	if len(statsJsonByte) == 0 {
   175  		statsJsonByte = []byte("{}")
   176  	}
   177  	return string(jsonByte), string(statsJsonByte)
   178  }
   179  
   180  var defaultSerializeExecPlan atomic.Value
   181  
   182  type SerializeExecPlanFunc func(ctx context.Context, plan any, uuid2 uuid.UUID) (jsonByte []byte, statsJson []byte, stats Statistic)
   183  
   184  func SetDefaultSerializeExecPlan(f SerializeExecPlanFunc) {
   185  	defaultSerializeExecPlan.Store(f)
   186  }
   187  
   188  func getDefaultSerializeExecPlan() SerializeExecPlanFunc {
   189  	if defaultSerializeExecPlan.Load() == nil {
   190  		return nil
   191  	} else {
   192  		return defaultSerializeExecPlan.Load().(SerializeExecPlanFunc)
   193  	}
   194  }
   195  
   196  // SetExecPlan record execPlan should be TxnComputationWrapper.plan obj, which support 2json.
   197  func (s *StatementInfo) SetExecPlan(execPlan any, SerializeFunc SerializeExecPlanFunc) {
   198  	s.mux.Lock()
   199  	defer s.mux.Unlock()
   200  	s.ExecPlan = execPlan
   201  	s.SerializeExecPlan = SerializeFunc
   202  }
   203  
   204  func (s *StatementInfo) SetTxnID(id []byte) {
   205  	copy(s.TransactionID[:], id)
   206  }
   207  
   208  func (s *StatementInfo) IsZeroTxnID() bool {
   209  	return bytes.Equal(s.TransactionID[:], nilTxnID[:])
   210  }
   211  
   212  func (s *StatementInfo) Report(ctx context.Context) {
   213  	s.reported = true
   214  	ReportStatement(ctx, s)
   215  }
   216  
   217  var EndStatement = func(ctx context.Context, err error) {
   218  	if !GetTracerProvider().IsEnable() {
   219  		return
   220  	}
   221  	s := StatementFromContext(ctx)
   222  	if s == nil {
   223  		panic(moerr.NewInternalError(ctx, "no statement info in context"))
   224  	}
   225  	s.mux.Lock()
   226  	defer s.mux.Unlock()
   227  	if !s.end { // cooperate with s.mux
   228  		// do report
   229  		s.end = true
   230  		s.ResponseAt = time.Now()
   231  		s.Duration = s.ResponseAt.Sub(s.RequestAt)
   232  		s.Status = StatementStatusSuccess
   233  		if err != nil {
   234  			s.Error = err
   235  			s.Status = StatementStatusFailed
   236  		}
   237  		if !s.reported || s.exported { // cooperate with s.mux
   238  			s.Report(ctx)
   239  		}
   240  	}
   241  }
   242  
   243  type StatementInfoStatus int
   244  
   245  const (
   246  	StatementStatusRunning StatementInfoStatus = iota
   247  	StatementStatusSuccess
   248  	StatementStatusFailed
   249  )
   250  
   251  func (s StatementInfoStatus) String() string {
   252  	switch s {
   253  	case StatementStatusSuccess:
   254  		return "Success"
   255  	case StatementStatusRunning:
   256  		return "Running"
   257  	case StatementStatusFailed:
   258  		return "Failed"
   259  	}
   260  	return "Unknown"
   261  }
   262  
   263  type StatementOption interface {
   264  	Apply(*StatementInfo)
   265  }
   266  
   267  type StatementOptionFunc func(*StatementInfo)
   268  
   269  var ReportStatement = func(ctx context.Context, s *StatementInfo) error {
   270  	if !GetTracerProvider().IsEnable() {
   271  		return nil
   272  	}
   273  	return GetGlobalBatchProcessor().Collect(ctx, s)
   274  }