Skip to content

Commit

Permalink
ui: add limit and sort to fingerprints pages
Browse files Browse the repository at this point in the history
This commit adds new knobs to the sql stats
fingerprint pages. Users can now specify a
limit and sort priority with their sql stats
fingerprints requests.

Closes: cockroachdb#97876
Part of: cockroachdb#97875

Release note: None
  • Loading branch information
xinhaoz committed May 16, 2023
1 parent 94e9f8e commit 595c55b
Show file tree
Hide file tree
Showing 20 changed files with 420 additions and 86 deletions.
2 changes: 2 additions & 0 deletions docs/generated/http/full.md
Expand Up @@ -4005,6 +4005,7 @@ Support status: [reserved](#support-status)
| start | [int64](#cockroach.server.serverpb.CombinedStatementsStatsRequest-int64) | | Unix time range for aggregated statements. | [reserved](#support-status) |
| end | [int64](#cockroach.server.serverpb.CombinedStatementsStatsRequest-int64) | | | [reserved](#support-status) |
| fetch_mode | [CombinedStatementsStatsRequest.FetchMode](#cockroach.server.serverpb.CombinedStatementsStatsRequest-cockroach.server.serverpb.CombinedStatementsStatsRequest.FetchMode) | | Note that if fetch_mode is set to transactions only, we will also include the statement statistics for the stmts in the transactions response. This is more of a hack-y method to get the complete stats for txns, because in the client we need to fill in some txn stats info from its stmt stats, such as the query string.<br><br>We prefer this hackier method right now to reduce surface area for backporting these changes, but in the future we will introduce more endpoints to properly organize these differing requests. TODO (xinhaoz) - Split this API into stmts and txns properly instead of using this param. | [reserved](#support-status) |
| limit | [int64](#cockroach.server.serverpb.CombinedStatementsStatsRequest-int64) | | | [reserved](#support-status) |



Expand All @@ -4019,6 +4020,7 @@ Support status: [reserved](#support-status)
| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| stats_type | [CombinedStatementsStatsRequest.StatsType](#cockroach.server.serverpb.CombinedStatementsStatsRequest-cockroach.server.serverpb.CombinedStatementsStatsRequest.StatsType) | | | [reserved](#support-status) |
| sort | [StatsSortOptions](#cockroach.server.serverpb.CombinedStatementsStatsRequest-cockroach.server.serverpb.StatsSortOptions) | | | [reserved](#support-status) |



Expand Down
44 changes: 42 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/api/statementsApi.ts
Expand Up @@ -9,8 +9,10 @@
// licenses/APL.txt.

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

const STATEMENTS_PATH = "/_status/combinedstmts";
const STATEMENT_DETAILS_PATH = "/_status/stmtdetails";
Expand All @@ -34,13 +36,49 @@ export type ErrorWithKey = {
key: string;
};

export const SqlStatsSortOptions = cockroach.server.serverpb.StatsSortOptions;
export type SqlStatsSortType = cockroach.server.serverpb.StatsSortOptions;

export const DEFAULT_STATS_REQ_OPTIONS = {
limit: 100,
sort: SqlStatsSortOptions.SERVICE_LAT,
};

// The required fields to create a stmts request.
type StmtReqFields = {
limit: number;
sort: SqlStatsSortType;
start: moment.Moment;
end: moment.Moment;
};

export function createCombinedStmtsRequest({
limit,
sort,
start,
end,
}: StmtReqFields): StatementsRequest {
return new cockroach.server.serverpb.CombinedStatementsStatsRequest({
start: Long.fromNumber(start.unix()),
end: Long.fromNumber(end.unix()),
limit: Long.fromNumber(limit ?? DEFAULT_STATS_REQ_OPTIONS.limit),
fetch_mode: new cockroach.server.serverpb.CombinedStatementsStatsRequest.FetchMode(
{
sort: sort,
},
),
});
}

export const getCombinedStatements = (
req: StatementsRequest,
): Promise<cockroach.server.serverpb.StatementsResponse> => {
): Promise<SqlStatsResponse> => {
const queryStr = propsToQueryString({
start: req.start.toInt(),
end: req.end.toInt(),
"fetch_mode.stats_type": FetchStatsMode.StmtStatsOnly,
"fetch_mode.sort": req.fetch_mode.sort,
limit: req.limit.toInt(),
});
return fetchData(
cockroach.server.serverpb.StatementsResponse,
Expand All @@ -58,6 +96,8 @@ export const getFlushedTxnStatsApi = (
start: req.start.toInt(),
end: req.end.toInt(),
"fetch_mode.stats_type": FetchStatsMode.TxnStatsOnly,
"fetch_mode.sort": req.fetch_mode?.sort,
limit: req.limit.toInt() ?? DEFAULT_STATS_REQ_OPTIONS.limit,
});
return fetchData(
cockroach.server.serverpb.StatementsResponse,
Expand Down
Expand Up @@ -17,6 +17,7 @@ import { noop } from "lodash";
import * as protos from "@cockroachlabs/crdb-protobuf-client";
import { cockroach } from "@cockroachlabs/crdb-protobuf-client";
import { RequestError } from "src/util";
import { DEFAULT_STATS_REQ_OPTIONS } from "../api/statementsApi";

type IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport;
type IStatementStatistics = protos.cockroach.sql.IStatementStatistics;
Expand Down Expand Up @@ -913,6 +914,8 @@ const statementsPagePropsFixture: StatementsPageProps = {
},
],
statementsError: null,
limit: DEFAULT_STATS_REQ_OPTIONS.limit,
reqSortSetting: DEFAULT_STATS_REQ_OPTIONS.sort,
timeScale: {
windowSize: moment.duration(5, "day"),
sampleSize: moment.duration(5, "minutes"),
Expand Down Expand Up @@ -940,6 +943,8 @@ const statementsPagePropsFixture: StatementsPageProps = {
onColumnsChange: noop,
onSortingChange: noop,
onFilterChange: noop,
onChangeLimit: noop,
onChangeReqSort: noop,
};

export const statementsPagePropsWithRequestError: StatementsPageProps = {
Expand Down
109 changes: 83 additions & 26 deletions pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.tsx
Expand Up @@ -60,15 +60,15 @@ import styles from "./statementsPage.module.scss";
import { EmptyStatementsPlaceholder } from "./emptyStatementsPlaceholder";
import { cockroach, google } from "@cockroachlabs/crdb-protobuf-client";
import { InlineAlert } from "@cockroachlabs/ui-components";

type IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport;
type IDuration = google.protobuf.IDuration;
import sortableTableStyles from "src/sortedtable/sortedtable.module.scss";
import ColumnsSelector from "../columnsSelector/columnsSelector";
import { SelectOption } from "../multiSelectCheckbox/multiSelectCheckbox";
import { UIConfigState } from "../store";
import { StatementsRequest } from "src/api/statementsApi";
import Long from "long";
import {
SqlStatsSortType,
StatementsRequest,
createCombinedStmtsRequest,
} from "src/api/statementsApi";
import ClearStats from "../sqlActivity/clearStats";
import LoadingError from "../sqlActivity/errorComponent";
import {
Expand All @@ -83,7 +83,17 @@ import {
import { commonStyles } from "../common";
import moment from "moment";

import { STATS_LONG_LOADING_DURATION } from "src/util/constants";
import { Dropdown } from "src/dropdown";
import {
STATS_LONG_LOADING_DURATION,
limitOptions,
stmtRequestSortOptions,
getSortLabel,
} from "src/util/sqlActivityConstants";
import { Button } from "src/button";

type IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport;
type IDuration = google.protobuf.IDuration;

const cx = classNames.bind(styles);
const sortableTableCx = classNames.bind(sortableTableStyles);
Expand Down Expand Up @@ -120,6 +130,8 @@ export interface StatementsPageDispatchProps {
onStatementClick?: (statement: string) => void;
onColumnsChange?: (selectedColumns: string[]) => void;
onTimeScaleChange: (ts: TimeScale) => void;
onChangeLimit: (limit: number) => void;
onChangeReqSort: (sort: SqlStatsSortType) => void;
}

export interface StatementsPageStateProps {
Expand All @@ -128,6 +140,8 @@ export interface StatementsPageStateProps {
isReqInFlight: boolean;
lastUpdated: moment.Moment | null;
timeScale: TimeScale;
limit: number;
reqSortSetting: SqlStatsSortType;
statementsError: Error | null;
apps: string[];
databases: string[];
Expand All @@ -147,17 +161,27 @@ export interface StatementsPageState {
pagination: ISortedTablePagination;
filters?: Filters;
activeFilters?: number;
timeScale: TimeScale;
limit: number;
reqSortSetting: SqlStatsSortType;
}

export type StatementsPageProps = StatementsPageDispatchProps &
StatementsPageStateProps &
RouteComponentProps<unknown>;

function stmtsRequestFromTimeScale(ts: TimeScale): StatementsRequest {
const [start, end] = toRoundedDateRange(ts);
return new cockroach.server.serverpb.CombinedStatementsStatsRequest({
start: Long.fromNumber(start.unix()),
end: Long.fromNumber(end.unix()),
type RequestParams = Pick<
StatementsPageState,
"limit" | "reqSortSetting" | "timeScale"
>;

function stmtsRequestFromParams(params: RequestParams): StatementsRequest {
const [start, end] = toRoundedDateRange(params.timeScale);
return createCombinedStmtsRequest({
start,
end,
limit: params.limit,
sort: params.reqSortSetting,
});
}

Expand Down Expand Up @@ -197,6 +221,9 @@ export class StatementsPage extends React.Component<
pageSize: 20,
current: 1,
},
limit: this.props.limit,
timeScale: this.props.timeScale,
reqSortSetting: this.props.reqSortSetting,
};
const stateFromHistory = this.getStateFromHistory();
this.state = merge(defaultState, stateFromHistory);
Expand Down Expand Up @@ -256,9 +283,17 @@ export class StatementsPage extends React.Component<
};

changeTimeScale = (ts: TimeScale): void => {
if (this.props.onTimeScaleChange) {
this.props.onTimeScaleChange(ts);
}
this.setState(prevState => ({
...prevState,
timeScale: ts,
}));
};

updateRequestParams = (): void => {
this.props.onChangeLimit(this.state.limit);
this.props.onChangeReqSort(this.state.reqSortSetting);
this.props.onTimeScaleChange(this.state.timeScale);
this.refreshStatements();
};

resetPagination = (): void => {
Expand All @@ -273,7 +308,7 @@ export class StatementsPage extends React.Component<
};

refreshStatements = (): void => {
const req = stmtsRequestFromTimeScale(this.props.timeScale);
const req = stmtsRequestFromParams(this.state);
this.props.refreshStatements(req);
};

Expand Down Expand Up @@ -337,21 +372,14 @@ export class StatementsPage extends React.Component<
);
}

componentDidUpdate = (prevProps: StatementsPageProps): void => {
componentDidUpdate = (): void => {
this.updateQueryParams();
if (!this.props.isTenant) {
this.props.refreshNodes();
if (!this.props.hasViewActivityRedactedRole) {
this.props.refreshStatementDiagnosticsRequests();
}
}

if (
prevProps.timeScale !== this.props.timeScale ||
(prevProps.isDataValid && !this.props.isDataValid)
) {
this.refreshStatements();
}
};

componentWillUnmount(): void {
Expand All @@ -360,8 +388,11 @@ export class StatementsPage extends React.Component<

onChangePage = (current: number): void => {
const { pagination } = this.state;
this.setState({ pagination: { ...pagination, current } });
this.props.onPageChanged != null && this.props.onPageChanged(current);
this.setState(prevState => ({
...prevState,
pagination: { ...pagination, current },
}));
this.props.onPageChanged(current);
};

onSubmitSearchField = (search: string): void => {
Expand Down Expand Up @@ -522,6 +553,14 @@ export class StatementsPage extends React.Component<
);
};

onChangeLimit = (newLimit: number): void => {
this.setState(prevState => ({ ...prevState, limit: newLimit }));
};

onChangeReqSort = (newSort: SqlStatsSortType): void => {
this.setState(prevState => ({ ...prevState, reqSortSetting: newSort }));
};

renderStatements = (regions: string[]): React.ReactElement => {
const { pagination, filters, activeFilters } = this.state;
const {
Expand Down Expand Up @@ -692,12 +731,30 @@ export class StatementsPage extends React.Component<
/>
</PageConfigItem>
<PageConfigItem className={commonStyles("separator")}>
<Dropdown items={limitOptions} onChange={this.onChangeLimit}>
Limit: {this.state.limit ?? "N/A"}
</Dropdown>
</PageConfigItem>
<PageConfigItem>
<Dropdown
items={stmtRequestSortOptions}
onChange={this.onChangeReqSort}
>
Sort By: {getSortLabel(this.state.reqSortSetting)}
</Dropdown>
</PageConfigItem>
<PageConfigItem>
<TimeScaleDropdown
options={timeScale1hMinOptions}
currentScale={this.props.timeScale}
currentScale={this.state.timeScale}
setTimeScale={this.changeTimeScale}
/>
</PageConfigItem>
<PageConfigItem>
<Button size="small" onClick={this.updateRequestParams}>
Submit Request
</Button>
</PageConfigItem>
{hasAdminRole && (
<PageConfigItem
className={`${commonStyles("separator")} ${cx(
Expand Down
Expand Up @@ -15,7 +15,11 @@ import { Dispatch } from "redux";
import { AppState, uiConfigActions } from "src/store";
import { actions as statementDiagnosticsActions } from "src/store/statementDiagnostics";
import { actions as analyticsActions } from "src/store/analytics";
import { actions as localStorageActions } from "src/store/localStorage";
import {
actions as localStorageActions,
updateStmtsPageLimitAction,
updateStmsPageReqSortAction,
} from "src/store/localStorage";
import { actions as sqlStatsActions } from "src/store/sqlStats";
import { actions as nodesActions } from "../store/nodes";
import {
Expand All @@ -39,13 +43,17 @@ import {
selectSearch,
selectStatementsLastUpdated,
} from "./statementsPage.selectors";
import {
selectStmtsPageLimit,
selectStmtsPageReqSort,
} from "../store/utils/selectors";
import {
selectIsTenant,
selectHasViewActivityRedactedRole,
selectHasAdminRole,
} from "../store/uiConfig";
import { nodeRegionsByIDSelector } from "../store/nodes";
import { StatementsRequest } from "src/api/statementsApi";
import { SqlStatsSortType, StatementsRequest } from "src/api/statementsApi";
import { TimeScale } from "../timeScaleDropdown";
import { cockroach, google } from "@cockroachlabs/crdb-protobuf-client";

Expand Down Expand Up @@ -83,6 +91,8 @@ export const ConnectedStatementsPage = withRouter(
lastUpdated: selectStatementsLastUpdated(state),
statementsError: selectStatementsLastError(state),
totalFingerprints: selectTotalFingerprints(state),
limit: selectStmtsPageLimit(state),
reqSortSetting: selectStmtsPageReqSort(state),
}),
(dispatch: Dispatch) => ({
refreshStatements: (req: StatementsRequest) =>
Expand Down Expand Up @@ -226,6 +236,10 @@ export const ConnectedStatementsPage = withRouter(
selectedColumns.length === 0 ? " " : selectedColumns.join(","),
}),
),
onChangeLimit: (limit: number) =>
dispatch(updateStmtsPageLimitAction(limit)),
onChangeReqSort: (sort: SqlStatsSortType) =>
dispatch(updateStmsPageReqSortAction(sort)),
}),
)(StatementsPage),
);

0 comments on commit 595c55b

Please sign in to comment.