github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/server/statement_diagnostics_requests.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package server 12 13 import ( 14 "bytes" 15 "context" 16 "encoding/json" 17 "time" 18 19 "github.com/cockroachdb/cockroach/pkg/security" 20 "github.com/cockroachdb/cockroach/pkg/server/serverpb" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 23 "github.com/cockroachdb/errors" 24 ) 25 26 type stmtDiagnosticsRequest struct { 27 ID int 28 StatementFingerprint string 29 Completed bool 30 StatementDiagnosticsID int 31 RequestedAt time.Time 32 } 33 34 type stmtDiagnostics struct { 35 ID int 36 StatementFingerprint string 37 Trace string 38 CollectedAt time.Time 39 } 40 41 func (request *stmtDiagnosticsRequest) toProto() serverpb.StatementDiagnosticsReport { 42 resp := serverpb.StatementDiagnosticsReport{ 43 Id: int64(request.ID), 44 Completed: request.Completed, 45 StatementFingerprint: request.StatementFingerprint, 46 StatementDiagnosticsId: int64(request.StatementDiagnosticsID), 47 RequestedAt: request.RequestedAt, 48 } 49 return resp 50 } 51 52 func (diagnostics *stmtDiagnostics) toProto() serverpb.StatementDiagnostics { 53 resp := serverpb.StatementDiagnostics{ 54 Id: int64(diagnostics.ID), 55 StatementFingerprint: diagnostics.StatementFingerprint, 56 CollectedAt: diagnostics.CollectedAt, 57 Trace: diagnostics.Trace, 58 } 59 return resp 60 } 61 62 // CreateStatementDiagnosticsRequest creates a statement diagnostics 63 // request in the `system.statement_diagnostics_requests` table 64 // to trace the next query matching the provided fingerprint. 65 func (s *statusServer) CreateStatementDiagnosticsReport( 66 ctx context.Context, req *serverpb.CreateStatementDiagnosticsReportRequest, 67 ) (*serverpb.CreateStatementDiagnosticsReportResponse, error) { 68 if _, err := s.admin.requireAdminUser(ctx); err != nil { 69 return nil, err 70 } 71 72 ctx = propagateGatewayMetadata(ctx) 73 ctx = s.AnnotateCtx(ctx) 74 75 response := &serverpb.CreateStatementDiagnosticsReportResponse{ 76 Report: &serverpb.StatementDiagnosticsReport{}, 77 } 78 79 err := s.stmtDiagnosticsRequester.InsertRequest(ctx, req.StatementFingerprint) 80 if err != nil { 81 return nil, err 82 } 83 84 response.Report.StatementFingerprint = req.StatementFingerprint 85 return response, nil 86 } 87 88 // StatementDiagnosticsRequests retrieves all of the statement 89 // diagnostics requests in the `system.statement_diagnostics_requests` table. 90 func (s *statusServer) StatementDiagnosticsRequests( 91 ctx context.Context, req *serverpb.StatementDiagnosticsReportsRequest, 92 ) (*serverpb.StatementDiagnosticsReportsResponse, error) { 93 if _, err := s.admin.requireAdminUser(ctx); err != nil { 94 return nil, err 95 } 96 97 ctx = propagateGatewayMetadata(ctx) 98 ctx = s.AnnotateCtx(ctx) 99 100 var err error 101 102 // TODO(davidh): Add pagination to this request. 103 rows, err := s.internalExecutor.QueryEx(ctx, "stmt-diag-get-all", nil, /* txn */ 104 sqlbase.InternalExecutorSessionDataOverride{ 105 User: security.RootUser, 106 }, 107 `SELECT 108 id, 109 statement_fingerprint, 110 completed, 111 statement_diagnostics_id, 112 requested_at 113 FROM 114 system.statement_diagnostics_requests`) 115 if err != nil { 116 return nil, err 117 } 118 119 requests := make([]stmtDiagnosticsRequest, len(rows)) 120 for i, row := range rows { 121 id := int(*row[0].(*tree.DInt)) 122 statementFingerprint := string(*row[1].(*tree.DString)) 123 completed := bool(*row[2].(*tree.DBool)) 124 req := stmtDiagnosticsRequest{ 125 ID: id, 126 StatementFingerprint: statementFingerprint, 127 Completed: completed, 128 } 129 130 if row[3] != tree.DNull { 131 sdi := int(*row[3].(*tree.DInt)) 132 req.StatementDiagnosticsID = sdi 133 } 134 135 if requestedAt, ok := row[4].(*tree.DTimestampTZ); ok { 136 req.RequestedAt = requestedAt.Time 137 } 138 139 requests[i] = req 140 } 141 142 response := &serverpb.StatementDiagnosticsReportsResponse{ 143 Reports: make([]serverpb.StatementDiagnosticsReport, len(requests)), 144 } 145 146 for i, request := range requests { 147 response.Reports[i] = request.toProto() 148 } 149 return response, nil 150 } 151 152 // StatementDiagnostics retrieves a statement diagnostics instance 153 // identified by the given ID. These are in the 154 // `system.statement_diagnostics` table. 155 // 156 // This is generated once the trace is completed on a request created 157 // by the CreateStatementDiagnosticsRequest call and is linked to 158 // the original request with its ID. 159 func (s *statusServer) StatementDiagnostics( 160 ctx context.Context, req *serverpb.StatementDiagnosticsRequest, 161 ) (*serverpb.StatementDiagnosticsResponse, error) { 162 if _, err := s.admin.requireAdminUser(ctx); err != nil { 163 return nil, err 164 } 165 166 ctx = propagateGatewayMetadata(ctx) 167 ctx = s.AnnotateCtx(ctx) 168 169 var err error 170 row, err := s.internalExecutor.QueryRowEx(ctx, "stmt-diag-get-one", nil, /* txn */ 171 sqlbase.InternalExecutorSessionDataOverride{ 172 User: security.RootUser, 173 }, 174 `SELECT 175 id, 176 statement_fingerprint, 177 collected_at, 178 trace 179 FROM 180 system.statement_diagnostics 181 WHERE 182 id = $1`, req.StatementDiagnosticsId) 183 if err != nil { 184 return nil, err 185 } 186 if row == nil { 187 return nil, errors.Newf( 188 "requested a statement diagnostic (%d) that does not exist", 189 req.StatementDiagnosticsId, 190 ) 191 } 192 193 diagnostics := stmtDiagnostics{ 194 ID: int(req.StatementDiagnosticsId), 195 } 196 197 if statementFingerprint, ok := row[1].(*tree.DString); ok { 198 diagnostics.StatementFingerprint = statementFingerprint.String() 199 } 200 201 if collectedAt, ok := row[2].(*tree.DTimestampTZ); ok { 202 diagnostics.CollectedAt = collectedAt.Time 203 } 204 205 if traceJSON, ok := row[3].(*tree.DJSON); ok { 206 traceJSONString, err := traceJSON.AsText() 207 if err != nil { 208 return nil, err 209 } 210 var prettyJSON bytes.Buffer 211 if err := json.Indent( 212 &prettyJSON, []byte(*traceJSONString), "" /* prefix */, "\t" /* indent */); err != nil { 213 return nil, errors.Wrap(err, "failed to parse JSON") 214 } 215 diagnostics.Trace = prettyJSON.String() 216 } 217 218 diagnosticsProto := diagnostics.toProto() 219 response := &serverpb.StatementDiagnosticsResponse{ 220 Diagnostics: &diagnosticsProto, 221 } 222 223 return response, nil 224 }