Skip to content

Commit

Permalink
ui: add index stats to table details page
Browse files Browse the repository at this point in the history
Resolves cockroachdb#67647, cockroachdb#72842

Previously, there was no way to view and clear index usage stats from
the frontend db console. This commit adds Index Stats tables for each
table on the Table Detail pages, allowing users to view index names,
total reads, and last used statistics. This commit also adds the
functionality of clearing all index stats as a button on the Index Stats
tables.

Release note (ui change): Add index stats table and button to clear
index usage stats on the Table Details page for each table.
  • Loading branch information
lindseyjin committed Nov 29, 2021
1 parent 41ffd13 commit 1a2e363
Show file tree
Hide file tree
Showing 13 changed files with 655 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

@import "src/core/index.module";

.tab-area {
margin-bottom: $spacing-large;
}

.database-table-page {
&__indexes {
&--value {
Expand Down Expand Up @@ -54,3 +58,58 @@
fill: $colors--primary-text;
}
}

.index-stats {
&__summary-card {
width: fit-content;
padding: 0;
}

&__header {
align-items: baseline;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: $spacing-medium $spacing-medium 0;
}

&__clear-info {
display: flex;
flex-direction: row;
}

&__last-cleared {
color: $colors--neutral-6;
margin-right: $spacing-base;
}

&__clear-btn {
border-bottom: none;
text-decoration: none;
}

&-table {
&__col {
&-indexes {
width: 30em;
}
&-last-used {
width: 30em;
}
}
}
}


.icon {
&--s {
height: $line-height--x-small;
width: $line-height--x-small;
margin-right: 10px;
}

&--primary {
fill: $colors--primary-text;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
randomTablePrivilege,
} from "src/storybook/fixtures";
import { DatabaseTablePage, DatabaseTablePageProps } from "./databaseTablePage";
import moment from "moment";

const withLoadingIndicator: DatabaseTablePageProps = {
databaseName: randomName(),
Expand All @@ -37,8 +38,16 @@ const withLoadingIndicator: DatabaseTablePageProps = {
sizeInBytes: 0,
rangeCount: 0,
},
indexStats: {
loading: true,
loaded: false,
stats: [],
lastReset: moment("2021-09-04T13:55:00Z"),
},
refreshTableDetails: () => {},
refreshTableStats: () => {},
refreshIndexStats: () => {},
resetIndexUsageStats: () => {},
};

const name = randomName();
Expand Down Expand Up @@ -80,8 +89,35 @@ const withData: DatabaseTablePageProps = {
nodesByRegionString:
"gcp-europe-west1(n8), gcp-us-east1(n1), gcp-us-west1(n6)",
},
indexStats: {
loading: false,
loaded: true,
stats: [
{
totalReads: 0,
lastUsed: moment("2021-10-11T11:29:00Z"),
lastUsedType: "read",
indexName: "primary",
},
{
totalReads: 3,
lastUsed: moment("2021-11-10T16:29:00Z"),
lastUsedType: "read",
indexName: "primary",
},
{
totalReads: 2,
lastUsed: moment("2021-09-04T13:55:00Z"),
lastUsedType: "reset",
indexName: "secondary",
},
],
lastReset: moment("2021-09-04T13:55:00Z"),
},
refreshTableDetails: () => {},
refreshTableStats: () => {},
refreshIndexStats: () => {},
resetIndexUsageStats: () => {},
};

storiesOf("Database Table Page", module)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Col, Row, Tabs } from "antd";
import classNames from "classnames/bind";
import _ from "lodash";
import { Tooltip } from "antd";
import { Heading } from "@cockroachlabs/ui-components";

import { Breadcrumbs } from "src/breadcrumbs";
import { CaretRight } from "src/icon/caretRight";
Expand All @@ -25,6 +26,9 @@ import * as format from "src/util/format";
import styles from "./databaseTablePage.module.scss";
import { commonStyles } from "src/common";
import { baseHeadingClasses } from "src/transactionsPage/transactionsPageClasses";
import moment, { Moment } from "moment";
import { Search as IndexIcon } from "@cockroachlabs/icons";
import { formatDate } from "antd/es/date-picker/utils";
const cx = classNames.bind(styles);

const { TabPane } = Tabs;
Expand Down Expand Up @@ -58,12 +62,24 @@ const { TabPane } = Tabs;
// rangeCount: number;
// nodesByRegionString: string;
// };
// indexStats: { // DatabaseTablePageIndexStats
// loading: boolean;
// loaded: boolean;
// stats: {
// indexName: string;
// totalReads: number;
// lastUsed: Moment;
// lastUsedType: string;
// }[];
// lastReset: Moment;
// };
// }
export interface DatabaseTablePageData {
databaseName: string;
name: string;
details: DatabaseTablePageDataDetails;
stats: DatabaseTablePageDataStats;
indexStats: DatabaseTablePageIndexStats;
showNodeRegionsSection?: boolean;
}

Expand All @@ -76,6 +92,20 @@ export interface DatabaseTablePageDataDetails {
grants: Grant[];
}

export interface DatabaseTablePageIndexStats {
loading: boolean;
loaded: boolean;
stats: IndexStat[];
lastReset: Moment;
}

interface IndexStat {
indexName: string;
totalReads: number;
lastUsed: Moment;
lastUsedType: string;
}

interface Grant {
user: string;
privilege: string;
Expand All @@ -91,7 +121,9 @@ export interface DatabaseTablePageDataStats {

export interface DatabaseTablePageActions {
refreshTableDetails: (database: string, table: string) => void;
refreshTableStats: (databse: string, table: string) => void;
refreshTableStats: (database: string, table: string) => void;
refreshIndexStats?: (database: string, table: string) => void;
resetIndexUsageStats?: (database: string, table: string) => void;
refreshNodes?: () => void;
}

Expand All @@ -103,6 +135,7 @@ interface DatabaseTablePageState {
}

class DatabaseTableGrantsTable extends SortedTable<Grant> {}
class IndexUsageStatsTable extends SortedTable<IndexStat> {}

export class DatabaseTablePage extends React.Component<
DatabaseTablePageProps,
Expand Down Expand Up @@ -143,13 +176,103 @@ export class DatabaseTablePage extends React.Component<
this.props.name,
);
}

if (!this.props.indexStats.loaded && !this.props.indexStats.loading) {
return this.props.refreshIndexStats(
this.props.databaseName,
this.props.name,
);
}
}

minDate = moment.utc("0001-01-01"); // minimum value as per UTC

private changeSortSetting(sortSetting: SortSetting) {
this.setState({ sortSetting });
}

private columns: ColumnDescriptor<Grant>[] = [
private getLastResetString() {
const lastReset = this.props.indexStats.lastReset;
if (lastReset.isSame(this.minDate)) {
return "Last cleared: Never";
} else {
return (
"Last cleared: " +
formatDate(lastReset, "MMM DD, YYYY [at] h:mm A [(UTC)]")
);
}
}

private getLastUsedString(indexStat: IndexStat) {
const lastReset = this.props.indexStats.lastReset;
switch (indexStat.lastUsedType) {
case "read":
return formatDate(
indexStat.lastUsed,
"[Last read:] MMM DD, YYYY [at] h:mm A",
);
case "reset":
default:
// TODO(lindseyjin): replace default case with create time after it's added to table_indexes
if (lastReset.isSame(this.minDate)) {
return "Last reset: Never";
} else {
return formatDate(
lastReset,
"[Last reset:] MMM DD, YYYY [at] h:mm A",
);
}
}
}

private indexStatsColumns: ColumnDescriptor<IndexStat>[] = [
{
name: "indexes",
title: (
<Tooltip placement="bottom" title="The index name.">
Indexes
</Tooltip>
),
className: cx("index-stats-table__col-indexes"),
cell: indexStat => (
<>
<IndexIcon className={cx("icon--s", "icon--primary")} />
{indexStat.indexName}
</>
),
sort: indexStat => indexStat.indexName,
},
{
name: "total reads",
title: (
<Tooltip
placement="bottom"
title="The total number of reads for this index."
>
Total Reads
</Tooltip>
),
cell: indexStat => indexStat.totalReads,
sort: indexStat => indexStat.totalReads,
},
{
name: "last used",
title: (
<Tooltip
placement="bottom"
title="The last time this index was used (reset or read from)."
>
Last Used (UTC)
</Tooltip>
),
className: cx("index-stats-table__col-last-used"),
cell: indexStat => this.getLastUsedString(indexStat),
sort: indexStat => indexStat.lastUsed,
},
// TODO(lindseyjin): add index recommendations column
];

private grantsColumns: ColumnDescriptor<Grant>[] = [
{
name: "user",
title: (
Expand Down Expand Up @@ -200,7 +323,7 @@ export class DatabaseTablePage extends React.Component<
</h3>
</section>

<section className={baseHeadingClasses.wrapper}>
<section className={(baseHeadingClasses.wrapper, cx("tab-area"))}>
<Tabs className={commonStyles("cockroach--tabs")}>
<TabPane tab="Overview" key="overview">
<Row>
Expand Down Expand Up @@ -247,12 +370,56 @@ export class DatabaseTablePage extends React.Component<
</SummaryCard>
</Col>
</Row>
<SummaryCard
className={cx("summary-card", "index-stats__summary-card")}
>
<Row>
<div className={cx("index-stats__header")}>
<Heading type="h5">Index Stats</Heading>
<div className={cx("index-stats__clear-info")}>
<Tooltip
placement="bottom"
title="Index stats accumulate from the time they were last cleared. Clicking ‘Clear index stats’ will reset index stats for the entire cluster."
>
<div
className={cx(
"index-stats__last-cleared",
"underline",
)}
>
{this.getLastResetString()}
</div>
</Tooltip>
<div>
<a
className={cx("action", "separator")}
onClick={() =>
this.props.resetIndexUsageStats(
this.props.databaseName,
this.props.name,
)
}
>
Clear index stats
</a>
</div>
</div>
</div>
<IndexUsageStatsTable
className="index-stats-table"
data={this.props.indexStats.stats}
columns={this.indexStatsColumns}
sortSetting={this.state.sortSetting}
onChangeSortSetting={this.changeSortSetting.bind(this)}
loading={this.props.indexStats.loading}
/>
</Row>
</SummaryCard>
</TabPane>

<TabPane tab="Grants" key="grants">
<DatabaseTableGrantsTable
data={this.props.details.grants}
columns={this.columns}
columns={this.grantsColumns}
sortSetting={this.state.sortSetting}
onChangeSortSetting={this.changeSortSetting.bind(this)}
loading={this.props.details.loading}
Expand Down
Loading

0 comments on commit 1a2e363

Please sign in to comment.