Skip to content

Commit

Permalink
ui: add list of fingerprints used by index
Browse files Browse the repository at this point in the history
Part Of cockroachdb#93087

This commits adds on the Index Details page the list
of fingerprints that were used by that index.

This first commit shows all statement fingerprints, follow PRs
will add features such as: search, filter, time selection and
listing Transactions.

Release note (ui change): Adds on the Index Details page a list of
all statement fingerprints that used that index.
  • Loading branch information
maryliag committed Jan 10, 2023
1 parent 6fa30af commit 42c82d9
Show file tree
Hide file tree
Showing 15 changed files with 397 additions and 19 deletions.
2 changes: 2 additions & 0 deletions docs/generated/http/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -4746,6 +4746,8 @@ Response object returned by TableIndexStatsResponse.
| index_type | [string](#cockroach.server.serverpb.TableIndexStatsResponse-string) | | index_type is the type of the index i.e. primary, secondary. | [reserved](#support-status) |
| create_statement | [string](#cockroach.server.serverpb.TableIndexStatsResponse-string) | | create_statement is the SQL statement that would re-create the current index if executed. | [reserved](#support-status) |
| created_at | [google.protobuf.Timestamp](#cockroach.server.serverpb.TableIndexStatsResponse-google.protobuf.Timestamp) | | CreatedAt is an approximate timestamp at which the index was created. Note that it may not always be populated. | [reserved](#support-status) |
| index_id | [string](#cockroach.server.serverpb.TableIndexStatsResponse-string) | | index_id is the ID of the index. | [reserved](#support-status) |
| table_id | [string](#cockroach.server.serverpb.TableIndexStatsResponse-string) | | table_id is the ID of the table which the index belongs to. | [reserved](#support-status) |



Expand Down
5 changes: 4 additions & 1 deletion pkg/server/serverpb/status.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1802,7 +1802,10 @@ message TableIndexStatsResponse {
// CreatedAt is an approximate timestamp at which the index was created.
// Note that it may not always be populated.
google.protobuf.Timestamp created_at = 5 [(gogoproto.stdtime) = true];

// index_id is the ID of the index.
string index_id = 6 [(gogoproto.customname) = "IndexID"];
// table_id is the ID of the table which the index belongs to.
string table_id = 7 [(gogoproto.customname) = "TableID"];
}

repeated ExtendedCollectedIndexUsageStatistics statistics = 1;
Expand Down
80 changes: 79 additions & 1 deletion pkg/ui/workspaces/cluster-ui/src/api/indexDetailsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,18 @@
// licenses/APL.txt.

import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "src/api";
import {
convertStatementRawFormatToAggregatedStatistics,
executeInternalSql,
fetchData,
SqlExecutionRequest,
sqlResultsAreEmpty,
StatementRawFormat,
} from "src/api";
import moment from "moment";
import { TimeScale, toDateRange } from "../timeScaleDropdown";
import { AggregateStatistics } from "../statementsTable";
import { INTERNAL_APP_NAME_PREFIX } from "../recentExecutions/recentStatementUtils";

export type TableIndexStatsRequest =
cockroach.server.serverpb.TableIndexStatsRequest;
Expand Down Expand Up @@ -50,3 +61,70 @@ export const resetIndexStats = (
"30M",
);
};

export type StatementsUsingIndexRequest = {
table: string;
index: string;
database: string;
start?: moment.Moment;
end?: moment.Moment;
};

export function StatementsListRequestFromDetails(
table: string,
index: string,
database: string,
ts: TimeScale,
): StatementsUsingIndexRequest {
if (ts === null) return { table, index, database };
const [start, end] = toDateRange(ts);
return { table, index, database, start, end };
}

export function getStatementsUsingIndex({
table,
index,
database,
start,
end,
}: StatementsUsingIndexRequest): Promise<AggregateStatistics[]> {
const args: any = [`"${table}@${index}"`];
let placeholder = 2;
let whereClause = "";
if (start) {
args.push(start);
whereClause = `${whereClause} AND aggregated_ts >= $${placeholder}`;
placeholder++;
}
if (end) {
args.push(end);
whereClause = `${whereClause} AND aggregated_ts <= $${placeholder}`;
placeholder++;
}

const selectStatements = {
sql: `SELECT * FROM system.statement_statistics
WHERE $1::jsonb <@ indexes_usage
AND app_name NOT LIKE '${INTERNAL_APP_NAME_PREFIX}%'
${whereClause};`,
arguments: args,
};

const req: SqlExecutionRequest = {
execute: true,
statements: [selectStatements],
database: database,
};

return executeInternalSql<StatementRawFormat>(req).then(res => {
if (res.error || sqlResultsAreEmpty(res)) {
return [];
}

const statements: AggregateStatistics[] = [];
res.execution.txn_results[0].rows.forEach(s => {
statements.push(convertStatementRawFormatToAggregatedStatistics(s));
});
return statements;
});
}
120 changes: 118 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/api/statementsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@

import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { fetchData } from "src/api";
import { propsToQueryString } from "src/util";

import {
FixFingerprintHexValue,
HexStringToInt64String,
NumericStat,
propsToQueryString,
stringToTimestamp,
} from "src/util";
import Long from "long";
import { AggregateStatistics } from "../statementsTable";
const STATEMENTS_PATH = "/_status/statements";
const STATEMENT_DETAILS_PATH = "/_status/stmtdetails";

Expand Down Expand Up @@ -64,3 +71,112 @@ export const getStatementDetails = (
"30M",
);
};

export type StatementMetadata = {
db: string;
distsql: boolean;
failed: boolean;
fullScan: boolean;
implicitTxn: boolean;
query: string;
querySummary: string;
stmtTyp: string;
vec: boolean;
};

type Statistics = {
bytesRead: NumericStat;
cnt: Long;
firstAttemptCnt: Long;
idleLat: NumericStat;
indexes: string[];
lastExecAt: string;
maxRetries: Long;
nodes: Long[];
numRows: NumericStat;
ovhLat: NumericStat;
parseLat: NumericStat;
planGists: string[];
planLat: NumericStat;
rowsRead: NumericStat;
rowsWritten: NumericStat;
runLat: NumericStat;
svcLat: NumericStat;
};

type ExecStats = {
contentionTime: NumericStat;
cnt: Long;
maxDiskUsage: NumericStat;
maxMemUsage: NumericStat;
networkBytes: NumericStat;
networkMsgs: NumericStat;
};

type StatementStatistics = {
execution_statistics: ExecStats;
index_recommendations: string[];
statistics: Statistics;
};

export type StatementRawFormat = {
aggregated_ts: number;
fingerprint_id: string;
transaction_fingerprint_id: string;
plan_hash: string;
app_name: string;
node_id: number;
agg_interval: number;
metadata: StatementMetadata;
statistics: StatementStatistics;
index_recommendations: string[];
indexes_usage: string[];
};

export function convertStatementRawFormatToAggregatedStatistics(
s: StatementRawFormat,
): AggregateStatistics {
return {
aggregationInterval: s.agg_interval,
applicationName: s.app_name,
database: s.metadata.db,
fullScan: s.metadata.fullScan,
implicitTxn: s.metadata.implicitTxn,
label: s.metadata.querySummary,
summary: s.metadata.querySummary,
aggregatedTs: s.aggregated_ts,
aggregatedFingerprintID: HexStringToInt64String(s.fingerprint_id),
aggregatedFingerprintHexID: FixFingerprintHexValue(s.fingerprint_id),
stats: {
exec_stats: {
contention_time: s.statistics.execution_statistics.contentionTime,
count: s.statistics.execution_statistics.cnt,
max_disk_usage: s.statistics.execution_statistics.maxDiskUsage,
max_mem_usage: s.statistics.execution_statistics.maxMemUsage,
network_bytes: s.statistics.execution_statistics.networkBytes,
network_messages: s.statistics.execution_statistics.networkMsgs,
},
bytes_read: s.statistics.statistics.bytesRead,
count: s.statistics.statistics.cnt,
first_attempt_count: s.statistics.statistics.firstAttemptCnt,
idle_lat: s.statistics.statistics.idleLat,
index_recommendations: s.statistics.index_recommendations,
indexes: s.statistics.statistics.indexes,
last_exec_timestamp: stringToTimestamp(
s.statistics.statistics.lastExecAt,
),
max_retries: s.statistics.statistics.maxRetries,
nodes: s.statistics.statistics.nodes,
num_rows: s.statistics.statistics.numRows,
overhead_lat: s.statistics.statistics.ovhLat,
parse_lat: s.statistics.statistics.parseLat,
plan_gists: s.statistics.statistics.planGists,
plan_lat: s.statistics.statistics.planLat,
rows_read: s.statistics.statistics.rowsRead,
rows_written: s.statistics.statistics.rowsWritten,
run_lat: s.statistics.statistics.runLat,
service_lat: s.statistics.statistics.svcLat,
sql_type: s.metadata.stmtTyp,
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ import {
} from "../util";
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { IndexDetailsPageData } from "./indexDetailsPage";
import { selectIsTenant } from "../store/uiConfig";
import {
selectHasViewActivityRedactedRole,
selectIsTenant,
} from "../store/uiConfig";
import { BreadcrumbItem } from "../breadcrumbs";
import { RecommendationType as RecType } from "./indexDetailsPage";
import { nodeRegionsByIDSelector } from "../store/nodes";
const { RecommendationType } = cockroach.sql.IndexRecommendation;

export const selectIndexDetails = createSelector(
Expand All @@ -39,7 +43,18 @@ export const selectIndexDetails = createSelector(
getMatchParamByName(props.match, indexNameAttr),
(state: AppState) => state.adminUI.indexStats.cachedData,
(state: AppState) => selectIsTenant(state),
(database, schema, table, index, indexStats): IndexDetailsPageData => {
(state: AppState) => selectHasViewActivityRedactedRole(state),
(state: AppState) => nodeRegionsByIDSelector(state),
(
database,
schema,
table,
index,
indexStats,
isTenant,
hasViewActivityRedactedRole,
nodeRegions,
): IndexDetailsPageData => {
const stats = indexStats[generateTableID(database, table)];
const details = stats?.data?.statistics.filter(
stat => stat.index_name === index, // index names must be unique for a table
Expand Down Expand Up @@ -70,10 +85,15 @@ export const selectIndexDetails = createSelector(
table,
index,
),
isTenant: isTenant,
hasViewActivityRedactedRole: hasViewActivityRedactedRole,
nodeRegions: nodeRegions,
details: {
loading: !!stats?.inFlight,
loaded: !!stats?.valid,
createStatement: details?.create_statement || "",
tableID: details?.statistics.key.table_id.toString(),
indexID: details?.statistics.key.index_id.toString(),
totalReads:
longToInt(details?.statistics?.stats?.total_read_count) || 0,
lastRead: TimestampToMoment(details?.statistics?.stats?.last_read),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,13 @@
margin-top: 20px;
}

.bottom-space {
margin-bottom: 40px;
}

.table-scroll {
width: calc(100% - -23px);
overflow-x: scroll;
padding-left: 0;
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ const withData: IndexDetailsPageProps = {
databaseName: randomName(),
tableName: randomName(),
indexName: randomName(),
isTenant: false,
nodeRegions: {},
details: {
loading: false,
loaded: true,
createStatement: `
CREATE UNIQUE INDEX "primary" ON system.public.database_role_settings USING btree (database_id ASC, role_name ASC)
`,
tableID: "1",
indexID: "1",
totalReads: 0,
lastRead: moment("2021-10-21T22:00:00Z"),
lastReset: moment("2021-12-02T07:12:00Z"),
Expand Down
Loading

0 comments on commit 42c82d9

Please sign in to comment.