From 3124c0a52842d0219d7b4f9df268f974c01aa67b Mon Sep 17 00:00:00 2001 From: Andrii Vorobiov Date: Mon, 14 Dec 2020 12:15:16 +0200 Subject: [PATCH] ui: re-import statement details page Statement details and Diagnostics view pages are moved out to `admin-ui-components` package and now they are imported back as a reusable components. In addition to changed imports, StatementDetails component now exposes additional props for Diagnostics view tab. Before, StatementDetails component used Diagnostics view connected to store and now it uses pure component. This change were made to provide single point of integration to redux store (the same way as other components connected). Release note: None --- pkg/ui/package.json | 2 +- pkg/ui/src/app.spec.tsx | 6 +- pkg/ui/src/redux/analyticsActions.ts | 22 + pkg/ui/src/redux/analyticsSagas.ts | 24 + .../statements/statementDetails.fixture.ts | 168 ---- .../statements/statementDetails.module.styl | 118 --- .../statements/statementDetails.stories.tsx | 60 -- .../src/views/statements/statementDetails.tsx | 849 +----------------- .../src/views/statements/statementsPage.tsx | 8 +- pkg/ui/yarn-vendor | 2 +- pkg/ui/yarn.lock | 96 +- 11 files changed, 163 insertions(+), 1192 deletions(-) delete mode 100644 pkg/ui/src/views/statements/statementDetails.fixture.ts delete mode 100644 pkg/ui/src/views/statements/statementDetails.module.styl delete mode 100644 pkg/ui/src/views/statements/statementDetails.stories.tsx diff --git a/pkg/ui/package.json b/pkg/ui/package.json index e1fb73a0ded0..1f5d542014bb 100644 --- a/pkg/ui/package.json +++ b/pkg/ui/package.json @@ -15,7 +15,7 @@ "cypress:update-snapshots": "yarn cypress run --env updateSnapshots=true --spec 'cypress/integration/**/*.visual.spec.ts'" }, "dependencies": { - "@cockroachlabs/admin-ui-components": "^0.1.28", + "@cockroachlabs/admin-ui-components": "^0.1.37", "analytics-node": "^3.4.0-beta.1", "antd": "^3.25.2", "babel-polyfill": "^6.26.0", diff --git a/pkg/ui/src/app.spec.tsx b/pkg/ui/src/app.spec.tsx index 7be5890f4df2..06f7653abcbb 100644 --- a/pkg/ui/src/app.spec.tsx +++ b/pkg/ui/src/app.spec.tsx @@ -32,8 +32,10 @@ import { } from "src/views/databases/containers/databases"; import { TableMain } from "src/views/databases/containers/tableDetails"; import { DataDistributionPage } from "src/views/cluster/containers/dataDistribution"; -import { StatementsPage } from "@cockroachlabs/admin-ui-components"; -import { StatementDetails } from "src/views/statements/statementDetails"; +import { + StatementsPage, + StatementDetails, +} from "@cockroachlabs/admin-ui-components"; import Debug from "src/views/reports/containers/debug"; import { ReduxDebug } from "src/views/reports/containers/redux"; import { CustomChart } from "src/views/reports/containers/customChart"; diff --git a/pkg/ui/src/redux/analyticsActions.ts b/pkg/ui/src/redux/analyticsActions.ts index e322cf94ce04..397bb62ba547 100644 --- a/pkg/ui/src/redux/analyticsActions.ts +++ b/pkg/ui/src/redux/analyticsActions.ts @@ -15,6 +15,10 @@ export const TRACK_STATEMENTS_SEARCH = export const TRACK_STATEMENTS_PAGINATION = "cockroachui/analytics/TRACK_STATEMENTS_PAGINATION"; export const TRACK_TABLE_SORT = "cockroachui/analytics/TRACK_TABLE_SORT"; +export const TRACK_DOWNLOAD_DIAGNOSTIC_BUNDLE = + "cockroachui/analytics/TRACK_DOWNLOAD_DIAGNOSTIC_BUNDLE"; +export const TRACK_STATEMENT_DETAILS_SUBNAV_SELECTION = + "cockroachui/analytics/TRACK_STATEMENT_DETAILS_SUBNAV_SELECTION"; export interface TableSortActionPayload { tableName: string; @@ -54,3 +58,21 @@ export function trackTableSortAction( }, }; } + +export function trackDownloadDiagnosticsBundleAction( + statementFingerprint: string, +): PayloadAction { + return { + type: TRACK_DOWNLOAD_DIAGNOSTIC_BUNDLE, + payload: statementFingerprint, + }; +} + +export function trackStatementDetailsSubnavSelectionAction( + tabName: string, +): PayloadAction { + return { + type: TRACK_STATEMENT_DETAILS_SUBNAV_SELECTION, + payload: tabName, + }; +} diff --git a/pkg/ui/src/redux/analyticsSagas.ts b/pkg/ui/src/redux/analyticsSagas.ts index 04c2d9a03a63..322b78846eec 100644 --- a/pkg/ui/src/redux/analyticsSagas.ts +++ b/pkg/ui/src/redux/analyticsSagas.ts @@ -21,12 +21,16 @@ import { trackPaginate, trackSearch, trackTableSort, + trackDownloadDiagnosticsBundle, + trackSubnavSelection, } from "src/util/analytics"; import { TRACK_STATEMENTS_SEARCH, TRACK_STATEMENTS_PAGINATION, TRACK_TABLE_SORT, TableSortActionPayload, + TRACK_DOWNLOAD_DIAGNOSTIC_BUNDLE, + TRACK_STATEMENT_DETAILS_SUBNAV_SELECTION, } from "./analyticsActions"; export function* trackActivateStatementsDiagnostics( @@ -58,6 +62,18 @@ export function* trackTableSortChange( yield call(trackTableSort, tableName, columnName, ascending); } +export function* trackDownloadDiagnosticBundleSaga( + action: PayloadAction, +) { + yield call(trackDownloadDiagnosticsBundle, action.payload); +} + +export function* trackStatementDetailsSubnavSelectionSaga( + action: PayloadAction, +) { + yield call(trackSubnavSelection, action.payload); +} + export function* analyticsSaga() { yield all([ takeEvery( @@ -68,5 +84,13 @@ export function* analyticsSaga() { takeEvery(TRACK_STATEMENTS_SEARCH, trackStatementsSearch), takeEvery(TRACK_STATEMENTS_PAGINATION, trackStatementsPagination), takeEvery(TRACK_TABLE_SORT, trackTableSortChange), + takeEvery( + TRACK_DOWNLOAD_DIAGNOSTIC_BUNDLE, + trackDownloadDiagnosticBundleSaga, + ), + takeEvery( + TRACK_STATEMENT_DETAILS_SUBNAV_SELECTION, + trackStatementDetailsSubnavSelectionSaga, + ), ]); } diff --git a/pkg/ui/src/views/statements/statementDetails.fixture.ts b/pkg/ui/src/views/statements/statementDetails.fixture.ts deleted file mode 100644 index 0f73b81426d5..000000000000 --- a/pkg/ui/src/views/statements/statementDetails.fixture.ts +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import Long from "long"; -import { createMemoryHistory } from "history"; -import { StatementDetailsProps } from "src/views/statements/statementDetails"; -import { - refreshStatementDiagnosticsRequests, - refreshStatements, -} from "oss/src/redux/apiReducers"; - -const history = createMemoryHistory({ initialEntries: ["/statements"] }); - -const statementStats: any = { - count: Long.fromNumber(36958), - first_attempt_count: Long.fromNumber(36958), - max_retries: Long.fromNumber(0), - num_rows: { - mean: 11.651577466313078, - squared_diffs: 1493154.3630337175, - }, - parse_lat: { - mean: 0, - squared_diffs: 0, - }, - plan_lat: { - mean: 0.00022804377942529385, - squared_diffs: 0.0030062544511648935, - }, - run_lat: { - mean: 0.00098355830943233, - squared_diffs: 0.04090499253784317, - }, - service_lat: { - mean: 0.0013101634016992284, - squared_diffs: 0.055668241814216965, - }, - overhead_lat: { - mean: 0.00009856131284160407, - squared_diffs: 0.0017520019405651047, - }, - bytes_read: { - mean: 4160407, - squared_diffs: 47880000000000, - }, - rows_read: { - mean: 7, - squared_diffs: 1000000, - }, - sensitive_info: { - last_err: "", - most_recent_plan_description: { - name: "render", - attrs: [ - { - key: "render", - value: "city", - }, - { - key: "render", - value: "id", - }, - ], - children: [ - { - name: "scan", - attrs: [ - { - key: "table", - value: "vehicles@vehicles_auto_index_fk_city_ref_users", - }, - { - key: "spans", - value: "1 span", - }, - ], - children: [], - }, - ], - }, - }, -}; - -export const statementDetailsPropsFixture: StatementDetailsProps = { - history, - location: { - pathname: - "/statement/true/SELECT city%2C id FROM vehicles WHERE city %3D %241", - search: "", - hash: "", - state: null, - }, - match: { - path: "/statement/:implicitTxn/:statement", - url: "/statement/true/SELECT city%2C id FROM vehicles WHERE city %3D %241", - isExact: true, - params: { - implicitTxn: "true", - statement: "SELECT city%2C id FROM vehicles WHERE city %3D %241", - }, - }, - statement: { - statement: "SELECT city, id FROM vehicles WHERE city = $1", - stats: statementStats, - byNode: [ - { - label: "4", - implicitTxn: true, - stats: statementStats, - }, - { - label: "3", - implicitTxn: true, - stats: statementStats, - }, - { - label: "2", - implicitTxn: true, - stats: statementStats, - }, - { - label: "1", - implicitTxn: true, - stats: statementStats, - }, - ], - app: ["movr"], - distSQL: { - numerator: 0, - denominator: 36958, - }, - vec: { - numerator: 36958, - denominator: 36958, - }, - opt: { - numerator: 36958, - denominator: 36958, - }, - implicit_txn: { - numerator: 36958, - denominator: 36958, - }, - failed: { - numerator: 0, - denominator: 36958, - }, - node_id: [4, 3, 2, 1], - }, - statementsError: null, - nodeNames: { - "1": "127.0.0.1:55529 (n1)", - "2": "127.0.0.1:55532 (n2)", - "3": "127.0.0.1:55538 (n3)", - "4": "127.0.0.1:55546 (n4)", - }, - diagnosticsCount: 1, - nodesSummary: undefined, - refreshStatements: (() => {}) as typeof refreshStatements, - refreshStatementDiagnosticsRequests: (() => {}) as typeof refreshStatementDiagnosticsRequests, -}; diff --git a/pkg/ui/src/views/statements/statementDetails.module.styl b/pkg/ui/src/views/statements/statementDetails.module.styl deleted file mode 100644 index cbddc585bbcd..000000000000 --- a/pkg/ui/src/views/statements/statementDetails.module.styl +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -@require '~styl/base/palette.styl' -@require '~src/views/shared/util/table.styl' -@require '~src/components/core/index' - -.app-name - white-space nowrap - - &__unset - color $tooltip-color - font-style italic - -.section - flex 0 0 auto - padding 12px 24px - max-width $max-window-width - clearfix() - - &--heading - padding-top 0 - padding-bottom 0 - - &--container - padding 0 24px 0 0 - -.page--header - padding 0 - &__title - font-family $font-family--base - font-size 20px - line-height 1.6 - letter-spacing -0.2px - color $colors--neutral-8 - margin-bottom 25px - -.base-heading - composes base-heading from '~styl/base/typography.styl' - -.back-link - text-decoration none - color $link-color - -.cockroach--tabs - :global(.ant-tabs-bar) - border-bottom 1px solid $grey2 - :global(.ant-tabs-tab) - font-family SourceSansPro-Regular - font-size 16px - line-height 1.5 - letter-spacing normal - color $placeholder - &:hover - color $adminui-grey-1 - :global(.ant-tabs-tab-active) - color $adminui-grey-1 - :global(.ant-tabs-ink-bar) - height 3px - border-radius 40px - background-color $blue - -@require "~src/components/core/index.styl" - -.table-details - .summary--card__counting - margin-bottom 15px - &--value - margin 0 - color $colors--neutral-8 - line-height 32px - &--label - font-size 14px - line-height 22px - letter-spacing 0.1px - -.last-cleared-tooltip, .numeric-stats-table, .plan-view-table - &__tooltip - width 36px // Reserve space for 10px padding around centered 16px icon - height 16px - display inline-block - - // Overrides to let the tooltip sit inside a table header. - text-transform none - font-weight normal - white-space normal - letter-spacing normal - font-size 14px - - &__tooltip-hover-area - width 100% - padding 0px 10px - - &__info-icon - width 16px - height 16px - border-radius 50% - border 1px solid $tooltip-color - font-size 12px - line-height 14px // Visual tweak to vertically center the "i" - text-align center - color $tooltip-color - - .hover-tooltip--hovered &__info-icon - border-color $body-color - color $body-color - -.statements-table - &__col-query-text - font-family $font-family--monospace - white-space pre-wrap diff --git a/pkg/ui/src/views/statements/statementDetails.stories.tsx b/pkg/ui/src/views/statements/statementDetails.stories.tsx deleted file mode 100644 index b268c1c4a1a0..000000000000 --- a/pkg/ui/src/views/statements/statementDetails.stories.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import { storiesOf } from "@storybook/react"; -import { Location } from "history"; - -import { - withBackgroundFactory, - withRouterProvider, -} from ".storybook/decorators"; -import { StatementDetails } from "./statementDetails"; -import { statementDetailsPropsFixture } from "./statementDetails.fixture"; - -// (koorosh) Note: Diagnostics tab isn't added here because it is independent -// connected view and has to be managed as separate story. - -storiesOf("StatementDetails", module) - .addDecorator(withRouterProvider) - .addDecorator(withBackgroundFactory()) - .add("Overview tab", () => ( - - )) - .add("Logical Plan tab", () => { - const location: Location = { - ...statementDetailsPropsFixture.history.location, - search: new URLSearchParams([["tab", "logical-plan"]]).toString(), - }; - return ( - - ); - }) - .add("Execution Stats tab", () => { - const location: Location = { - ...statementDetailsPropsFixture.history.location, - search: new URLSearchParams([["tab", "execution-stats"]]).toString(), - }; - return ( - - ); - }); diff --git a/pkg/ui/src/views/statements/statementDetails.tsx b/pkg/ui/src/views/statements/statementDetails.tsx index 19850f422116..db90c64031ab 100644 --- a/pkg/ui/src/views/statements/statementDetails.tsx +++ b/pkg/ui/src/views/statements/statementDetails.tsx @@ -7,839 +7,50 @@ // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. - -import { Col, Row, Tabs } from "antd"; -import _ from "lodash"; -import React, { ReactNode } from "react"; -import { Helmet } from "react-helmet"; import { connect } from "react-redux"; import { - Link, RouteComponentProps, match as Match, withRouter, } from "react-router-dom"; import { createSelector } from "reselect"; +import _ from "lodash"; import { refreshStatementDiagnosticsRequests, refreshStatements, } from "src/redux/apiReducers"; -import { nodeDisplayNameByIDSelector, NodesSummary } from "src/redux/nodes"; +import { nodeDisplayNameByIDSelector } from "src/redux/nodes"; import { AdminUIState } from "src/redux/state"; import { combineStatementStats, ExecutionStatistics, flattenStatementStats, - NumericStat, StatementStatistics, - stdDev, } from "src/util/appStats"; import { appAttr, implicitTxnAttr, statementAttr } from "src/util/constants"; import { FixLong } from "src/util/fixLong"; -import { Bytes, Duration } from "src/util/format"; -import { intersperse } from "src/util/intersperse"; -import { Pick } from "src/util/pick"; -import { Loading } from "@cockroachlabs/admin-ui-components"; -import { SortSetting } from "src/views/shared/components/sortabletable"; -import SqlBox from "src/views/shared/components/sql/box"; -import { formatNumberForDisplay } from "src/views/shared/components/summaryBar"; -import { ToolTipWrapper } from "src/views/shared/components/toolTip"; -import { PlanView } from "src/views/statements/planView"; -import { SummaryCard } from "../shared/components/summaryCard"; +import { AggregateStatistics } from "./statementsTable"; +import { getMatchParamByName } from "src/util/query"; +import { selectDiagnosticsReportsByStatementFingerprint } from "src/redux/statements/statementsSelectors"; import { - approximify, - latencyBreakdown, - genericBarChart, - longToInt, - rowsBreakdown, -} from "./barCharts"; + StatementDetails, + StatementDetailsDispatchProps, + StatementDetailsStateProps, + StatementDetailsProps, +} from "@cockroachlabs/admin-ui-components"; +import { createStatementDiagnosticsReportAction } from "src/redux/statements"; +import { createStatementDiagnosticsAlertLocalSetting } from "src/redux/alerts"; import { - AggregateStatistics, - makeNodesColumns, - StatementsSortedTable, -} from "./statementsTable"; -import { getMatchParamByName } from "src/util/query"; -import DiagnosticsView from "./diagnostics"; -import classNames from "classnames/bind"; -import { selectDiagnosticsReportsCountByStatementFingerprint } from "src/redux/statements/statementsSelectors"; -import { Button } from "@cockroachlabs/admin-ui-components"; -import { ArrowLeft } from "@cockroachlabs/icons"; -import { trackSubnavSelection } from "src/util/analytics"; -import styles from "./statementDetails.module.styl"; -import sortableTableStyles from "src/views/shared/components/sortabletable/sortabletable.module.styl"; -import summaryCardStyles from "src/views/shared/components/summaryCard/summaryCard.module.styl"; -import d3 from "d3"; - -const { TabPane } = Tabs; + trackDownloadDiagnosticsBundleAction, + trackStatementDetailsSubnavSelectionAction, +} from "src/redux/analyticsActions"; interface Fraction { numerator: number; denominator: number; } -interface SingleStatementStatistics { - statement: string; - app: string[]; - distSQL: Fraction; - vec: Fraction; - opt: Fraction; - implicit_txn: Fraction; - failed: Fraction; - node_id: number[]; - stats: StatementStatistics; - byNode: AggregateStatistics[]; -} - -const cx = classNames.bind(styles); -const sortableTableCx = classNames.bind(sortableTableStyles); -const summaryCardStylesCx = classNames.bind(summaryCardStyles); - -function AppLink(props: { app: string }) { - if (!props.app) { - return (unset); - } - - return ( - - {props.app} - - ); -} - -interface StatementDetailsOwnProps { - statement: SingleStatementStatistics; - statementsError: Error | null; - nodeNames: { [nodeId: string]: string }; - refreshStatements: typeof refreshStatements; - refreshStatementDiagnosticsRequests: typeof refreshStatementDiagnosticsRequests; - nodesSummary: NodesSummary; - diagnosticsCount: number; -} - -export type StatementDetailsProps = StatementDetailsOwnProps & - RouteComponentProps; - -interface StatementDetailsState { - sortSetting: SortSetting; - currentTab?: string; -} - -interface NumericStatRow { - name: string; - value: NumericStat; - bar?: () => ReactNode; - summary?: boolean; - // You can override the table's formatter on a per-row basis with this format - // method. - format?: (v: number) => string; -} - -interface NumericStatTableProps { - title?: string; - description?: string; - measure: string; - rows: NumericStatRow[]; - count: number; - format?: (v: number) => string; -} - -class NumericStatTable extends React.Component { - static defaultProps = { - format: (v: number) => `${v}`, - }; - - render() { - const { rows } = this.props; - return ( - - - - - - - - - - {rows.map((row: NumericStatRow) => { - let { format } = this.props; - if (row.format) { - format = row.format; - } - const className = sortableTableCx( - "sort-table__row", - "sort-table__row--body", - { - "sort-table__row--summary": row.summary, - }, - ); - return ( - - - - - - ); - })} - -
- {this.props.title} - - Mean {this.props.measure} - - Standard Deviation -
- {row.name} - - {row.bar ? row.bar() : null} - - {format(stdDev(row.value, this.props.count))} -
- ); - } -} - -export class StatementDetails extends React.Component< - StatementDetailsProps, - StatementDetailsState -> { - constructor(props: StatementDetailsProps) { - super(props); - const searchParams = new URLSearchParams(props.history.location.search); - this.state = { - sortSetting: { - sortKey: 5, // Latency - ascending: false, - }, - currentTab: searchParams.get("tab") || "overview", - }; - } - - changeSortSetting = (ss: SortSetting) => { - this.setState({ - sortSetting: ss, - }); - }; - - componentDidMount() { - this.props.refreshStatements(); - this.props.refreshStatementDiagnosticsRequests(); - } - - componentDidUpdate() { - this.props.refreshStatements(); - this.props.refreshStatementDiagnosticsRequests(); - } - - backToStatementsPage = () => { - const { history, location } = this.props; - history.push({ - ...location, - pathname: "/statements", - }); - }; - - onTabChange = (tabId: string) => { - const { history } = this.props; - const searchParams = new URLSearchParams(history.location.search); - searchParams.set("tab", tabId); - trackSubnavSelection(tabId); - history.replace({ - ...history.location, - search: searchParams.toString(), - }); - this.setState({ - currentTab: tabId, - }); - }; - - render() { - const app = getMatchParamByName(this.props.match, appAttr); - return ( -
- -
- -

- Statement Details -

-
-
- -
-
- ); - } - - renderContent = () => { - const { diagnosticsCount } = this.props; - const { currentTab } = this.state; - - if (!this.props.statement) { - return null; - } - const { - stats, - statement, - app, - distSQL, - vec, - opt, - failed, - implicit_txn, - } = this.props.statement; - - if (!stats) { - const sourceApp = getMatchParamByName(this.props.match, appAttr); - const listUrl = "/statements" + (sourceApp ? "/" + sourceApp : ""); - - return ( - -
- -
-
-

Unable to find statement

- There are no execution statistics for this statement.{" "} - - Back to Statements - -
-
- ); - } - - const count = FixLong(stats.count).toInt(); - - const { rowsBarChart } = rowsBreakdown(this.props.statement); - const { - parseBarChart, - planBarChart, - runBarChart, - overheadBarChart, - overallBarChart, - } = latencyBreakdown(this.props.statement); - - const totalCountBarChart = longToInt(this.props.statement.stats.count); - const firstAttemptsBarChart = longToInt( - this.props.statement.stats.first_attempt_count, - ); - const retriesBarChart = totalCountBarChart - firstAttemptsBarChart; - const maxRetriesBarChart = longToInt( - this.props.statement.stats.max_retries, - ); - - const statsByNode = this.props.statement.byNode; - const logicalPlan = - stats.sensitive_info && stats.sensitive_info.most_recent_plan_description; - const duration = (v: number) => Duration(v * 1e9); - return ( - - - - - - - - - - -
-

- {formatNumberForDisplay( - count * stats.service_lat.mean, - duration, - )} -

-

- Total Time -

-
- - -
-

- {formatNumberForDisplay( - stats.service_lat.mean, - duration, - )} -

-

- Mean Service Latency -

-
- -
-

-

-

- App: -

-

- {intersperse( - app.map((a) => ), - ", ", - )} -

-
-
-

- Transaction Type -

-

- {renderTransactionType(implicit_txn)} -

-
-
-

- Distributed execution? -

-

- {renderBools(distSQL)} -

-
-
-

- Vectorized execution? -

-

- {renderBools(vec)} -

-
-
-

- Used cost-based optimizer? -

-

- {renderBools(opt)} -

-
-
-

- Failed? -

-

- {renderBools(failed)} -

-
-
- -

- Execution Count -

-
-

- First Attempts -

-

- {firstAttemptsBarChart} -

-
-
-

- Retries -

-

0, - }, - )} - > - {retriesBarChart} -

-
-
-

- Max Retries -

-

0, - }, - )} - > - {maxRetriesBarChart} -

-
-
-

- Total -

-

- {totalCountBarChart} -

-
-

-

- Rows Affected -

-
-

- Mean Rows -

-

- {rowsBarChart(true)} -

-
-
-

- Standard Deviation -

-

- {rowsBarChart()} -

-
-
- -
-
- 0 ? `(${diagnosticsCount})` : "" - }`} - key="diagnostics" - > - - - - - - - - - -

- Execution Latency By Phase -
- -
-
- i -
-
-
-
-

- Duration(v * 1e9)} - rows={[ - { name: "Parse", value: stats.parse_lat, bar: parseBarChart }, - { name: "Plan", value: stats.plan_lat, bar: planBarChart }, - { name: "Run", value: stats.run_lat, bar: runBarChart }, - { - name: "Overhead", - value: stats.overhead_lat, - bar: overheadBarChart, - }, - { - name: "Overall", - summary: true, - value: stats.service_lat, - bar: overallBarChart, - }, - ]} - /> -
- -

- Other Execution Statistics -

- -
- -

- Stats By Node -
- -
-
- i -
-
-
-
-

- -
-
-
- ); - }; -} - -function renderTransactionType(implicitTxn: Fraction) { - if (Number.isNaN(implicitTxn.numerator)) { - return "(unknown)"; - } - if (implicitTxn.numerator === 0) { - return "Explicit"; - } - if (implicitTxn.numerator === implicitTxn.denominator) { - return "Implicit"; - } - const fraction = - approximify(implicitTxn.numerator) + - " of " + - approximify(implicitTxn.denominator); - return `${fraction} were Implicit Txns`; -} - -function renderBools(fraction: Fraction) { - if (Number.isNaN(fraction.numerator)) { - return "(unknown)"; - } - if (fraction.numerator === 0) { - return "No"; - } - if (fraction.numerator === fraction.denominator) { - return "Yes"; - } - return ( - approximify(fraction.numerator) + " of " + approximify(fraction.denominator) - ); -} - -type StatementsState = Pick; - interface StatementDetailsData { nodeId: number; implicitTxn: boolean; @@ -925,8 +136,8 @@ function filterByRouterParamsPredicate( } export const selectStatement = createSelector( - (state: StatementsState) => state.cachedData.statements, - (_state: StatementsState, props: RouteComponentProps) => props, + (state: AdminUIState) => state.cachedData.statements, + (_state: AdminUIState, props: RouteComponentProps) => props, (statementsState, props) => { const statements = statementsState.data?.statements; if (!statements) { @@ -956,26 +167,36 @@ export const selectStatement = createSelector( }, ); -const mapStateToProps = (state: AdminUIState, props: StatementDetailsProps) => { +const mapStateToProps = ( + state: AdminUIState, + props: StatementDetailsProps, +): StatementDetailsStateProps => { const statement = selectStatement(state, props); + const statementFingerprint = statement?.statement; return { statement, statementsError: state.cachedData.statements.lastError, nodeNames: nodeDisplayNameByIDSelector(state), - diagnosticsCount: selectDiagnosticsReportsCountByStatementFingerprint( + diagnosticsReports: selectDiagnosticsReportsByStatementFingerprint( state, - statement?.statement, + statementFingerprint, ), }; }; -const mapDispatchToProps = { +const mapDispatchToProps: StatementDetailsDispatchProps = { refreshStatements, refreshStatementDiagnosticsRequests, + dismissStatementDiagnosticsAlertMessage: () => + createStatementDiagnosticsAlertLocalSetting.set({ show: false }), + createStatementDiagnosticsReport: createStatementDiagnosticsReportAction, + onTabChanged: trackStatementDetailsSubnavSelectionAction, + onDiagnosticBundleDownload: trackDownloadDiagnosticsBundleAction, }; -const StatementDetailsConnected = withRouter( - connect(mapStateToProps, mapDispatchToProps)(StatementDetails), +export default withRouter( + connect( + mapStateToProps, + mapDispatchToProps, + )(StatementDetails), ); - -export default StatementDetailsConnected; diff --git a/pkg/ui/src/views/statements/statementsPage.tsx b/pkg/ui/src/views/statements/statementsPage.tsx index 121ce86d9d9e..f8b084ce668b 100644 --- a/pkg/ui/src/views/statements/statementsPage.tsx +++ b/pkg/ui/src/views/statements/statementsPage.tsx @@ -42,11 +42,11 @@ import { createStatementDiagnosticsReportAction, } from "src/redux/statements"; import { + trackDownloadDiagnosticsBundleAction, trackStatementsPaginationAction, trackStatementsSearchAction, trackTableSortAction, } from "src/redux/analyticsActions"; -import { trackDownloadDiagnosticsBundle } from "src/util/analytics"; type ICollectedStatementStatistics = protos.cockroach.server.serverpb.StatementsResponse.ICollectedStatementStatistics; type IStatementDiagnosticsReport = protos.cockroach.server.serverpb.IStatementDiagnosticsReport; @@ -187,7 +187,7 @@ export const selectLastReset = createSelector( }, ); -const StatementsPageConnected = withRouter( +export default withRouter( connect( (state: AdminUIState, props: RouteComponentProps) => ({ statements: selectStatements(state, props), @@ -208,9 +208,7 @@ const StatementsPageConnected = withRouter( onPageChanged: trackStatementsPaginationAction, onSortingChange: trackTableSortAction, onDiagnosticsReportDownload: (report: IStatementDiagnosticsReport) => - trackDownloadDiagnosticsBundle(report.statement_fingerprint), + trackDownloadDiagnosticsBundleAction(report.statement_fingerprint), }, )(StatementsPage), ); - -export default StatementsPageConnected; diff --git a/pkg/ui/yarn-vendor b/pkg/ui/yarn-vendor index d27dd45245a0..e27b89eae0a9 160000 --- a/pkg/ui/yarn-vendor +++ b/pkg/ui/yarn-vendor @@ -1 +1 @@ -Subproject commit d27dd45245a09c58bb7d904bec2eecf921cb94b1 +Subproject commit e27b89eae0a9536fbd32378e06efe9f9a888134f diff --git a/pkg/ui/yarn.lock b/pkg/ui/yarn.lock index 7405dd585dd5..0dfe120bb2d3 100644 --- a/pkg/ui/yarn.lock +++ b/pkg/ui/yarn.lock @@ -1806,14 +1806,14 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@cockroachlabs/admin-ui-components@^0.1.28": - version "0.1.28" - resolved "https://registry.yarnpkg.com/@cockroachlabs/admin-ui-components/-/admin-ui-components-0.1.28.tgz#6dc00be6d4ea41dd9853e2f754c85936f701950c" - integrity sha512-i12d+wtCgxBM25ZpkcLtyuIRlhl/jZreFmJV1SpQYLVLV7OxLTb5WHlUCMMQFC85+UFVPs7evr2l575Ne5AzvA== - dependencies: - "@cockroachlabs/crdb-protobuf-client" "^0.0.3" - "@cockroachlabs/icons" "^0.2.2" - "@cockroachlabs/ui-components" "^0.2.11" +"@cockroachlabs/admin-ui-components@^0.1.37": + version "0.1.37" + resolved "https://registry.yarnpkg.com/@cockroachlabs/admin-ui-components/-/admin-ui-components-0.1.37.tgz#0862de4864777e0ddf1201febba24cfbd7f897f8" + integrity sha512-tvZ0skbITu4uky9xPxrq5kVXbSgLKikOKqUflH05o/Li8wudoJX4Br71S3lwENeTKlOB2FF3YvXwQYtdDhdB9A== + dependencies: + "@cockroachlabs/crdb-protobuf-client" "^0.0.4" + "@cockroachlabs/icons" "0.3.0" + "@cockroachlabs/ui-components" "0.2.14-alpha.0" "@popperjs/core" "^2.4.0" "@reduxjs/toolkit" "^1.5.0" babel-polyfill "^6.26.0" @@ -1822,15 +1822,16 @@ d3-scale "^3.2.3" highlight.js "^10.2.0" long "^4.0.0" + npm-run-all "^4.1.5" react-helmet "^5.2.0" react-popper "^2.2.3" react-select "^1.2.1" reselect "^4.0.0" -"@cockroachlabs/crdb-protobuf-client@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@cockroachlabs/crdb-protobuf-client/-/crdb-protobuf-client-0.0.3.tgz#3ce8dd4953a1209f1895c713cf90595a15c54ab1" - integrity sha512-AXHWWW7hI05hj5fTdXgIIjfZrqfacQ/zsT83LoUsrnFUOeWZCa6qSF3qVonaR2h8FloRfEeFhC+27TDsi8RI0A== +"@cockroachlabs/crdb-protobuf-client@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@cockroachlabs/crdb-protobuf-client/-/crdb-protobuf-client-0.0.4.tgz#9b53ef7cbb187cd7d73b4269c95b0f573caafd45" + integrity sha512-n/SEmLzU7i9h5m8cAw9NPRAcQSgzHNdm5+F0dN3myfQSCuEMTgTlbWsfm6EnjTRL1FnZlieqY2gZzgyx4Ke0Ug== "@cockroachlabs/eslint-config@^0.1.11": version "0.1.11" @@ -1840,20 +1841,15 @@ "@typescript-eslint/parser" "^2.34.0" eslint-config-prettier "^6.11.0" -"@cockroachlabs/icons@^0.2.2": - version "0.2.9" - resolved "https://registry.yarnpkg.com/@cockroachlabs/icons/-/icons-0.2.9.tgz#b626fe409ea49be66b19a9a0358a082d50ade2d0" - integrity sha512-s1kH8sU/DeIGrGUwUMVMAjxGTOn6SHAK8q0tmJY6zcIUaoYb/5UfnQjzdG+ybflgBdyMTB41/bWTXnpsYxxSpw== - -"@cockroachlabs/icons@^0.3.0": +"@cockroachlabs/icons@0.3.0", "@cockroachlabs/icons@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@cockroachlabs/icons/-/icons-0.3.0.tgz#160573074396f266e92fcbe5e520c5ba1d8750f9" integrity sha512-GJxhlXy8Z3/PYFb9C3iM1dvU9wajGoaA/+VCj0an2ipfbkI2fhToq+h0b33vu7JuZ3dS4QMRjfVE4uhlyIUH2Q== -"@cockroachlabs/ui-components@^0.2.11": - version "0.2.12" - resolved "https://registry.yarnpkg.com/@cockroachlabs/ui-components/-/ui-components-0.2.12.tgz#617f97351606026a0a8e5f17730f313b6cc6000c" - integrity sha512-RHRvKAsaUDJpCefA3VqmQSbu1jc1/TM2cEKkhN9G4gg2KgePcaqHupUc4wTD+rw6OqZHwbOHQe9ESXBMhiMVLw== +"@cockroachlabs/ui-components@0.2.14-alpha.0": + version "0.2.14-alpha.0" + resolved "https://registry.yarnpkg.com/@cockroachlabs/ui-components/-/ui-components-0.2.14-alpha.0.tgz#d90a7ce6fbede9d8b177d9a940f24218c262d267" + integrity sha512-8zOZswstmBbJQxXmQ7yfEXEOEUJ4JT5xlzGQ5FD7z8sjy67UDXFoe6FMqFXrkpiFwlCrkK2Vd36LKhLvGY2xkA== dependencies: "@cockroachlabs/icons" "^0.3.0" "@popperjs/core" "^2.4.3" @@ -5121,7 +5117,7 @@ create-react-context@0.3.0, create-react-context@^0.3.0: gud "^1.0.0" warning "^4.0.3" -cross-spawn@6.0.5, cross-spawn@^6.0.0: +cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -8683,6 +8679,16 @@ load-json-file@^1.0.0, load-json-file@^1.1.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + loader-runner@^2.3.1, loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -9025,6 +9031,11 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + merge-deep@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" @@ -9569,6 +9580,21 @@ npm-packlist@^1.1.6: ignore-walk "^3.0.1" npm-bundled "^1.0.1" +npm-run-all@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" + integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== + dependencies: + ansi-styles "^3.2.1" + chalk "^2.4.1" + cross-spawn "^6.0.5" + memorystream "^0.3.1" + minimatch "^3.0.4" + pidtree "^0.3.0" + read-pkg "^3.0.0" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -10212,6 +10238,11 @@ picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +pidtree@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" + integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -11607,6 +11638,15 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -12369,6 +12409,11 @@ shell-quote@1.6.1: array-reduce "~0.0.0" jsonify "~0.0.0" +shell-quote@^1.6.1: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + shelljs@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" @@ -12965,6 +13010,11 @@ strip-bom@^2.0.0: dependencies: is-utf8 "^0.2.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"