Skip to content

Commit

Permalink
Merge #92463 #92631
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.

92631: logictest: ensure lower bound on bytes limit for sqlite r=yuzefovich a=yuzefovich

This commit makes it so that `defaultBatchBytesLimit` is set to at least 3KiB when running sqlite logic tests since if that value is too low, the tests can take really long time (in one example with value of 163 bytes it took 25 minutes vs 3 minutes with 2.5KiB value). This is achieved by explicitly updating this single metamorphic value when run with the sqlite target. I chose this option rather than forcing production values knob (which would disable some other randomizations) to have the smallest possible reduction in test coverage while still making the tests fast enough.

Fixes: #92534.

Release note: None

Co-authored-by: maryliag <[email protected]>
Co-authored-by: Yahor Yuzefovich <[email protected]>
  • Loading branch information
3 people committed Nov 29, 2022
3 parents 2b595c5 + 38823ee + c745d96 commit 91e881d
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 1 deletion.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/cmd/generate-logictest/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ func runSqliteLogicTest(t *testing.T, file string) {
MaxSQLMemoryLimit: 512 << 20, // 512 MiB
DisableUseMVCCRangeTombstonesForPointDeletes: true,
DisableSmallEngineBlocks: true,
// Some sqlite tests with very low bytes limit value are too slow, so
// ensure 3 KiB lower bound.
BatchBytesLimitLowerBound: 3 << 10, // 3 KiB
}
logictest.RunLogicTest(t, serverArgs, configIdx, filepath.Join(sqliteLogicTestDir, file))
}
Expand Down
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
1 change: 1 addition & 0 deletions pkg/sql/logictest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ go_library(
"//pkg/sql/pgwire/pgcode",
"//pkg/sql/pgwire/pgerror",
"//pkg/sql/randgen",
"//pkg/sql/rowinfra",
"//pkg/sql/schemachanger/corpus",
"//pkg/sql/schemachanger/scexec",
"//pkg/sql/schemachanger/scplan",
Expand Down
13 changes: 13 additions & 0 deletions pkg/sql/logictest/logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/randgen"
"github.com/cockroachdb/cockroach/pkg/sql/rowinfra"
"github.com/cockroachdb/cockroach/pkg/sql/schemachanger/corpus"
"github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec"
"github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scplan"
Expand Down Expand Up @@ -4008,6 +4009,9 @@ type TestServerArgs struct {
// If set, then we will disable the metamorphic randomization of
// smallEngineBlocks variable.
DisableSmallEngineBlocks bool
// If positive, it provides a lower bound for the default-batch-bytes-limit
// metamorphic constant.
BatchBytesLimitLowerBound int64
}

// RunLogicTests runs logic tests for all files matching the given glob.
Expand Down Expand Up @@ -4103,6 +4107,15 @@ func RunLogicTest(
if err := coldata.SetBatchSizeForTests(coldata.DefaultColdataBatchSize); err != nil {
panic(errors.Wrapf(err, "could not set batch size for test"))
}
} else if serverArgsCopy.BatchBytesLimitLowerBound > 0 {
// If we're not forcing the production values, but we're asked to have a
// lower bound on the batch bytes limit, then check whether the lower
// bound is already satisfied and update the value if not.
min := rowinfra.BytesLimit(serverArgsCopy.BatchBytesLimitLowerBound)
if rowinfra.GetDefaultBatchBytesLimit(false /* forceProductionValue */) < min {
value := min + rowinfra.BytesLimit(rng.Intn(100<<10))
rowinfra.SetDefaultBatchBytesLimitForTests(value)
}
}
hasOverride, overriddenBackupRestoreProbability := logictestbase.ReadBackupRestoreProbabilityOverride(t, path)
config.BackupRestoreProbability = backupRestoreProbability
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/rowinfra/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,10 @@ func GetDefaultBatchBytesLimit(forceProductionValue bool) BytesLimit {
}
return defaultBatchBytesLimit
}

// SetDefaultBatchBytesLimitForTests overrides defaultBatchBytesLimit to the
// given value. This should only be used for tests when forcing the production
// via ForceProductionValues testing knob is undesirable.
func SetDefaultBatchBytesLimitForTests(v BytesLimit) {
defaultBatchBytesLimit = v
}
3 changes: 3 additions & 0 deletions pkg/sql/sqlitelogictest/tests/fakedist-disk/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/sql/sqlitelogictest/tests/fakedist/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/sql/sqlitelogictest/tests/local-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/sql/sqlitelogictest/tests/local/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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 91e881d

Please sign in to comment.