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 }