Skip to content

Commit

Permalink
ui: show data for txn insights when max size reached
Browse files Browse the repository at this point in the history
Previously, when the sql api returned a max size reached
error, we were just showing the error, but not the data
that was also being returned.

This commit updates the Insights Workload > Transaction page
with the new behaviour.

Part Of: cockroachdb#96184
Release note (ui change): Still show data on the console
(with a warning) for Transaction Insights when we reach a
"max size exceed" error from the sql api.
  • Loading branch information
maryliag committed Feb 17, 2023
1 parent 09843a7 commit 7d279ad
Show file tree
Hide file tree
Showing 20 changed files with 166 additions and 77 deletions.
8 changes: 4 additions & 4 deletions pkg/ui/workspaces/cluster-ui/src/api/sqlApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ export type SqlExecutionErrorMessage = {
source: { file: string; line: number; function: "string" };
};

export type ApiResponse<ResultType> = {
export type SqlApiResponse<ResultType> = {
maxSizeReached: boolean;
results: Array<ResultType>;
results: ResultType;
};

export const SQL_API_PATH = "/api/v2/sql/";
Expand Down Expand Up @@ -165,15 +165,15 @@ export function sqlApiErrorMessage(message: string): string {
return message;
}

function isMaxSizeError(message: string): boolean {
export function isMaxSizeError(message: string): boolean {
return !!message?.includes("max result size exceeded");
}

export function formatApiResult(
results: Array<any>,
error: SqlExecutionErrorMessage,
errorMessageContext: string,
): ApiResponse<any> {
): SqlApiResponse<any> {
const maxSizeError = isMaxSizeError(error?.message);

if (error && !maxSizeError) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// licenses/APL.txt.

import {
ApiResponse,
SqlApiResponse,
executeInternalSql,
formatApiResult,
LARGE_RESULT_SIZE,
Expand Down Expand Up @@ -138,7 +138,7 @@ export const stmtInsightsByTxnExecutionQuery = (id: string): string => `

export async function getStmtInsightsApi(
req?: StmtInsightsReq,
): Promise<ApiResponse<StmtInsightEvent>> {
): Promise<SqlApiResponse<StmtInsightEvent[]>> {
const request: SqlExecutionRequest = {
statements: [
{
Expand Down
61 changes: 29 additions & 32 deletions pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@
// licenses/APL.txt.

import {
SqlApiResponse,
executeInternalSql,
formatApiResult,
INTERNAL_SQL_API_APP,
LARGE_RESULT_SIZE,
LONG_TIMEOUT,
sqlApiErrorMessage,
SqlExecutionRequest,
SqlExecutionResponse,
sqlResultsAreEmpty,
isMaxSizeError,
} from "./sqlApi";
import {
ContentionDetails,
Expand Down Expand Up @@ -129,11 +132,6 @@ function createStmtFingerprintToQueryMap(
return idToQuery;
}

export type TransactionContentionEventDetails = Omit<
TxnContentionInsightDetails,
"application" | "queries" | "blockingQueries"
>;

type PartialTxnContentionDetails = Omit<
TxnContentionInsightDetails,
"application" | "queries"
Expand All @@ -159,12 +157,6 @@ function formatTxnContentionDetailsResponse(
};
}

export type TxnContentionDetailsRequest = {
txnExecutionID?: string;
start?: moment.Moment;
end?: moment.Moment;
};

export async function getTxnInsightsContentionDetailsApi(
req: TxnInsightDetailsRequest,
): Promise<TxnContentionInsightDetails> {
Expand Down Expand Up @@ -411,30 +403,27 @@ export type TxnInsightsRequest = {
end?: moment.Moment;
};

export function getTxnInsightsApi(
export async function getTxnInsightsApi(
req?: TxnInsightsRequest,
): Promise<TxnInsightEvent[]> {
): Promise<SqlApiResponse<TxnInsightEvent[]>> {
const filters: TxnQueryFilters = {
start: req?.start,
end: req?.end,
execID: req?.txnExecutionID,
fingerprintID: req?.txnFingerprintID,
};
const request = makeInsightsSqlRequest([createTxnInsightsQuery(filters)]);
return executeInternalSql<TxnInsightsResponseRow>(request).then(result => {
if (result.error) {
throw new Error(
`Error while retrieving insights information: ${sqlApiErrorMessage(
result.error.message,
)}`,
);
}
const result = await executeInternalSql<TxnInsightsResponseRow>(request);

if (sqlResultsAreEmpty(result)) {
return [];
}
return result.execution.txn_results[0].rows.map(formatTxnInsightsRow);
});
if (sqlResultsAreEmpty(result)) {
return formatApiResult([], result.error, "retrieving insights information");
}

return formatApiResult(
result.execution.txn_results[0].rows.map(formatTxnInsightsRow),
result.error,
"retrieving insights information",
);
}

export type TxnInsightDetailsRequest = {
Expand All @@ -461,7 +450,7 @@ export type TxnInsightDetailsResponse = {

export async function getTxnInsightDetailsApi(
req: TxnInsightDetailsRequest,
): Promise<TxnInsightDetailsResponse> {
): Promise<SqlApiResponse<TxnInsightDetailsResponse>> {
// All queries in this request read from virtual tables, which is an
// expensive operation. To reduce the number of RPC fanouts, we have the
// caller specify which parts of the txn details we should return, since
Expand All @@ -481,6 +470,7 @@ export async function getTxnInsightDetailsApi(
statementsErr: null,
};

let maxSizeReached = false;
if (!req.excludeTxn) {
const request = makeInsightsSqlRequest([
createTxnInsightsQuery({
Expand All @@ -489,10 +479,12 @@ export async function getTxnInsightDetailsApi(
end: req?.end,
}),
]);

try {
const result = await executeInternalSql<TxnInsightsResponseRow>(request);
maxSizeReached = isMaxSizeError(result.error?.message);

if (result.error) {
if (result.error && !maxSizeReached) {
throw new Error(
`Error while retrieving insights information: ${sqlApiErrorMessage(
result.error.message,
Expand All @@ -517,14 +509,16 @@ export async function getTxnInsightDetailsApi(
]);

const result = await executeInternalSql<StmtInsightsResponseRow>(request);
const maxSizeStmtReached = isMaxSizeError(result.error?.message);

if (result.error) {
if (result.error && !maxSizeStmtReached) {
throw new Error(
`Error while retrieving insights information: ${sqlApiErrorMessage(
result.error.message,
)}`,
);
}
maxSizeReached = maxSizeReached || maxSizeStmtReached;

const stmts = result.execution.txn_results[0];
if (stmts.rows?.length) {
Expand All @@ -550,8 +544,11 @@ export async function getTxnInsightDetailsApi(
}

return {
txnExecutionID: req.txnExecutionID,
result: txnInsightDetails,
errors,
maxSizeReached: maxSizeReached,
results: {
txnExecutionID: req.txnExecutionID,
result: txnInsightDetails,
errors,
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ArrowLeft } from "@cockroachlabs/icons";
import { Tabs } from "antd";
import "antd/lib/col/style";
import "antd/lib/row/style";
import "antd/lib/tabs/style";
import { Button } from "src/button";
import { getMatchParamByName } from "src/util/query";
import { TxnInsightDetailsRequest, TxnInsightDetailsReqErrs } from "src/api";
Expand All @@ -25,14 +26,16 @@ import { idAttr } from "src/util";
import { TransactionInsightDetailsOverviewTab } from "./transactionInsightDetailsOverviewTab";
import { TransactionInsightsDetailsStmtsTab } from "./transactionInsightDetailsStmtsTab";
import { timeScaleRangeToObj } from "src/timeScaleDropdown/utils";

import "antd/lib/tabs/style";
import { InlineAlert } from "@cockroachlabs/ui-components";
import { insights } from "src/util";
import { Anchor } from "src/anchor";

export interface TransactionInsightDetailsStateProps {
insightDetails: TxnInsightDetails;
insightError: TxnInsightDetailsReqErrs | null;
timeScale?: TimeScale;
hasAdminRole: boolean;
maxSizeApiReached?: boolean;
}

export interface TransactionInsightDetailsDispatchProps {
Expand Down Expand Up @@ -65,6 +68,7 @@ export const TransactionInsightDetails: React.FC<
match,
hasAdminRole,
refreshUserSQLRoles,
maxSizeApiReached,
}) => {
const fetches = useRef<number>(0);
const executionID = getMatchParamByName(match, idAttr);
Expand Down Expand Up @@ -153,6 +157,7 @@ export const TransactionInsightDetails: React.FC<
contentionDetails={insightDetails.blockingContentionDetails}
setTimeScale={setTimeScale}
hasAdminRole={hasAdminRole}
maxApiSizeReached={maxSizeApiReached}
/>
</Tabs.TabPane>
{(insightDetails.txnDetails?.stmtExecutionIDs?.length ||
Expand All @@ -169,6 +174,20 @@ export const TransactionInsightDetails: React.FC<
error={insightError?.statementsErr}
statements={insightDetails?.statements}
/>
{maxSizeApiReached && (
<InlineAlert
intent="info"
title={
<>
Not all statements are displayed because the maximum
number of statements was reached in the console.&nbsp;
<Anchor href={insights} target="_blank">
Learn more
</Anchor>
</>
}
/>
)}
</Tabs.TabPane>
)}
</Tabs>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
selectTransactionInsightDetails,
selectTransactionInsightDetailsError,
actions,
selectTransactionInsightDetailsMaxSizeReached,
} from "src/store/insightDetails/transactionInsightDetails";
import { TimeScale } from "../../timeScaleDropdown";
import { actions as sqlStatsActions } from "../../store/sqlStats";
Expand All @@ -39,6 +40,10 @@ const mapStateToProps = (
insightError: insightError,
timeScale: selectTimeScale(state),
hasAdminRole: selectHasAdminRole(state),
maxSizeApiReached: selectTransactionInsightDetailsMaxSizeReached(
state,
props,
),
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type Props = {
hasAdminRole: boolean;
errors: TxnInsightDetailsReqErrs | null;
maxRequestsReached: boolean;
maxApiSizeReached: boolean;
};

export const TransactionInsightDetailsOverviewTab: React.FC<Props> = ({
Expand All @@ -78,9 +79,13 @@ export const TransactionInsightDetailsOverviewTab: React.FC<Props> = ({
const isCockroachCloud = useContext(CockroachCloudContext);

const queryFromStmts = statements?.map(s => s.query)?.join("\n");
const insightQueries = queryFromStmts?.length
let insightQueries = queryFromStmts?.length
? queryFromStmts
: txnDetails?.query ?? "Insight not found.";
if (maxRequestsReached) {
insightQueries = `${insightQueries} \n\nNot all statements are displayed because
the maximum number of statements was reached in the console.`;
}
const insightsColumns = makeInsightsColumns(
isCockroachCloud,
hasAdminRole,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ import styles from "src/statementsPage/statementsPage.module.scss";
import sortableTableStyles from "src/sortedtable/sortedtable.module.scss";
import { commonStyles } from "../../../common";
import { useFetchDataWithPolling } from "src/util/hooks";
import { InlineAlert } from "@cockroachlabs/ui-components";
import { insights } from "src/util";
import { Anchor } from "src/anchor";

const cx = classNames.bind(styles);
const sortableTableCx = classNames.bind(sortableTableStyles);
Expand All @@ -66,6 +69,7 @@ export type TransactionInsightsViewStateProps = {
isLoading?: boolean;
dropDownSelect?: React.ReactElement;
timeScale?: TimeScale;
maxSizeApiReached?: boolean;
};

export type TransactionInsightsViewDispatchProps = {
Expand Down Expand Up @@ -99,6 +103,7 @@ export const TransactionInsightsView: React.FC<TransactionInsightsViewProps> = (
onSortChange,
setTimeScale,
dropDownSelect,
maxSizeApiReached,
} = props;

const [pagination, setPagination] = useState<ISortedTablePagination>({
Expand Down Expand Up @@ -293,6 +298,20 @@ export const TransactionInsightsView: React.FC<TransactionInsightsViewProps> = (
total={filteredTransactions?.length}
onChange={onChangePage}
/>
{maxSizeApiReached && (
<InlineAlert
intent="info"
title={
<>
Not all insights are displayed because the maximum number of
insights was reached in the console.&nbsp;
<Anchor href={insights} target="_blank">
Learn more
</Anchor>
</>
}
/>
)}
</div>
</Loading>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
selectFilters,
selectSortSetting,
selectTransactionInsightsLoading,
selectTransactionInsightsMaxApiReached,
} from "src/store/insights/transactionInsights";
import { Dispatch } from "redux";
import { TimeScale } from "../../timeScaleDropdown";
Expand All @@ -63,6 +64,7 @@ const transactionMapStateToProps = (
sortSetting: selectSortSetting(state),
timeScale: selectTimeScale(state),
isLoading: selectTransactionInsightsLoading(state),
maxSizeApiReached: selectTransactionInsightsMaxApiReached(state),
});

const statementMapStateToProps = (
Expand Down
Loading

0 comments on commit 7d279ad

Please sign in to comment.