Skip to content

Commit

Permalink
ui/cluster-ui: add 'Interval Start Time' column to stmts/txns tables
Browse files Browse the repository at this point in the history
Resolves #69648

This commit adds the `Interval Start Time (UTC)` column to stmt and txn
tables. Statements and transactions are now both grouped by their `aggregated_ts`
field in addition to the stmt / fingerprint id.

To support viewing of statements grouped by aggregation interval start time,
a new query parameter has been added to statement details pages.
If `aggregated_ts` is set, it will display the statement details for statements
aggregated at that interval, using data from combined statements API response.
If unset, we will show data aggregated over the current date range.

Release note (ui change): A new column, 'Interval Start Time (UTC)', has
been added to both statement and transaction tables. The column represents
the start time in UTC of the stats aggregation interval for a statement.
By default, the aggregation interval is 1 hour.

A new query parameter has been added to statement details pages.
If the search param `aggregated_ts` is set, it will display the statement details
for statements aggregated at that interval. If unset, we will display the statement
details for the statement aggregated over the current date range.
  • Loading branch information
xinhaoz committed Sep 21, 2021
1 parent 16d935d commit 2051127
Show file tree
Hide file tree
Showing 37 changed files with 550 additions and 331 deletions.
39 changes: 0 additions & 39 deletions pkg/ui/workspaces/cluster-ui/src/api/fetchData.spec.ts

This file was deleted.

13 changes: 0 additions & 13 deletions pkg/ui/workspaces/cluster-ui/src/api/fetchData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { RequestError } from "../util";
import { getBasePath } from "./basePath";
import { stringify } from "querystring";

interface ProtoBuilder<
P extends ConstructorType,
Expand All @@ -30,18 +29,6 @@ export function toArrayBuffer(encodedRequest: Uint8Array): ArrayBuffer {
);
}

// propsToQueryString is a helper function that converts a set of object
// properties to a query string
// - keys with null or undefined values will be skipped
// - non-string values will be toString'd
export function propsToQueryString(props: { [k: string]: any }) {
const params = new URLSearchParams();
Object.entries(props).forEach(
([k, v]: [string, any]) => v != null && params.set(k, v.toString()),
);
return params.toString();
}

/**
* @param RespBuilder expects protobuf stub class to build decode response;
* @param path relative URL path for requested resource;
Expand Down
5 changes: 3 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/api/statementsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
// licenses/APL.txt.

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

const STATEMENTS_PATH = "/_status/statements";

Expand All @@ -28,7 +29,7 @@ export const getCombinedStatements = (
const queryStr = propsToQueryString({
start: req.start.toInt(),
end: req.end.toInt(),
combined: true,
combined: req.combined,
});
return fetchData(
cockroach.server.serverpb.StatementsResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { sessionAttr } from "src/util/constants";
import { Helmet } from "react-helmet";
import { Loading } from "../loading";
import _ from "lodash";
import { Link, RouteComponentProps, withRouter } from "react-router-dom";
import { Link, RouteComponentProps } from "react-router-dom";

import { SessionInfo } from "./sessionsTable";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ const statementStats: any = {
exec_stats: execStats,
};

const aggregatedTs = Date.parse("Sep 15 2021 01:00:00 GMT") * 1e-3;

export const getStatementDetailsPropsFixture = (): StatementDetailsProps => ({
history,
location: {
Expand Down Expand Up @@ -146,27 +148,31 @@ export const getStatementDetailsPropsFixture = (): StatementDetailsProps => ({
byNode: [
{
label: "4",
aggregatedTs,
implicitTxn: true,
database: "defaultdb",
fullScan: true,
stats: statementStats,
},
{
label: "3",
aggregatedTs,
implicitTxn: true,
database: "defaultdb",
fullScan: true,
stats: statementStats,
},
{
label: "2",
aggregatedTs,
implicitTxn: true,
database: "defaultdb",
fullScan: true,
stats: statementStats,
},
{
label: "1",
aggregatedTs,
implicitTxn: true,
database: "defaultdb",
fullScan: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import { createSelector } from "@reduxjs/toolkit";
import { RouteComponentProps, match as Match } from "react-router-dom";
import { Location } from "history";
import _ from "lodash";
import { AppState } from "../store";
import {
Expand All @@ -24,12 +25,15 @@ import {
databaseAttr,
StatementStatistics,
statementKey,
aggregatedTsAttr,
queryByName,
} from "../util";
import { AggregateStatistics } from "../statementsTable";
import { Fraction } from "./statementDetails";

interface StatementDetailsData {
nodeId: number;
aggregatedTs: number;
implicitTxn: boolean;
fullScan: boolean;
database: string;
Expand All @@ -46,6 +50,7 @@ function coalesceNodeStats(
if (!(key in statsKey)) {
statsKey[key] = {
nodeId: stmt.node_id,
aggregatedTs: stmt.aggregated_ts,
implicitTxn: stmt.implicit_txn,
fullScan: stmt.full_scan,
database: stmt.database,
Expand All @@ -59,6 +64,7 @@ function coalesceNodeStats(
const stmt = statsKey[key];
return {
label: stmt.nodeId.toString(),
aggregatedTs: stmt.aggregatedTs,
implicitTxn: stmt.implicitTxn,
fullScan: stmt.fullScan,
database: stmt.database,
Expand Down Expand Up @@ -87,15 +93,20 @@ function fractionMatching(

function filterByRouterParamsPredicate(
match: Match<any>,
location: Location,
internalAppNamePrefix: string,
): (stat: ExecutionStatistics) => boolean {
const statement = getMatchParamByName(match, statementAttr);
const implicitTxn = getMatchParamByName(match, implicitTxnAttr) === "true";
const database = getMatchParamByName(match, databaseAttr);
// If the aggregatedTs is unset, we will aggregate across the current date range.
const aggregatedTs = queryByName(location, aggregatedTsAttr);
let app = getMatchParamByName(match, appAttr);

const filterByKeys = (stmt: ExecutionStatistics) =>
stmt.statement === statement &&
aggregatedTs == null &&
(aggregatedTs == null || stmt.aggregated_ts.toString() === aggregatedTs) &&
stmt.implicit_txn === implicitTxn &&
(stmt.database === database || database === null);

Expand Down Expand Up @@ -129,7 +140,11 @@ export const selectStatement = createSelector(
const flattened = flattenStatementStats(statements);
const results = _.filter(
flattened,
filterByRouterParamsPredicate(props.match, internalAppNamePrefix),
filterByRouterParamsPredicate(
props.match,
props.location,
internalAppNamePrefix,
),
);
const statement = getMatchParamByName(props.match, statementAttr);
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ export class StatementDetails extends React.Component<
isTenant: false,
};

changeSortSetting = (ss: SortSetting) => {
changeSortSetting = (ss: SortSetting): void => {
this.setState({
sortSetting: ss,
});
Expand All @@ -343,12 +343,12 @@ export class StatementDetails extends React.Component<
}
};

refreshStatements = () => {
refreshStatements = (): void => {
const req = statementsRequestFromProps(this.props);
this.props.refreshStatements(req);
};

componentDidMount() {
componentDidMount(): void {
this.refreshStatements();
if (!this.props.isTenant) {
this.props.refreshStatementDiagnosticsRequests();
Expand All @@ -357,7 +357,7 @@ export class StatementDetails extends React.Component<
}
}

componentDidUpdate() {
componentDidUpdate(): void {
this.refreshStatements();
if (!this.props.isTenant) {
this.props.refreshStatementDiagnosticsRequests();
Expand All @@ -366,7 +366,7 @@ export class StatementDetails extends React.Component<
}
}

onTabChange = (tabId: string) => {
onTabChange = (tabId: string): void => {
const { history } = this.props;
const searchParams = new URLSearchParams(history.location.search);
searchParams.set("tab", tabId);
Expand All @@ -380,7 +380,7 @@ export class StatementDetails extends React.Component<
this.props.onTabChanged && this.props.onTabChanged(tabId);
};

backToStatementsClick = () => {
backToStatementsClick = (): void => {
this.props.history.push("/statements");
if (this.props.onBackToStatementsClick) {
this.props.onBackToStatementsClick();
Expand Down
Loading

0 comments on commit 2051127

Please sign in to comment.