Skip to content

Commit

Permalink
ui/cluster-ui: insights cleanup
Browse files Browse the repository at this point in the history
Closes #88847

This commit cleans up some insights related components in
preparation for the next version of transaction insights.

- Rework txn insights api calls to use async/await as
the promise chain became quite long.
- Reduce number of api calls made in txn insight details,
these likely won't be needed once we record insights for
txns meeting the insights contention threshold.
- Introduce shared selector combiner functions for insights
components where possible.
- Convert transaction insight details redux field to a
map of ids to details objects, similar to stmt details,
instead of storing a single txn insight details object. Each
key is cleared at an interval of 5 minutes.
- Populate insights field from api response in selector
instead of component level.
- Convert txn insight details component from class component
to functional component

Release note: None
  • Loading branch information
xinhaoz committed Nov 14, 2022
1 parent 84fdcfb commit c6aadeb
Show file tree
Hide file tree
Showing 22 changed files with 712 additions and 678 deletions.
455 changes: 198 additions & 257 deletions pkg/ui/workspaces/cluster-ui/src/api/insightsApi.ts

Large diffs are not rendered by default.

102 changes: 52 additions & 50 deletions pkg/ui/workspaces/cluster-ui/src/insights/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
WorkloadInsightEventFilters,
} from "./types";

export const getTransactionInsights = (
const getTransactionInsights = (
eventState: TransactionInsightEventState,
): Insight[] => {
const insights: Insight[] = [];
Expand All @@ -57,24 +57,16 @@ export const getTransactionInsights = (
export const getTransactionInsightsFromDetails = (
eventState: TransactionInsightEventDetailsState,
): Insight[] => {
const insights: Insight[] = [];
if (eventState) {
InsightTypes.forEach(insight => {
if (
insight(eventState.execType, eventState.contentionThreshold).name ==
eventState.insightName
) {
insights.push(
insight(
eventState.execType,
eventState.contentionThreshold,
eventState.totalContentionTime,
),
);
}
});
if (!eventState) {
return [];
}
return insights;
return InsightTypes.map(insight =>
insight(
eventState.execType,
eventState.contentionThreshold,
eventState.totalContentionTime,
),
).filter(insight => insight.name === eventState.insightName);
};

export function getInsightsFromState(
Expand Down Expand Up @@ -107,21 +99,21 @@ export function getInsightsFromState(
return insightEvents;
}

// This function adds the insights field to TransactionInsightEventDetailsResponse
export function getTransactionInsightEventDetailsFromState(
insightEventDetailsResponse: TransactionInsightEventDetailsResponse,
): TransactionInsightEventDetails {
let insightEventDetails: TransactionInsightEventDetails = null;
const insightsForEventDetails = getTransactionInsightsFromDetails(
insightEventDetailsResponse,
);
if (insightsForEventDetails.length > 0) {
const { insightName, ...resp } = insightEventDetailsResponse;
insightEventDetails = {
...resp,
insights: insightsForEventDetails,
};
if (!insightsForEventDetails?.length) {
return null;
}
return insightEventDetails;
const { insightName, ...resp } = insightEventDetailsResponse;
return {
...resp,
insights: insightsForEventDetails,
};
}

export const filterTransactionInsights = (
Expand Down Expand Up @@ -329,37 +321,47 @@ export function getAppsFromStatementInsights(
}

export function populateStatementInsightsFromProblemAndCauses(
statements: StatementInsightEvent[],
): StatementInsightEvent[] {
if (!statements || statements?.length === 0) {
statements: StatementInsights,
): StatementInsights {
if (!statements?.length) {
return [];
}
const stmts: StatementInsightEvent[] = [];
statements.forEach(statement => {
const stmt = Object.assign({}, statement);

const stmtsWithInsights: StatementInsights = statements.map(statement => {
// TODO(ericharmeling,todd): Replace these strings when using the insights protos.
if (statement.problem === "SlowExecution") {
if (statement.causes?.length === 0) {
stmt.insights = [
const insights: Insight[] = [];
switch (statement.problem) {
case "SlowExecution":
statement.causes?.forEach(cause =>
insights.push(
getInsightFromProblem(cause, InsightExecEnum.STATEMENT),
),
);

if (insights.length === 0) {
insights.push(
getInsightFromProblem(
InsightNameEnum.slowExecution,
InsightExecEnum.STATEMENT,
),
);
}
break;

case "FailedExecution":
insights.push(
getInsightFromProblem(
InsightNameEnum.slowExecution,
InsightNameEnum.failedExecution,
InsightExecEnum.STATEMENT,
),
];
} else {
stmt.insights = statement.causes?.map(x =>
getInsightFromProblem(x, InsightExecEnum.STATEMENT),
);
}
} else if (statement.problem === "FailedExecution") {
stmt.insights = [
getInsightFromProblem(
InsightNameEnum.failedExecution,
InsightExecEnum.STATEMENT,
),
];
break;

default:
}
stmts.push(stmt);

return { ...statement, insights };
});
return stmts;

return stmtsWithInsights;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import "antd/lib/row/style";
import { Button } from "src/button";
import { Loading } from "src/loading";
import { SqlBox, SqlBoxSize } from "src/sql";
import { getMatchParamByName } from "src/util/query";
import { getMatchParamByName, executionIdAttr } from "src/util";
import { StatementInsightEvent } from "../types";
import { InsightsError } from "../insightsErrorComponent";
import classNames from "classnames/bind";
Expand Down Expand Up @@ -98,7 +98,7 @@ export const StatementInsightDetails: React.FC<
}
};

const executionID = getMatchParamByName(match, "id");
const executionID = getMatchParamByName(match, executionIdAttr);

useEffect(() => {
if (insightEventDetails == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
InsightRecommendation,
StatementInsightEvent,
} from "../types";
import { populateStatementInsightsFromProblemAndCauses } from "../utils";
import classNames from "classnames/bind";
import { CockroachCloudContext } from "../../contexts";

Expand All @@ -47,73 +46,69 @@ const insightsTableData = (
): InsightRecommendation[] => {
if (!insightDetails) return [];

const recs: InsightRecommendation[] = [];
let rec: InsightRecommendation;
const execDetails: executionDetails = {
statement: insightDetails.query,
fingerprintID: insightDetails.statementFingerprintID,
retries: insightDetails.retries,
};
insightDetails.insights.forEach(insight => {
switch (insight.name) {
case InsightNameEnum.highContention:
rec = {
type: "HighContention",
execution: execDetails,
details: {
duration: insightDetails.elapsedTimeMillis,
description: insight.description,
},
};
break;
case InsightNameEnum.failedExecution:
rec = {
type: "FailedExecution",
};
break;
case InsightNameEnum.highRetryCount:
rec = {
type: "HighRetryCount",
execution: execDetails,
details: {
description: insight.description,
},
};
break;
case InsightNameEnum.planRegression:
rec = {
type: "PlanRegression",
execution: execDetails,
details: {
description: insight.description,
},
};
break;
case InsightNameEnum.suboptimalPlan:
rec = {
type: "SuboptimalPlan",
database: insightDetails.databaseName,
execution: {
...execDetails,
indexRecommendations: insightDetails.indexRecommendations,
},
details: {
description: insight.description,
},
};
break;
default:
rec = {
type: "Unknown",
details: {
duration: insightDetails.elapsedTimeMillis,
description: insight.description,
},
};
break;
}
recs.push(rec);
});

const recs: InsightRecommendation[] = insightDetails.insights?.map(
insight => {
switch (insight.name) {
case InsightNameEnum.highContention:
return {
type: "HighContention",
execution: execDetails,
details: {
duration: insightDetails.elapsedTimeMillis,
description: insight.description,
},
};
case InsightNameEnum.failedExecution:
return {
type: "FailedExecution",
};
case InsightNameEnum.highRetryCount:
return {
type: "HighRetryCount",
execution: execDetails,
details: {
description: insight.description,
},
};
break;
case InsightNameEnum.planRegression:
return {
type: "PlanRegression",
execution: execDetails,
details: {
description: insight.description,
},
};
case InsightNameEnum.suboptimalPlan:
return {
type: "SuboptimalPlan",
database: insightDetails.databaseName,
execution: {
...execDetails,
indexRecommendations: insightDetails.indexRecommendations,
},
details: {
description: insight.description,
},
};
default:
return {
type: "Unknown",
details: {
duration: insightDetails.elapsedTimeMillis,
description: insight.description,
},
};
}
},
);

return recs;
};

Expand All @@ -132,11 +127,7 @@ export const StatementInsightDetailsOverviewTab: React.FC<
[isCockroachCloud],
);

const insightDetailsArr = useMemo(
() => populateStatementInsightsFromProblemAndCauses([insightEventDetails]),
[insightEventDetails],
);
const insightDetails = insightDetailsArr.length ? insightDetailsArr[0] : null;
const insightDetails = insightEventDetails;
const tableData = insightsTableData(insightDetails);

return (
Expand Down
Loading

0 comments on commit c6aadeb

Please sign in to comment.