Skip to content

Commit

Permalink
Merge #92463
Browse files Browse the repository at this point in the history
92463: server, ui: add used index to statement details r=maryliag a=maryliag

This commit convert from table id and index id on
the statement details endpoint, and adds the index used information to the Explain Plan tab on Statement Details.

The elements of the list are links that redirect to the table or index details page.

Fixes #82615

https://www.loom.com/share/530bf4e795d648bb854c18d60b074e4c

<img width="1483" alt="Screen Shot 2022-11-24 at 11 11 54 AM" src="https://user-images.githubusercontent.com/1017486/203828306-0a6fb905-cae1-4217-8ea3-b4e323cf3a72.png">



Release note (ui change): Add a list of used index per explain plan, under the Explain Plan tab on Statement Details page, with links to the table or index details pages.

Co-authored-by: maryliag <marylia@cockroachlabs.com>
  • Loading branch information
craig[bot] and maryliag committed Nov 29, 2022
2 parents 6588db8 + 38823ee commit 1af116f
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 1 deletion.
37 changes: 37 additions & 0 deletions pkg/server/combined_statement_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,37 @@ func getExplainPlanFromGist(ctx context.Context, ie *sql.InternalExecutor, planG
return strings.Join(explainPlan, "\n")
}

func getIdxAndTableName(ctx context.Context, ie *sql.InternalExecutor, indexInfo string) string {
var args []interface{}
idxInfoArr := strings.Split(indexInfo, "@")
tableID, err := strconv.ParseInt(idxInfoArr[0], 10, 64)
if err != nil {
return indexInfo
}
indexID, err := strconv.ParseInt(idxInfoArr[1], 10, 64)
if err != nil {
return indexInfo
}
args = append(args, tableID)
args = append(args, indexID)

row, err := ie.QueryRowEx(ctx, "combined-stmts-details-get-index-and-table-names", nil,
sessiondata.InternalExecutorOverride{
User: username.NodeUserName(),
}, `SELECT descriptor_name, index_name FROM crdb_internal.table_indexes
WHERE descriptor_id =$1 AND index_id=$2`, args...)
if err != nil {
return indexInfo
}
if row == nil {
// Value being used on the UI for checks.
return "dropped"
}
tableName := tree.MustBeDString(row[0])
indexName := tree.MustBeDString(row[1])
return fmt.Sprintf("%s@%s", tableName, indexName)
}

// getStatementDetailsPerPlanHash returns the list of statements
// per plan hash, not using the columns aggregated timestamp as
// part of the key on the grouping.
Expand Down Expand Up @@ -824,6 +855,12 @@ func getStatementDetailsPerPlanHash(
}
aggregatedMetadata.TotalCount = metadata.Stats.Count

var indexes []string
for _, idx := range metadata.Stats.Indexes {
indexes = append(indexes, getIdxAndTableName(ctx, ie, idx))
}
metadata.Stats.Indexes = indexes

stmt := serverpb.StatementDetailsResponse_CollectedStatementGroupedByPlanHash{
AggregationInterval: time.Duration(aggInterval.Nanos()),
ExplainPlan: explainPlan,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@import "src/core/index.module";

.bold-link {
font-weight: $font-weight--bold;

&:hover {
color: $colors--link;
text-decoration: underline;
}
}

.regular-link:hover {
color: $colors--link;
text-decoration: underline;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import React from "react";
import React, { ReactNode } from "react";
import { ColumnDescriptor, SortedTable } from "src/sortedtable";
import { Tooltip } from "@cockroachlabs/ui-components";
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
Expand All @@ -22,20 +22,27 @@ import {
explainPlan,
limitText,
Count,
intersperse,
} from "../../util";
import { Anchor } from "../../anchor";
import classNames from "classnames/bind";
import styles from "./plansTable.module.scss";
import { Link } from "react-router-dom";

export type PlanHashStats =
cockroach.server.serverpb.StatementDetailsResponse.ICollectedStatementGroupedByPlanHash;
export class PlansSortedTable extends SortedTable<PlanHashStats> {}

const cx = classNames.bind(styles);

const planDetailsColumnLabels = {
avgExecTime: "Average Execution Time",
avgRowsRead: "Average Rows Read",
distSQL: "Distributed",
execCount: "Execution Count",
fullScan: "Full Scan",
insights: "Insights",
indexes: "Used Indexes",
lastExecTime: "Last Execution Time",
planGist: "Plan Gist",
vectorized: "Vectorized",
Expand Down Expand Up @@ -153,6 +160,17 @@ export const planDetailsTableTitles: PlanDetailsTableTitleType = {
</Tooltip>
);
},
indexes: () => {
return (
<Tooltip
style="tableTitle"
placement="bottom"
content={"Indexes used by the Explain Plan."}
>
{planDetailsColumnLabels.indexes}
</Tooltip>
);
},
};

function formatInsights(recommendations: string[]): string {
Expand All @@ -165,6 +183,78 @@ function formatInsights(recommendations: string[]): string {
return `${recommendations.length} Insights`;
}

function formatIndexes(indexes: string[], database: string): ReactNode {
if (indexes.length == 0) {
return <></>;
}
const indexMap: Map<string, Array<string>> = new Map<string, Array<string>>();
let droppedCount = 0;
let tableName;
let idxName;
let indexInfo;
for (let i = 0; i < indexes.length; i++) {
if (indexes[i] === "dropped") {
droppedCount++;
continue;
}
if (!indexes[i].includes("@")) {
continue;
}
indexInfo = indexes[i].split("@");
tableName = indexInfo[0];
idxName = indexInfo[1];
if (indexMap.has(tableName)) {
indexMap.set(tableName, indexMap.get(tableName).concat(idxName));
} else {
indexMap.set(tableName, [idxName]);
}
}

let newLine;
const list = Array.from(indexMap).map((value, i) => {
const table = value[0];
newLine = i > 0 ? <br /> : "";
const indexesList = intersperse<ReactNode>(
value[1].map(idx => {
return (
<Link
className={cx("regular-link")}
to={`/database/${database}/table/${table}/index/${idx}`}
key={`${table}${idx}`}
>
{idx}
</Link>
);
}),
", ",
);
return (
<span key={table}>
{newLine}
<Link
className={cx("bold-link")}
to={`/database/${database}/table/${table}`}
>
{table}
</Link>
: {indexesList}
</span>
);
});
newLine = list.length > 0 ? <br /> : "";
if (droppedCount === 1) {
list.push(<span key={`dropped`}>{newLine}[dropped index]</span>);
} else if (droppedCount > 1) {
list.push(
<span key={`dropped`}>
{newLine}[{droppedCount} dropped indexes]
</span>,
);
}

return intersperse<ReactNode>(list, ",");
}

export function makeExplainPlanColumns(
handleDetails: (plan: PlanHashStats) => void,
): ColumnDescriptor<PlanHashStats>[] {
Expand All @@ -184,6 +274,13 @@ export function makeExplainPlanColumns(
sort: (item: PlanHashStats) => item.stats.plan_gists[0],
alwaysShow: true,
},
{
name: "indexes",
title: planDetailsTableTitles.indexes(),
cell: (item: PlanHashStats) =>
formatIndexes(item.stats.indexes, item.metadata.databases[0]),
sort: (item: PlanHashStats) => item.stats.indexes?.join(""),
},
{
name: "insights",
title: planDetailsTableTitles.insights(),
Expand Down

0 comments on commit 1af116f

Please sign in to comment.