Skip to content

Commit eb64879

Browse files
committed
ui: add apply recommendations to table details
On Table details page, we have a table with index recommendations, which right now is focused on drop unused indexes. This commit adds the button the apply the recommendations, when there is one. Fixes #86949 Release note (ui change): Add apply button on Table Details page (db console only), when there is a recommendation to drop an unused index.
1 parent 9281a67 commit eb64879

5 files changed

Lines changed: 72 additions & 21 deletions

File tree

pkg/ui/workspaces/cluster-ui/src/databaseTablePage/databaseTablePage.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// by the Apache License, Version 2.0, included in the file
99
// licenses/APL.txt.
1010

11-
import React from "react";
11+
import React, { useContext } from "react";
1212
import { Col, Row, Tabs, Tooltip } from "antd";
1313
import "antd/lib/col/style";
1414
import "antd/lib/row/style";
@@ -48,6 +48,9 @@ import { Search as IndexIcon } from "@cockroachlabs/icons";
4848
import booleanSettingStyles from "../settings/booleanSetting.module.scss";
4949
import { CircleFilled } from "../icon";
5050
import { performanceTuningRecipes } from "src/util/docs";
51+
import { CockroachCloudContext } from "../contexts";
52+
import IdxRecAction from "../insights/indexActionBtn";
53+
import { RecommendationType } from "../indexDetailsPage";
5154

5255
const cx = classNames.bind(styles);
5356
const booleanSettingCx = classnames.bind(booleanSettingStyles);
@@ -134,7 +137,7 @@ interface IndexStat {
134137
}
135138

136139
interface IndexRecommendation {
137-
type: string;
140+
type: RecommendationType;
138141
reason: string;
139142
}
140143

@@ -330,18 +333,15 @@ export class DatabaseTablePage extends React.Component<
330333
</div>
331334
);
332335
}
333-
return indexStat.indexRecommendations.map(recommendation => {
336+
return indexStat.indexRecommendations.map((recommendation, key) => {
334337
let text: string;
335338
switch (recommendation.type) {
336339
case "DROP_UNUSED":
337340
text = "Drop unused index";
338341
}
339-
// TODO(thomas): using recommendation.type as the key seems not good.
340-
// - if it is possible for an index to have multiple recommendations of the same type
341-
// this could cause issues.
342342
return (
343343
<Tooltip
344-
key={recommendation.type}
344+
key={key}
345345
placement="bottom"
346346
title={
347347
<div className={cx("index-recommendations-text__tooltip-anchor")}>
@@ -361,6 +361,31 @@ export class DatabaseTablePage extends React.Component<
361361
});
362362
};
363363

364+
private renderActionCell = (indexStat: IndexStat): React.ReactNode => {
365+
const isCockroachCloud = useContext(CockroachCloudContext);
366+
if (isCockroachCloud || indexStat.indexRecommendations.length === 0) {
367+
return <></>;
368+
}
369+
370+
const query = indexStat.indexRecommendations.map(recommendation => {
371+
switch (recommendation.type) {
372+
case "DROP_UNUSED":
373+
return `DROP INDEX ${this.props.name}@${indexStat.indexName};`;
374+
}
375+
});
376+
if (query.length === 0) {
377+
return <></>;
378+
}
379+
380+
return (
381+
<IdxRecAction
382+
actionQuery={query.join(" ")}
383+
actionType={"DropIndex"}
384+
database={this.props.databaseName}
385+
/>
386+
);
387+
};
388+
364389
private indexStatsColumns: ColumnDescriptor<IndexStat>[] = [
365390
{
366391
name: "indexes",
@@ -406,6 +431,11 @@ export class DatabaseTablePage extends React.Component<
406431
cell: this.renderIndexRecommendations,
407432
sort: indexStat => indexStat.indexRecommendations.length,
408433
},
434+
{
435+
name: "action",
436+
title: "",
437+
cell: this.renderActionCell,
438+
},
409439
];
410440

411441
private grantsColumns: ColumnDescriptor<Grant>[] = [

pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetails.selectors.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
2525
import { IndexDetailsPageData } from "./indexDetailsPage";
2626
import { selectIsTenant } from "../store/uiConfig";
2727
import { BreadcrumbItem } from "../breadcrumbs";
28+
import { RecommendationType as RecType } from "./indexDetailsPage";
2829
const { RecommendationType } = cockroach.sql.IndexRecommendation;
2930

3031
export const selectIndexDetails = createSelector(
@@ -48,8 +49,13 @@ export const selectIndexDetails = createSelector(
4849
indexRec => indexRec.index_id === details.statistics.key.index_id,
4950
) || [];
5051
const indexRecommendations = filteredIndexRecommendations.map(indexRec => {
52+
let type: RecType = "Unknown";
53+
switch (RecommendationType[indexRec.type].toString()) {
54+
case "DROP_UNUSED":
55+
type = "DROP_UNUSED";
56+
}
5157
return {
52-
type: RecommendationType[indexRec.type].toString(),
58+
type: type,
5359
reason: indexRec.reason,
5460
};
5561
});

pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ interface IndexDetails {
7070
indexRecommendations: IndexRecommendation[];
7171
}
7272

73+
export type RecommendationType = "DROP_UNUSED" | "Unknown";
74+
7375
interface IndexRecommendation {
74-
type: string;
76+
type: RecommendationType;
7577
reason: string;
7678
}
7779

@@ -141,20 +143,14 @@ export class IndexDetailsPage extends React.Component<
141143
</tr>
142144
);
143145
}
144-
return indexRecommendations.map(recommendation => {
146+
return indexRecommendations.map((recommendation, key) => {
145147
let recommendationType: string;
146148
switch (recommendation.type) {
147149
case "DROP_UNUSED":
148150
recommendationType = "Drop unused index";
149151
}
150-
// TODO(thomas): using recommendation.type as the key seems not good.
151-
// - if it is possible for an index to have multiple recommendations of the same type
152-
// this could cause issues.
153152
return (
154-
<tr
155-
key={recommendationType}
156-
className={cx("index-recommendations-rows")}
157-
>
153+
<tr key={key} className={cx("index-recommendations-rows")}>
158154
<td
159155
className={cx(
160156
"index-recommendations-rows__header",

pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
import { RouteComponentProps } from "react-router";
1212
import { createSelector } from "reselect";
1313
import _ from "lodash";
14-
import { DatabaseTablePageData, util } from "@cockroachlabs/cluster-ui";
14+
import {
15+
DatabaseTablePageData,
16+
util,
17+
RecommendationType as RecType,
18+
} from "@cockroachlabs/cluster-ui";
1519

1620
import { cockroach } from "src/js/protos";
1721
import {
@@ -95,8 +99,13 @@ export const mapStateToProps = createSelector(
9599
) || [];
96100
const indexRecommendations = filteredIndexRecommendations.map(
97101
indexRec => {
102+
let type: RecType = "Unknown";
103+
switch (RecommendationType[indexRec.type].toString()) {
104+
case "DROP_UNUSED":
105+
type = "DROP_UNUSED";
106+
}
98107
return {
99-
type: RecommendationType[indexRec.type].toString(),
108+
type: type,
100109
reason: indexRec.reason,
101110
};
102111
},

pkg/ui/workspaces/db-console/src/views/databases/indexDetailsPage/redux.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
// licenses/APL.txt.
1010

1111
import { createSelector } from "reselect";
12-
import { IndexDetailsPageData, util } from "@cockroachlabs/cluster-ui";
12+
import {
13+
IndexDetailsPageData,
14+
util,
15+
RecommendationType as RecType,
16+
} from "@cockroachlabs/cluster-ui";
1317
import { AdminUIState } from "src/redux/state";
1418
import { RouteComponentProps } from "react-router";
1519
import { getMatchParamByName } from "src/util/query";
@@ -47,8 +51,14 @@ export const mapStateToProps = createSelector(
4751
indexRec => indexRec.index_id === details.statistics.key.index_id,
4852
) || [];
4953
const indexRecommendations = filteredIndexRecommendations.map(indexRec => {
54+
let type: RecType = "Unknown";
55+
switch (RecommendationType[indexRec.type].toString()) {
56+
case "DROP_UNUSED":
57+
type = "DROP_UNUSED";
58+
}
59+
5060
return {
51-
type: RecommendationType[indexRec.type].toString(),
61+
type: type,
5262
reason: indexRec.reason,
5363
};
5464
});

0 commit comments

Comments
 (0)