From ac161dd39a2be9c8d623f944cb3504e6a83b3cd3 Mon Sep 17 00:00:00 2001 From: Xin Hao Zhang Date: Mon, 8 Aug 2022 13:33:12 -0400 Subject: [PATCH] cluster-ui: update active execution and sessions details Fixes #85968 Closes #85912 Closes #85973 This commit adds new details to the active execution details pages: full scan (both stmt and txn), priority (txn only), and last retry reason (txn only). New information is also added to the sessions table and details pages: transaction count, active duration, recent txn fingerprint ids (cache size comes from a cluster setting). This commit also fixes a bug in the sessions overview UI where the duration for closed sessions was incorrectly calcualted based on the current time instead of the session end time. Release note (ui change): the following fields have been added to the active stmt/txn details pages: - Full Scan: indicates if the execution contains a full scan - Last Retry Reason (txn page only): the last recorded reason the txn was retried - Priority (txn page only): the txn priority The following fields have been added to the sessions table and page: - Transaction count: the number of txns executed by the session - Session active duration: the time a session spent executing txns - Session most recent fingerprint ids --- .../activeStatementUtils.spec.ts | 4 ++ .../activeExecutions/activeStatementUtils.ts | 5 +++ .../activeTransactionsTable.tsx | 2 - .../cluster-ui/src/activeExecutions/types.ts | 3 ++ .../src/sessions/sessionDetails.module.scss | 10 +++++ .../src/sessions/sessionDetails.tsx | 44 ++++++++++++++++--- .../cluster-ui/src/sessions/sessionsTable.tsx | 29 +++++++++++- .../activeStatementDetails.tsx | 4 ++ .../src/statsTableUtil/statsTableUtil.tsx | 26 +++++++++++ .../activeTransactionDetails.tsx | 18 +++++++- .../workspaces/cluster-ui/src/util/convert.ts | 17 +++++++ 11 files changed, 151 insertions(+), 11 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.spec.ts b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.spec.ts index 92eda07ab3ad..e9009f039488 100644 --- a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.spec.ts +++ b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.spec.ts @@ -57,6 +57,7 @@ const defaultActiveStatement: ActiveStatement = { application: "test", user: "user", clientAddress: "clientAddress", + isFullScan: false, }; // makeActiveStatement creates an ActiveStatement object with the default active statement above @@ -83,6 +84,9 @@ function makeActiveTxn( query: defaultActiveStatement.query, statementID: defaultActiveStatement.statementID, retries: 3, + lastAutoRetryReason: null, + isFullScan: defaultActiveStatement.isFullScan, + priority: "Normal", statementCount: 5, status: "Executing", ...props, diff --git a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.ts b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.ts index c06122a340eb..145ffd4b53a8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.ts +++ b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeStatementUtils.ts @@ -118,6 +118,7 @@ export function getActiveExecutionsFromSessions( application: session.application_name, user: session.username, clientAddress: session.client_address, + isFullScan: query.is_full_scan || false, // Or here is for conversion in case the field is null. }; statements.push(stmt); @@ -138,6 +139,9 @@ export function getActiveExecutionsFromSessions( application: session.application_name, retries: activeTxn.num_auto_retries, statementCount: activeTxn.num_statements_executed, + isFullScan: session.active_queries.some(query => query.is_full_scan), + lastAutoRetryReason: activeTxn.last_auto_retry_reason, + priority: activeTxn.priority, }); }); @@ -147,6 +151,7 @@ export function getActiveExecutionsFromSessions( if (!mostRecentStmt) return txn; txn.query = mostRecentStmt.query; txn.statementID = mostRecentStmt.statementID; + txn.isFullScan = mostRecentStmt.isFullScan; return txn; }); diff --git a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeTransactionsTable/activeTransactionsTable.tsx b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeTransactionsTable/activeTransactionsTable.tsx index 8e7ea74dbdfd..339e806a8ed7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeTransactionsTable/activeTransactionsTable.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/activeTransactionsTable/activeTransactionsTable.tsx @@ -18,14 +18,12 @@ import { SortSetting } from "../../sortedtable"; import { ActiveTransaction, ExecutionType } from "../types"; import { isSelectedColumn } from "../../columnsSelector/utils"; import { Link } from "react-router-dom"; -import { StatusIcon } from "../statusIcon"; import { getLabel, executionsTableTitles, ExecutionsColumn, activeTransactionColumnsFromCommon, } from "../execTableCommon"; -import { DATE_FORMAT, Duration } from "../../util"; interface ActiveTransactionsTable { data: ActiveTransaction[]; diff --git a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/types.ts b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/types.ts index 17a101d53ffd..a575d6548081 100644 --- a/pkg/ui/workspaces/cluster-ui/src/activeExecutions/types.ts +++ b/pkg/ui/workspaces/cluster-ui/src/activeExecutions/types.ts @@ -34,6 +34,7 @@ export interface ActiveExecution { application: string; query?: string; // Possibly empty for a transaction. timeSpentWaiting?: moment.Duration; + isFullScan: boolean; } export type ActiveStatement = ActiveExecution & @@ -45,6 +46,8 @@ export type ActiveStatement = ActiveExecution & export type ActiveTransaction = ActiveExecution & { statementCount: number; retries: number; + lastAutoRetryReason?: string; + priority: string; }; export type ActiveExecutions = { diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.module.scss b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.module.scss index 27b5d85ae197..faff25f38413 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.module.scss +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.module.scss @@ -75,3 +75,13 @@ fill: $colors--neutral-4; } } + +.session-txn-fingerprints { + margin-top: 12px; + max-height: 300px; + overflow-y: scroll; + + div { + margin-bottom: 8px; + } +} \ No newline at end of file diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx index 7f46973b3e36..e400ebcaf1dd 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionDetails.tsx @@ -25,7 +25,7 @@ import { import { SummaryCard, SummaryCardItem } from "../summaryCard"; import SQLActivityError from "../sqlActivity/errorComponent"; -import { TimestampToMoment } from "src/util/convert"; +import { DurationToMomentDuration, TimestampToMoment } from "src/util/convert"; import { Bytes, DATE_FORMAT } from "src/util/format"; import { Col, Row } from "antd"; import "antd/lib/col/style"; @@ -41,10 +41,7 @@ import { Button } from "../button"; import { ArrowLeft } from "@cockroachlabs/icons"; import { Text, TextTypes } from "../text"; import { SqlBox } from "src/sql/box"; -import { - NodeLink, - StatementLinkTarget, -} from "src/statementsTable/statementsTableContent"; +import { NodeLink } from "src/statementsTable/statementsTableContent"; import { ICancelQueryRequest, @@ -256,7 +253,7 @@ export class SessionDetails extends React.Component { } let txnInfo = No Active Transaction; - if (session.active_txn) { + if (session.active_txn && session.end == null) { const txn = session.active_txn; const start = TimestampToMoment(txn.start); txnInfo = ( @@ -352,6 +349,20 @@ export class SessionDetails extends React.Component { value={TimestampToMoment(session.start).format(DATE_FORMAT)} className={cx("details-item")} /> + {session.end && ( + + )} + {!isTenant && ( { value={session.username} className={cx("details-item")} /> + @@ -415,6 +431,22 @@ export class SessionDetails extends React.Component { Most Recent Statement {curStmtInfo} +
+ + Most Recent Transaction Fingerprints Executed + + + A list of the most recent transaction fingerprint IDs executed by + this session represented in hexadecimal. + + + {session.txn_fingerprint_ids.map((txnFingerprintID, i) => ( +
{txnFingerprintID.toString(16)}
+ ))} +
+
); }; diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTable.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTable.tsx index 5fb1a236ed8b..ddf169ecfd92 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTable.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTable.tsx @@ -11,7 +11,11 @@ import classNames from "classnames/bind"; import styles from "./sessionsTable.module.scss"; -import { TimestampToMoment } from "src/util/convert"; +import { + DurationToMomentDuration, + DurationToNumber, + TimestampToMoment, +} from "src/util/convert"; import { BytesWithPrecision } from "src/util/format"; import { Link } from "react-router-dom"; import React from "react"; @@ -175,9 +179,23 @@ export function makeSessionsColumns( name: "sessionDuration", title: statisticsTableTitles.sessionDuration(statType), className: cx("cl-table__col-session"), - cell: session => TimestampToMoment(session.session.start).fromNow(true), + cell: session => { + const startMoment = TimestampToMoment(session.session.start); + if (session.session.end != null) { + return TimestampToMoment(session.session.end).from(startMoment, true); + } + return startMoment.fromNow(true); + }, sort: session => TimestampToMoment(session.session.start).valueOf(), }, + { + name: "sessionActiveDuration", + title: statisticsTableTitles.sessionActiveDuration(statType), + className: cx("cl-table__col-session"), + cell: session => + DurationToMomentDuration(session.session.total_active_time).humanize(), + sort: session => DurationToNumber(session.session.total_active_time), + }, { name: "status", title: statisticsTableTitles.status(statType), @@ -202,6 +220,13 @@ export function makeSessionsColumns( ? session.session.active_queries[0].start.seconds : 0, }, + { + name: "sessionTxnCount", + title: statisticsTableTitles.sessionTxnCount(statType), + className: cx("cl-table__col-session"), + cell: session => session.session?.num_txns_executed, + sort: session => session.session?.num_txns_executed, + }, { name: "memUsage", title: statisticsTableTitles.memUsage(statType), diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/activeStatementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/activeStatementDetails.tsx index 7ed99f897d83..f02b8b68a6d9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/activeStatementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/activeStatementDetails.tsx @@ -116,6 +116,10 @@ export const ActiveStatementDetails: React.FC = ({ } /> + diff --git a/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx b/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx index 92621d7ebc4e..ecedab9575e8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statsTableUtil/statsTableUtil.tsx @@ -30,6 +30,8 @@ export type NodeNames = { [nodeId: string]: string }; export const statisticsColumnLabels = { sessionStart: "Session Start Time (UTC)", sessionDuration: "Session Duration", + sessionActiveDuration: "Session Active Duration", + sessionTxnCount: "Transaction Count", mostRecentStatement: "Most Recent Statement", status: "Status", statementStartTime: "Statement Start Time (UTC)", @@ -133,6 +135,30 @@ export const statisticsTableTitles: StatisticTableTitleType = { ); }, + sessionActiveDuration: () => { + return ( + + {getLabel("sessionActiveDuration")} + + ); + }, + sessionTxnCount: () => { + return ( + + {getLabel("sessionTxnCount")} + + ); + }, status: () => { return ( } /> + + @@ -145,6 +157,10 @@ export const ActiveTransactionDetails: React.FC< label={ActiveTxnInsightsLabels.RETRY_COUNT} value={transaction.retries} /> +