Skip to content

Commit a6bd588

Browse files
authored
Merge pull request #154472 from dhartunian/blathers/backport-release-25.4-154206
release-25.4: ui: add transaction diagnostics api client and page
2 parents da44d30 + 79f01be commit a6bd588

File tree

14 files changed

+1383
-148
lines changed

14 files changed

+1383
-148
lines changed

pkg/ui/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ui/workspaces/cluster-ui/src/api/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
export * from "./fetchData";
77
export * from "./statementDiagnosticsApi";
8+
export * from "./transactionDiagnosticsApi";
89
export * from "./statementsApi";
910
export * from "./basePath";
1011
export * from "./nodesApi";
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright 2025 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the CockroachDB Software License
4+
// included in the /LICENSE file.
5+
6+
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
7+
import Long from "long";
8+
import moment from "moment-timezone";
9+
10+
import { fetchData } from "src/api";
11+
12+
import { DurationToMomentDuration, NumberToDuration } from "../util";
13+
14+
const TRANSACTION_DIAGNOSTICS_PATH = "_status/txndiagreports";
15+
const CANCEL_TRANSACTION_DIAGNOSTICS_PATH =
16+
TRANSACTION_DIAGNOSTICS_PATH + "/cancel";
17+
18+
export type TransactionDiagnosticsReport = {
19+
id: string;
20+
transaction_fingerprint: string;
21+
transaction_fingerprint_id: bigint;
22+
statement_fingerprint_ids: Uint8Array[];
23+
completed: boolean;
24+
transaction_diagnostics_id?: string;
25+
requested_at: moment.Moment;
26+
min_execution_latency?: moment.Duration;
27+
expires_at?: moment.Moment;
28+
sampling_probability?: number;
29+
redacted: boolean;
30+
username: string;
31+
};
32+
33+
export type TransactionDiagnosticsResponse = TransactionDiagnosticsReport[];
34+
35+
export async function getTransactionDiagnosticsReports(): Promise<TransactionDiagnosticsResponse> {
36+
const response = await fetchData(
37+
cockroach.server.serverpb.TransactionDiagnosticsReportsResponse,
38+
TRANSACTION_DIAGNOSTICS_PATH,
39+
);
40+
return response.reports.map(report => {
41+
const minExecutionLatency = report.min_execution_latency
42+
? DurationToMomentDuration(report.min_execution_latency)
43+
: null;
44+
let txnId = BigInt(0);
45+
if (
46+
report.transaction_fingerprint_id &&
47+
report.transaction_fingerprint_id.length >= 8
48+
) {
49+
// Note: the probuf code constructs the Uint8Array fingeprint
50+
// using a shared underlying buffer so we need to use its
51+
// specific offset when decoding the Uint64 within.
52+
const dv = new DataView(report.transaction_fingerprint_id.buffer);
53+
txnId = dv.getBigUint64(report.transaction_fingerprint_id.byteOffset);
54+
}
55+
return {
56+
id: report.id.toString(),
57+
transaction_fingerprint: report.transaction_fingerprint,
58+
transaction_fingerprint_id: txnId,
59+
statement_fingerprint_ids: report.statement_fingerprint_ids,
60+
completed: report.completed,
61+
transaction_diagnostics_id: report.transaction_diagnostics_id?.toString(),
62+
requested_at: moment.unix(report.requested_at?.seconds.toNumber()),
63+
min_execution_latency: minExecutionLatency,
64+
expires_at: moment.unix(report.expires_at?.seconds.toNumber()),
65+
sampling_probability: report.sampling_probability,
66+
redacted: report.redacted,
67+
username: report.username,
68+
};
69+
});
70+
}
71+
72+
export type InsertTxnDiagnosticRequest = {
73+
transactionFingerprintId: Uint8Array;
74+
statementFingerprintIds: Uint8Array[];
75+
samplingProbability?: number;
76+
minExecutionLatencySeconds?: number;
77+
expiresAfterSeconds?: number;
78+
redacted: boolean;
79+
};
80+
81+
export type InsertTxnDiagnosticResponse = {
82+
req_resp: boolean;
83+
};
84+
85+
export async function createTransactionDiagnosticsReport(
86+
req: InsertTxnDiagnosticRequest,
87+
): Promise<InsertTxnDiagnosticResponse> {
88+
return fetchData(
89+
cockroach.server.serverpb.CreateTransactionDiagnosticsReportResponse,
90+
TRANSACTION_DIAGNOSTICS_PATH,
91+
cockroach.server.serverpb.CreateTransactionDiagnosticsReportRequest,
92+
new cockroach.server.serverpb.CreateTransactionDiagnosticsReportRequest({
93+
transaction_fingerprint_id: req.transactionFingerprintId,
94+
statement_fingerprint_ids: req.statementFingerprintIds,
95+
sampling_probability: req.samplingProbability,
96+
min_execution_latency: NumberToDuration(req.minExecutionLatencySeconds),
97+
expires_at: NumberToDuration(req.expiresAfterSeconds),
98+
redacted: req.redacted,
99+
}),
100+
).then(response => {
101+
return {
102+
req_resp: response.report !== null,
103+
};
104+
});
105+
}
106+
107+
export type CancelTxnDiagnosticRequest = {
108+
requestId: string;
109+
};
110+
111+
export type CancelTxnDiagnosticResponse = {
112+
txn_diag_req_id: string;
113+
};
114+
115+
export async function cancelTransactionDiagnosticsReport(
116+
req: CancelTxnDiagnosticRequest,
117+
): Promise<CancelTxnDiagnosticResponse> {
118+
return fetchData(
119+
cockroach.server.serverpb.CancelTransactionDiagnosticsReportResponse,
120+
CANCEL_TRANSACTION_DIAGNOSTICS_PATH,
121+
cockroach.server.serverpb.CancelTransactionDiagnosticsReportRequest,
122+
new cockroach.server.serverpb.CancelTransactionDiagnosticsReportRequest({
123+
request_id: Long.fromString(req.requestId),
124+
}),
125+
).then(response => {
126+
if (response.error) {
127+
throw new Error(response.error);
128+
}
129+
return {
130+
txn_diag_req_id: req.requestId,
131+
};
132+
});
133+
}

pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsUtils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import { TimeScale, toDateRange } from "src/timeScaleDropdown";
1010

1111
import { StatementDiagnosticsReport } from "../../api";
1212

13-
export function getDiagnosticsStatus(
14-
diagnosticsRequest: StatementDiagnosticsReport,
15-
): DiagnosticStatuses {
13+
export function getDiagnosticsStatus(diagnosticsRequest: {
14+
completed: boolean;
15+
}): DiagnosticStatuses {
1616
if (diagnosticsRequest.completed) {
1717
return "READY";
1818
}

pkg/ui/workspaces/db-console/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"redux-saga": "^1.1.3",
7373
"redux-thunk": "^2.3.0",
7474
"reselect": "^4.0.0",
75+
"swr": "2.2.5",
7576
"uplot": "^1.6.19",
7677
"whatwg-fetch": "^2.0.3"
7778
},

pkg/ui/workspaces/db-console/src/app.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import { ConnectedDecommissionedNodeHistory } from "src/views/reports";
6666
import Certificates from "src/views/reports/containers/certificates";
6767
import CustomChart from "src/views/reports/containers/customChart";
6868
import Debug from "src/views/reports/containers/debug";
69+
import DiagnosticsHistoryPage from "src/views/reports/containers/diagnosticsHistoryPage";
6970
import EnqueueRange from "src/views/reports/containers/enqueueRange";
7071
import HotRanges from "src/views/reports/containers/hotranges";
7172
import Localities from "src/views/reports/containers/localities";
@@ -75,7 +76,6 @@ import ProblemRanges from "src/views/reports/containers/problemRanges";
7576
import Range from "src/views/reports/containers/range";
7677
import ReduxDebug from "src/views/reports/containers/redux";
7778
import Settings from "src/views/reports/containers/settings";
78-
import StatementsDiagnosticsHistoryView from "src/views/reports/containers/statementDiagnosticsHistory";
7979
import Stores from "src/views/reports/containers/stores";
8080
import ScheduleDetails from "src/views/schedules/scheduleDetails";
8181
import SchedulesPage from "src/views/schedules/schedulesPage";
@@ -467,8 +467,14 @@ export const App: React.FC<AppProps> = (props: AppProps) => {
467467
/>
468468
<Route
469469
exact
470-
path={`/reports/statements/diagnosticshistory`}
471-
component={StatementsDiagnosticsHistoryView}
470+
path={`/reports/diagnosticshistory`}
471+
component={DiagnosticsHistoryPage}
472+
/>
473+
{/* Redirect old statement diagnostics route to new tabbed page */}
474+
<Redirect
475+
exact
476+
from={`/reports/statements/diagnosticshistory`}
477+
to={`/reports/diagnosticshistory?tab=Statements`}
472478
/>
473479
{/* hot ranges */}
474480
<Route

pkg/ui/workspaces/db-console/src/views/reports/containers/debug/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ function StatementDiagnosticsSelector(props: {
229229
return (
230230
canSeeDebugPanelLink && (
231231
<DebugPanelLink
232-
name="Statement Diagnostics History"
233-
url="#/reports/statements/diagnosticshistory"
234-
note="View the history of statement diagnostics requests"
232+
name="Diagnostics History"
233+
url="#/reports/diagnosticshistory"
234+
note="View the history of statement and transaction diagnostics requests"
235235
/>
236236
)
237237
);

0 commit comments

Comments
 (0)