diff --git a/pkg/roachpb/app_stats.proto b/pkg/roachpb/app_stats.proto
index c5d3a8822143..658ef94698b5 100644
--- a/pkg/roachpb/app_stats.proto
+++ b/pkg/roachpb/app_stats.proto
@@ -224,6 +224,7 @@ message AggregatedStatementMetadata {
optional int64 full_scan_count = 10 [(gogoproto.nullable) = false];
optional int64 vec_count = 11 [(gogoproto.nullable) = false];
optional int64 total_count = 12 [(gogoproto.nullable) = false];
+ optional string fingerprint_id = 13 [(gogoproto.nullable) = false, (gogoproto.customname) = "FingerprintID"];
}
// CollectedStatementStatistics wraps collected timings and metadata for some
diff --git a/pkg/server/combined_statement_stats.go b/pkg/server/combined_statement_stats.go
index fe26562bc2e7..6d115f49a059 100644
--- a/pkg/server/combined_statement_stats.go
+++ b/pkg/server/combined_statement_stats.go
@@ -480,7 +480,7 @@ func getStatementDetailsQueryClausesAndArgs(
return whereClause, args, nil
}
-// getTotalStatementDetails return all the statistics for the selectec statement combined.
+// getTotalStatementDetails return all the statistics for the selected statement combined.
func getTotalStatementDetails(
ctx context.Context, ie *sql.InternalExecutor, whereClause string, args []interface{},
) (serverpb.StatementDetailsResponse_CollectedStatementSummary, error) {
@@ -490,13 +490,15 @@ func getTotalStatementDetails(
aggregation_interval,
array_agg(app_name) as app_names,
crdb_internal.merge_statement_stats(array_agg(statistics)) AS statistics,
- max(sampled_plan) as sampled_plan
+ max(sampled_plan) as sampled_plan,
+ encode(fingerprint_id, 'hex') as fingerprint_id
FROM crdb_internal.statement_statistics %s
GROUP BY
- aggregation_interval
+ aggregation_interval,
+ fingerprint_id
LIMIT 1`, whereClause)
- const expectedNumDatums = 5
+ const expectedNumDatums = 6
var statement serverpb.StatementDetailsResponse_CollectedStatementSummary
row, err := ie.QueryRowEx(ctx, "combined-stmts-details-total", nil,
@@ -552,6 +554,8 @@ func getTotalStatementDetails(
cfg.LineWidth = tree.ConsoleLineWidth
aggregatedMetadata.FormattedQuery = cfg.Pretty(queryTree.AST)
+ aggregatedMetadata.FingerprintID = string(tree.MustBeDString(row[5]))
+
statement = serverpb.StatementDetailsResponse_CollectedStatementSummary{
Metadata: aggregatedMetadata,
AggregationInterval: time.Duration(aggInterval.Nanos()),
diff --git a/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding.go b/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding.go
index 7d32b50963ba..09e527e03d43 100644
--- a/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding.go
+++ b/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding.go
@@ -267,7 +267,9 @@ func BuildTxnStatisticsJSON(statistics *roachpb.CollectedTransactionStatistics)
// "properties": {
// "stmtType": { "type": "string" },
// "query": { "type": "string" },
+// "fingerprintID": { "type": "string" },
// "querySummary": { "type": "string" },
+// "formattedQuery": { "type": "string" },
// "implicitTxn": { "type": "boolean" },
// "distSQLCount": { "type": "number" },
// "failedCount": { "type": "number" },
@@ -280,6 +282,12 @@ func BuildTxnStatisticsJSON(statistics *roachpb.CollectedTransactionStatistics)
// "type": "string"
// }
// },
+// "appNames": {
+// "type": "array",
+// "items": {
+// "type": "string"
+// }
+// },
// }
// }
func BuildStmtDetailsMetadataJSON(
diff --git a/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding_test.go b/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding_test.go
index acb5d2a895e5..d9e83f9c9248 100644
--- a/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding_test.go
+++ b/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_encoding_test.go
@@ -395,7 +395,8 @@ func TestSQLStatsJsonEncoding(t *testing.T) {
"fullScanCount": {{.Int64}},
"totalCount": {{.Int64}},
"db": [{{joinStrings .StringArray}}],
- "appNames": [{{joinStrings .StringArray}}]
+ "appNames": [{{joinStrings .StringArray}}],
+ "fingerprintID": "{{.String}}"
}
`
expectedAggregatedMetadataStr := fillTemplate(t, expectedAggregatedMetadataStrTemplate, data)
diff --git a/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_impl.go b/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_impl.go
index f68a806b1998..dc8d163bde22 100644
--- a/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_impl.go
+++ b/pkg/sql/sqlstats/persistedsqlstats/sqlstatsutil/json_impl.go
@@ -120,6 +120,7 @@ func (s *aggregatedMetadata) jsonFields() jsonFields {
{"stmtType", (*jsonString)(&s.StmtType)},
{"vecCount", (*jsonInt)(&s.VecCount)},
{"totalCount", (*jsonInt)(&s.TotalCount)},
+ {"fingerprintID", (*jsonString)(&s.FingerprintID)},
}
}
diff --git a/pkg/ui/workspaces/cluster-ui/src/api/insightsApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/insightsApi.ts
index 9971dc3a447a..003ce1c3797f 100644
--- a/pkg/ui/workspaces/cluster-ui/src/api/insightsApi.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/api/insightsApi.ts
@@ -30,7 +30,7 @@ import {
} from "src/insights";
import moment from "moment";
import { INTERNAL_APP_NAME_PREFIX } from "src/activeExecutions/activeStatementUtils";
-import { CheckHexValue } from "../util";
+import { FixFingerprintHexValue } from "../util";
// Transaction contention insight events.
@@ -87,7 +87,9 @@ function formatTxnContentionResults(
return response.execution.txn_results[0].rows.map(row => ({
transactionID: row.waiting_txn_id,
- transactionFingerprintID: CheckHexValue(row.waiting_txn_fingerprint_id),
+ transactionFingerprintID: FixFingerprintHexValue(
+ row.waiting_txn_fingerprint_id,
+ ),
startTime: moment(row.collection_ts).utc(),
contentionDuration: moment.duration(row.contention_duration),
contentionThreshold: moment.duration(row.threshold).asMilliseconds(),
@@ -134,7 +136,9 @@ function formatTxnFingerprintsResults(
}
return response.execution.txn_results[0].rows.map(row => ({
- transactionFingerprintID: CheckHexValue(row.transaction_fingerprint_id),
+ transactionFingerprintID: FixFingerprintHexValue(
+ row.transaction_fingerprint_id,
+ ),
queryIDs: row.query_ids,
application: row.app_name,
}));
@@ -169,7 +173,10 @@ function createStmtFingerprintToQueryMap(
return idToQuery;
}
response.execution.txn_results[0].rows.forEach(row => {
- idToQuery.set(CheckHexValue(row.statement_fingerprint_id), row.query);
+ idToQuery.set(
+ FixFingerprintHexValue(row.statement_fingerprint_id),
+ row.query,
+ );
});
return idToQuery;
@@ -364,7 +371,7 @@ function formatTxnContentionDetailsResponse(
totalContentionTime += contentionTimeInMs;
blockingContentionDetails[idx] = {
blockingExecutionID: value.blocking_txn_id,
- blockingTxnFingerprintID: CheckHexValue(
+ blockingTxnFingerprintID: FixFingerprintHexValue(
value.blocking_txn_fingerprint_id,
),
blockingQueries: null,
@@ -385,7 +392,9 @@ function formatTxnContentionDetailsResponse(
const contentionThreshold = moment.duration(row.threshold).asMilliseconds();
return {
transactionExecutionID: row.waiting_txn_id,
- transactionFingerprintID: CheckHexValue(row.waiting_txn_fingerprint_id),
+ transactionFingerprintID: FixFingerprintHexValue(
+ row.waiting_txn_fingerprint_id,
+ ),
startTime: moment(row.collection_ts).utc(),
totalContentionTimeMs: totalContentionTime,
blockingContentionDetails: blockingContentionDetails,
@@ -551,7 +560,9 @@ function organizeExecutionInsightsResponseIntoTxns(
if (!txnInsight) {
txnInsight = {
transactionExecutionID: row.txn_id,
- transactionFingerprintID: CheckHexValue(row.txn_fingerprint_id),
+ transactionFingerprintID: FixFingerprintHexValue(
+ row.txn_fingerprint_id,
+ ),
implicitTxn: row.implicit_txn,
databaseName: row.database_name,
application: row.app_name,
@@ -575,7 +586,7 @@ function organizeExecutionInsightsResponseIntoTxns(
endTime: end,
elapsedTimeMillis: end.diff(start, "milliseconds"),
statementExecutionID: row.stmt_id,
- statementFingerprintID: CheckHexValue(row.stmt_fingerprint_id),
+ statementFingerprintID: FixFingerprintHexValue(row.stmt_fingerprint_id),
isFullScan: row.full_scan,
rowsRead: row.rows_read,
rowsWritten: row.rows_written,
diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx
index cdfbb98ffe0c..081a61ada4d2 100644
--- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx
@@ -30,6 +30,7 @@ import { AlignedData, Options } from "uplot";
import {
appAttr,
appNamesAttr,
+ FixFingerprintHexValue,
DATE_FORMAT_24_UTC,
intersperse,
queryByName,
@@ -72,7 +73,6 @@ import {
import { Delayed } from "../delayed";
import moment from "moment";
import {
- CancelStmtDiagnosticRequest,
InsertStmtDiagnosticRequest,
StatementDiagnosticsReport,
} from "../api";
@@ -514,6 +514,7 @@ export class StatementDetails extends React.Component<
const {
app_names,
databases,
+ fingerprint_id,
failed_count,
full_scan_count,
vec_count,
@@ -651,6 +652,10 @@ export class StatementDetails extends React.Component<
", ",
)}
/>
+
- CheckHexValue(item.stats_data?.transaction_fingerprint_id.toString(16)), + FixFingerprintHexValue( + item.stats_data?.transaction_fingerprint_id.toString(16), + ), sort: (item: TransactionInfo) => item.stats_data?.transaction_fingerprint_id.toString(16), showByDefault: false, diff --git a/pkg/ui/workspaces/cluster-ui/src/util/format.spec.ts b/pkg/ui/workspaces/cluster-ui/src/util/format.spec.ts index 818ac7374db7..515f1992dd81 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/format.spec.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/format.spec.ts @@ -15,7 +15,7 @@ import { BytesFitScale, byteUnits, HexStringToInt64String, - CheckHexValue, + FixFingerprintHexValue, } from "./format"; describe("Format utils", () => { @@ -61,13 +61,17 @@ describe("Format utils", () => { }); }); - describe("CheckHexValue", () => { + describe("FixFingerprintHexValue", () => { it("add leading 0 to hex values", () => { - expect(CheckHexValue(undefined)).toBe(""); - expect(CheckHexValue(null)).toBe(""); - expect(CheckHexValue("fb9111f22f2213b7")).toBe("fb9111f22f2213b7"); - expect(CheckHexValue("b9111f22f2213b7")).toBe("0b9111f22f2213b7"); - expect(CheckHexValue("9111f22f2213b7")).toBe("009111f22f2213b7"); + expect(FixFingerprintHexValue(undefined)).toBe(""); + expect(FixFingerprintHexValue(null)).toBe(""); + expect(FixFingerprintHexValue("fb9111f22f2213b7")).toBe( + "fb9111f22f2213b7", + ); + expect(FixFingerprintHexValue("b9111f22f2213b7")).toBe( + "0b9111f22f2213b7", + ); + expect(FixFingerprintHexValue("9111f22f2213b7")).toBe("009111f22f2213b7"); }); }); }); diff --git a/pkg/ui/workspaces/cluster-ui/src/util/format.ts b/pkg/ui/workspaces/cluster-ui/src/util/format.ts index 60c1ddfbd913..11a4ed2a884e 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/format.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/format.ts @@ -256,15 +256,15 @@ export function HexStringToInt64String(s: string): string { return dec; } -// CheckHexValue adds the leading 0 on strings with hex value that -// have a length < 16. -export function CheckHexValue(s: string): string { +// FixFingerprintHexValue adds the leading 0 on strings with hex value that +// have a length < 16. This can occur because it was returned like this from the DB +// or because the hex value was generated using `.toString(16)` (which removes the +// leading zeros). +// The zeros need to be added back to match the value on our sql stats tables. +export function FixFingerprintHexValue(s: string): string { if (s === undefined || s === null || s.length === 0) { return ""; } - if (s?.length === 16) { - return s; - } while (s.length < 16) { s = `0${s}`; } diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx index b513716c150b..c3a2cf3f7b3d 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx @@ -142,7 +142,7 @@ export const selectStatements = createSelector( if (!(key in statsByStatementKey)) { statsByStatementKey[key] = { statementFingerprintID: stmt.statement_fingerprint_id?.toString(), - statementFingerprintHexID: util.CheckHexValue( + statementFingerprintHexID: util.FixFingerprintHexValue( stmt.statement_fingerprint_id?.toString(16), ), statement: stmt.statement, @@ -163,7 +163,7 @@ export const selectStatements = createSelector( const stmt = statsByStatementKey[key]; return { aggregatedFingerprintID: stmt.statementFingerprintID, - aggregatedFingerprintHexID: util.CheckHexValue( + aggregatedFingerprintHexID: util.FixFingerprintHexValue( stmt.statementFingerprintHexID, ), label: stmt.statement,