diff --git a/pkg/ui/workspaces/cluster-ui/src/api/statementDiagnosticsApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/statementDiagnosticsApi.ts index 7eb8527576b3..e9d36e87c4e2 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/statementDiagnosticsApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/statementDiagnosticsApi.ts @@ -74,6 +74,7 @@ export type InsertStmtDiagnosticRequest = { samplingProbability?: number; minExecutionLatencySeconds?: number; expiresAfterSeconds?: number; + planGist: string; }; export type InsertStmtDiagnosticResponse = { @@ -85,8 +86,15 @@ export function createStatementDiagnosticsReport({ samplingProbability, minExecutionLatencySeconds, expiresAfterSeconds, + planGist, }: InsertStmtDiagnosticRequest): Promise { const args: any = [stmtFingerprint]; + let query = `SELECT crdb_internal.request_statement_bundle($1, $2, $3::INTERVAL, $4::INTERVAL) as req_resp`; + + if (planGist) { + args.push(planGist); + query = `SELECT crdb_internal.request_statement_bundle($1, $2, $3, $4::INTERVAL, $5::INTERVAL) as req_resp`; + } if (samplingProbability) { args.push(samplingProbability); @@ -105,7 +113,7 @@ export function createStatementDiagnosticsReport({ } const createStmtDiag = { - sql: `SELECT crdb_internal.request_statement_bundle($1, $2, $3::INTERVAL, $4::INTERVAL) as req_resp`, + sql: query, arguments: args, }; diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.spec.tsx index 72a971c09427..77b127b3c215 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.spec.tsx @@ -63,6 +63,7 @@ describe("DiagnosticsView", () => { requestTime={undefined} currentScale={ts} onChangeTimeScale={mockSetTimeScale} + planGists={["gist"]} /> , ); @@ -73,6 +74,7 @@ describe("DiagnosticsView", () => { activateButtonComponent.simulate("click"); expect(activateDiagnosticsRef.current.showModalFor).toBeCalledWith( statementFingerprint, + ["gist"], ); }); }); @@ -94,6 +96,7 @@ describe("DiagnosticsView", () => { dismissAlertMessage={() => {}} currentScale={ts} onChangeTimeScale={mockSetTimeScale} + planGists={["gist"]} /> , ); @@ -110,6 +113,7 @@ describe("DiagnosticsView", () => { activateButtonComponent.simulate("click"); expect(activateDiagnosticsRef.current.showModalFor).toBeCalledWith( statementFingerprint, + ["gist"], ); }); @@ -128,6 +132,7 @@ describe("DiagnosticsView", () => { currentScale={ts} requestTime={requestTime} onChangeTimeScale={mockSetTimeScale} + planGists={["gist"]} /> , ); @@ -152,6 +157,7 @@ describe("DiagnosticsView", () => { currentScale={ts} requestTime={requestTime} onChangeTimeScale={mockSetTimeScale} + planGists={["gist"]} /> , ); diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx index 00efa19cbba5..ab9100bbc3dd 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/diagnostics/diagnosticsView.tsx @@ -60,6 +60,7 @@ export interface DiagnosticsViewDispatchProps { export interface DiagnosticsViewOwnProps { statementFingerprint?: string; + planGists?: string[]; } export type DiagnosticsViewProps = DiagnosticsViewOwnProps & @@ -80,6 +81,7 @@ const NavButton: React.FC = props => ( export const EmptyDiagnosticsView = ({ statementFingerprint, + planGists, showDiagnosticsViewLink, activateDiagnosticsRef, }: DiagnosticsViewProps): React.ReactElement => { @@ -94,6 +96,7 @@ export const EmptyDiagnosticsView = ({ onClick={() => activateDiagnosticsRef?.current?.showModalFor( statementFingerprint, + planGists, ) } > @@ -272,6 +275,7 @@ export class DiagnosticsView extends React.Component< activateDiagnosticsRef, currentScale, onChangeTimeScale, + planGists, } = this.props; const readyToRequestDiagnostics = diagnosticsReports.every( @@ -329,6 +333,7 @@ export class DiagnosticsView extends React.Component< onClick={() => activateDiagnosticsRef?.current?.showModalFor( statementFingerprint, + planGists, ) } disabled={!readyToRequestDiagnostics} diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index 594093832323..ebb475c9eb03 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -1021,6 +1021,7 @@ export class StatementDetails extends React.Component< onSortingChange={this.props.onSortingChange} currentScale={this.props.timeScale} onChangeTimeScale={this.changeTimeScale} + planGists={this.props.statementDetails.statement.stats.plan_gists} /> ); }; diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.scss b/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.scss index 1cf9369c6a49..23b97487b8ba 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.scss +++ b/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.scss @@ -53,6 +53,10 @@ font-weight: $font-weight--light; } + &__plan-gist-group { + margin-bottom: -25px; + } + &__conditional-container { display: flex; flex-direction: column; @@ -73,6 +77,14 @@ margin-bottom: $spacing-smaller; } + &__plan-gist-container { + display: flex; + flex-direction: row; + margin-top: $spacing-smaller; + margin-bottom: $spacing-smaller; + margin-left: $spacing-medium; + } + &__trace-warning { font-family: $font-family--base; font-size: $font-size--small; @@ -127,6 +139,15 @@ height: 40px; line-height: 40px; + .ant-select-selection__rendered { + margin-top: 0; + } + } + &__plan-gist { + width: 400px; + height: 40px; + line-height: 40px; + .ant-select-selection__rendered { margin-top: 0; } @@ -144,3 +165,7 @@ white-space: normal; } } + +.margin-bottom { + margin-bottom: 10px; +} diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.tsx index b70de9544f92..c7346d7e36a8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsDiagnostics/activateStatementDiagnosticsModal.tsx @@ -31,11 +31,11 @@ const { Option } = Select; export interface ActivateDiagnosticsModalProps { activate: (insertStmtDiagnosticsRequest: InsertStmtDiagnosticRequest) => void; refreshDiagnosticsReports: () => void; - onOpenModal?: (statement: string) => void; + onOpenModal?: (statement: string, planGists: string[]) => void; } export interface ActivateDiagnosticsModalRef { - showModalFor: (statement: string) => void; + showModalFor: (statement: string, planGists: string[]) => void; } export const ActivateStatementDiagnosticsModal = React.forwardRef( @@ -45,7 +45,10 @@ export const ActivateStatementDiagnosticsModal = React.forwardRef( ) => { const [visible, setVisible] = useState(false); const [statement, setStatement] = useState(); + const [planGists, setPlanGists] = useState(); const [conditional, setConditional] = useState(true); + const [filterPerPlanGist, setFilterPerPlanGist] = useState(false); + const [selectedPlanGist, setSelectedPlanGist] = useState(""); const [expires, setExpires] = useState(true); const [minExecLatency, setMinExecLatency] = useState(100); const [minExecLatencyUnit, setMinExecLatencyUnit] = @@ -84,6 +87,7 @@ export const ActivateStatementDiagnosticsModal = React.forwardRef( const onOkHandler = useCallback(() => { activate({ stmtFingerprint: statement, + planGist: filterPerPlanGist ? selectedPlanGist : null, minExecutionLatencySeconds: getMinExecLatency( conditional, minExecLatency, @@ -102,20 +106,30 @@ export const ActivateStatementDiagnosticsModal = React.forwardRef( expires, expiresAfter, traceSampleRate, + filterPerPlanGist, + selectedPlanGist, ]); const onCancelHandler = useCallback(() => setVisible(false), []); useImperativeHandle(ref, () => { return { - showModalFor: (forwardStatement: string) => { + showModalFor: ( + forwardStatement: string, + forwardPlanGists: string[], + ) => { setStatement(forwardStatement); + setPlanGists(forwardPlanGists); setVisible(true); - onOpenModal && onOpenModal(forwardStatement); + onOpenModal && onOpenModal(forwardStatement, forwardPlanGists); }, }; }); + if (planGists && selectedPlanGist == "") { + setSelectedPlanGist(planGists[0]); + } + return ( + + + + setFilterPerPlanGist(false)} + > + For all plan gists + +
+ setFilterPerPlanGist(true)} + > + For the following plan gist: +
+ +
+
+
+
+ setExpires(!expires)}>
Diagnostics request expires after: diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTableContent.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTableContent.tsx index 2445361b0063..c302fecc0fb5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTableContent.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsTable/statementsTableContent.tsx @@ -95,7 +95,10 @@ export const StatementTableCell = { {canActivateDiagnosticReport ? (