diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/types.ts b/pkg/ui/workspaces/cluster-ui/src/insights/types.ts
index fc860e48b28d..72ff8786d7c0 100644
--- a/pkg/ui/workspaces/cluster-ui/src/insights/types.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/insights/types.ts
@@ -200,7 +200,7 @@ export const highContentionInsight = (
const description = `This ${execType} waited on other ${execType}s to execute for ${waitDuration}.`;
return {
name: InsightNameEnum.highContention,
- label: "High Contention",
+ label: InsightEnumToLabel.get(InsightNameEnum.highContention),
description: description,
tooltipDescription:
description + ` Click the ${execType} execution ID to see more details.`,
@@ -219,7 +219,7 @@ export const slowExecutionInsight = (
const description = `This ${execType} took longer than ${threshold} to execute.`;
return {
name: InsightNameEnum.slowExecution,
- label: "Slow Execution",
+ label: InsightEnumToLabel.get(InsightNameEnum.slowExecution),
description: description,
tooltipDescription:
description + ` Click the ${execType} execution ID to see more details.`,
@@ -233,7 +233,7 @@ export const planRegressionInsight = (execType: InsightExecEnum): Insight => {
`search conditions, or a change in the database schema.`;
return {
name: InsightNameEnum.planRegression,
- label: "Plan Regression",
+ label: InsightEnumToLabel.get(InsightNameEnum.planRegression),
description: description,
tooltipDescription:
description + ` Click the ${execType} execution ID to see more details.`,
@@ -246,7 +246,7 @@ export const suboptimalPlanInsight = (execType: InsightExecEnum): Insight => {
`due to outdated statistics or missing indexes.`;
return {
name: InsightNameEnum.suboptimalPlan,
- label: "Suboptimal Plan",
+ label: InsightEnumToLabel.get(InsightNameEnum.suboptimalPlan),
description: description,
tooltipDescription:
description + ` Click the ${execType} execution ID to see more details.`,
@@ -259,7 +259,7 @@ export const highRetryCountInsight = (execType: InsightExecEnum): Insight => {
`'sql.insights.high_retry_count.threshold' cluster setting.`;
return {
name: InsightNameEnum.highRetryCount,
- label: "High Retry Count",
+ label: InsightEnumToLabel.get(InsightNameEnum.highRetryCount),
description: description,
tooltipDescription:
description + ` Click the ${execType} execution ID to see more details.`,
@@ -272,7 +272,7 @@ export const failedExecutionInsight = (execType: InsightExecEnum): Insight => {
`saturation, or syntax errors.`;
return {
name: InsightNameEnum.failedExecution,
- label: "Failed Execution",
+ label: InsightEnumToLabel.get(InsightNameEnum.failedExecution),
description: description,
tooltipDescription:
description + ` Click the ${execType} execution ID to see more details.`,
@@ -310,7 +310,18 @@ export const InsightExecOptions = new Map([
[InsightExecEnum.STATEMENT.toString(), "Statement Executions"],
]);
-export type WorkloadInsightEventFilters = Pick;
+export const InsightEnumToLabel = new Map([
+ [InsightNameEnum.highContention.toString(), "High Contention"],
+ [InsightNameEnum.slowExecution.toString(), "Slow Execution"],
+ [InsightNameEnum.suboptimalPlan.toString(), "Suboptimal Plan"],
+ [InsightNameEnum.highRetryCount.toString(), "High Retry Count"],
+ [InsightNameEnum.failedExecution.toString(), "Failed Execution"],
+]);
+
+export type WorkloadInsightEventFilters = Pick<
+ Filters,
+ "app" | "workloadInsightType"
+>;
export type SchemaInsightEventFilters = Pick<
Filters,
diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/utils.ts b/pkg/ui/workspaces/cluster-ui/src/insights/utils.ts
index 52627d177516..bdf21c33d3bf 100644
--- a/pkg/ui/workspaces/cluster-ui/src/insights/utils.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/insights/utils.ts
@@ -63,6 +63,19 @@ export const filterTransactionInsights = (
} else {
filteredTransactions = filteredTransactions.filter(txn => !isInternal(txn));
}
+ if (filters.workloadInsightType && filters.workloadInsightType.length > 0) {
+ const workloadInsightTypes = filters.workloadInsightType
+ .toString()
+ .split(",");
+
+ filteredTransactions = filteredTransactions.filter(transaction =>
+ workloadInsightTypes.some(workloadType =>
+ transaction.insights.some(
+ txnInsight => workloadType === txnInsight.label,
+ ),
+ ),
+ );
+ }
if (search) {
search = search.toLowerCase();
@@ -205,6 +218,19 @@ export const filterStatementInsights = (
stmt => !isInternal(stmt.application),
);
}
+ if (filters.workloadInsightType && filters.workloadInsightType.length > 0) {
+ const workloadInsightTypes = filters.workloadInsightType
+ .toString()
+ .split(",");
+
+ filteredStatements = filteredStatements.filter(statement =>
+ workloadInsightTypes.some(workloadType =>
+ statement.insights.some(
+ stmtInsight => workloadType === stmtInsight.label,
+ ),
+ ),
+ );
+ }
if (search) {
search = search.toLowerCase();
filteredStatements = filteredStatements.filter(
diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsView.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsView.tsx
index c444e2f4736a..2675ba1c69d4 100644
--- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsView.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/statementInsights/statementInsightsView.tsx
@@ -20,6 +20,7 @@ import { PageConfig, PageConfigItem } from "src/pageConfig/pageConfig";
import { Search } from "src/search/search";
import {
calculateActiveFilters,
+ defaultFilters,
Filter,
getFullFiltersAsStringRecord,
} from "src/queryFilter/filter";
@@ -62,6 +63,7 @@ const sortableTableCx = classNames.bind(sortableTableStyles);
export type StatementInsightsViewStateProps = {
statements: FlattenedStmtInsights;
statementsError: Error | null;
+ insightTypes: string[];
filters: WorkloadInsightEventFilters;
sortSetting: SortSetting;
selectedColumnNames: string[];
@@ -91,6 +93,7 @@ export const StatementInsightsView: React.FC = (
sortSetting,
statements,
statementsError,
+ insightTypes,
filters,
timeScale,
isLoading,
@@ -210,7 +213,8 @@ export const StatementInsightsView: React.FC = (
const clearFilters = () =>
onSubmitFilters({
- app: "",
+ app: defaultFilters.app,
+ workloadInsightType: defaultFilters.workloadInsightType,
});
const apps = getAppsFromStatementInsights(
@@ -253,6 +257,8 @@ export const StatementInsightsView: React.FC = (
onSubmitFilters={onSubmitFilters}
appNames={apps}
filters={filters}
+ workloadInsightTypes={insightTypes.sort()}
+ showWorkloadInsightTypes={true}
/>
@@ -294,7 +300,10 @@ export const StatementInsightsView: React.FC = (
renderNoResult={
0 && filteredStatements?.length === 0
+ (search?.length > 0 &&
+ filteredStatements?.length === 0) ||
+ (countActiveFilters > 0 &&
+ filteredStatements?.length === 0)
}
/>
}
diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsView.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsView.tsx
index 14b047277c44..01e4214f75dd 100644
--- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsView.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/transactionInsights/transactionInsightsView.tsx
@@ -20,6 +20,7 @@ import { PageConfig, PageConfigItem } from "src/pageConfig/pageConfig";
import { Search } from "src/search/search";
import {
calculateActiveFilters,
+ defaultFilters,
Filter,
getFullFiltersAsStringRecord,
} from "src/queryFilter/filter";
@@ -56,6 +57,7 @@ const sortableTableCx = classNames.bind(sortableTableStyles);
export type TransactionInsightsViewStateProps = {
transactions: MergedTxnInsightEvent[];
transactionsError: Error | null;
+ insightTypes: string[];
filters: WorkloadInsightEventFilters;
sortSetting: SortSetting;
isLoading?: boolean;
@@ -83,6 +85,7 @@ export const TransactionInsightsView: React.FC = (
sortSetting,
transactions,
transactionsError,
+ insightTypes,
filters,
timeScale,
isLoading,
@@ -194,7 +197,8 @@ export const TransactionInsightsView: React.FC = (
const clearFilters = () =>
onSubmitFilters({
- app: "",
+ app: defaultFilters.app,
+ workloadInsightType: defaultFilters.workloadInsightType,
});
const transactionInsights = transactions;
@@ -228,6 +232,8 @@ export const TransactionInsightsView: React.FC = (
onSubmitFilters={onSubmitFilters}
appNames={apps}
filters={filters}
+ workloadInsightTypes={insightTypes.sort()}
+ showWorkloadInsightTypes={true}
/>
@@ -265,7 +271,10 @@ export const TransactionInsightsView: React.FC = (
renderNoResult={
0 && filteredTransactions?.length === 0
+ (search?.length > 0 &&
+ filteredTransactions?.length === 0) ||
+ (countActiveFilters > 0 &&
+ filteredTransactions?.length === 0)
}
/>
}
diff --git a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/workloadInsightsPageConnected.tsx b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/workloadInsightsPageConnected.tsx
index 9adc51e9b01b..47bf11daf57c 100644
--- a/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/workloadInsightsPageConnected.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/insights/workloadInsights/workloadInsightsPageConnected.tsx
@@ -32,6 +32,7 @@ import {
selectExecutionInsights,
selectExecutionInsightsError,
selectExecutionInsightsLoading,
+ selectInsightTypes,
} from "src/store/insights/statementInsights";
import {
actions as transactionInsights,
@@ -52,6 +53,7 @@ const transactionMapStateToProps = (
): TransactionInsightsViewStateProps => ({
transactions: selectTransactionInsights(state),
transactionsError: selectTransactionInsightsError(state),
+ insightTypes: selectInsightTypes(),
filters: selectFilters(state),
sortSetting: selectSortSetting(state),
timeScale: selectTimeScale(state),
@@ -64,6 +66,7 @@ const statementMapStateToProps = (
): StatementInsightsViewStateProps => ({
statements: selectExecutionInsights(state),
statementsError: selectExecutionInsightsError(state),
+ insightTypes: selectInsightTypes(),
filters: selectFilters(state),
sortSetting: selectSortSetting(state),
selectedColumnNames: selectColumns(state),
diff --git a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.spec.tsx
index 3bab1b4b9173..b0bc8814f000 100644
--- a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.spec.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.spec.tsx
@@ -17,6 +17,7 @@ describe("Test filter functions", (): void => {
app: "",
timeNumber: "0",
timeUnit: "seconds",
+ executionStatus: "",
fullScan: false,
sqlType: "",
database: "",
@@ -25,6 +26,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "",
nodes: "",
username: "",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString("");
expect(resultFilters).toEqual(expectedFilters);
@@ -36,6 +38,7 @@ describe("Test filter functions", (): void => {
app: "$ internal",
timeNumber: "1",
timeUnit: "milliseconds",
+ executionStatus: "",
fullScan: true,
sqlType: "DML",
database: "movr",
@@ -44,6 +47,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "idle",
nodes: "n1,n2",
username: "root",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString(
"app=%24+internal&timeNumber=1&timeUnit=milliseconds&fullScan=true&sqlType=DML&database=movr&sessionStatus=idle&username=root®ions=us-central&nodes=n1,n2&schemaInsightType=Drop+Unused+Index",
@@ -56,6 +60,7 @@ describe("Test filter functions", (): void => {
app: "",
timeNumber: "0",
timeUnit: "seconds",
+ executionStatus: "",
fullScan: true,
sqlType: "",
database: "",
@@ -64,6 +69,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "",
nodes: "",
username: "",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString("fullScan=true");
expect(resultFilters).toEqual(expectedFilters);
@@ -74,6 +80,7 @@ describe("Test filter functions", (): void => {
app: "",
timeNumber: "0",
timeUnit: "seconds",
+ executionStatus: "",
fullScan: false,
sqlType: "",
database: "",
@@ -82,6 +89,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "",
nodes: "",
username: "",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString("fullScan=false");
expect(resultFilters).toEqual(expectedFilters);
@@ -92,6 +100,7 @@ describe("Test filter functions", (): void => {
app: "",
timeNumber: "0",
timeUnit: "seconds",
+ executionStatus: "",
fullScan: false,
sqlType: "",
database: "",
@@ -100,6 +109,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "open",
nodes: "",
username: "",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString("sessionStatus=open");
expect(resultFilters).toEqual(expectedFilters);
@@ -110,6 +120,7 @@ describe("Test filter functions", (): void => {
app: "",
timeNumber: "0",
timeUnit: "seconds",
+ executionStatus: "",
fullScan: false,
sqlType: "",
database: "",
@@ -118,6 +129,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "idle",
nodes: "",
username: "",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString("sessionStatus=idle");
expect(resultFilters).toEqual(expectedFilters);
@@ -128,6 +140,7 @@ describe("Test filter functions", (): void => {
app: "",
timeNumber: "0",
timeUnit: "seconds",
+ executionStatus: "",
fullScan: false,
sqlType: "",
database: "",
@@ -136,6 +149,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "closed",
nodes: "",
username: "",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString("sessionStatus=closed");
expect(resultFilters).toEqual(expectedFilters);
@@ -146,6 +160,7 @@ describe("Test filter functions", (): void => {
app: "",
timeNumber: "0",
timeUnit: "seconds",
+ executionStatus: "",
fullScan: false,
sqlType: "",
database: "",
@@ -154,6 +169,7 @@ describe("Test filter functions", (): void => {
sessionStatus: "",
nodes: "",
username: "",
+ workloadInsightType: "",
};
const resultFilters = getFiltersFromQueryString(
"schemaInsightType=Drop+Unused+Index",
diff --git a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx
index 0b8bed3d2b81..532667689066 100644
--- a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx
@@ -39,7 +39,9 @@ interface QueryFilter {
dbNames?: string[];
usernames?: string[];
sessionStatuses?: string[];
+ executionStatuses?: string[];
schemaInsightTypes?: string[];
+ workloadInsightTypes?: string[];
regions?: string[];
nodes?: string[];
hideAppNames?: boolean;
@@ -47,7 +49,9 @@ interface QueryFilter {
showDB?: boolean;
showUsername?: boolean;
showSessionStatus?: boolean;
+ showExecutionStatus?: boolean;
showSchemaInsightTypes?: boolean;
+ showWorkloadInsightTypes?: boolean;
showSqlType?: boolean;
showScan?: boolean;
showRegions?: boolean;
@@ -70,7 +74,9 @@ export interface Filters extends Record {
nodes?: string;
username?: string;
sessionStatus?: string;
+ executionStatus?: string;
schemaInsightType?: string;
+ workloadInsightType?: string;
}
const timeUnit = [
@@ -91,6 +97,8 @@ export const defaultFilters: Required = {
username: "",
sessionStatus: "",
schemaInsightType: "",
+ workloadInsightType: "",
+ executionStatus: "",
};
// getFullFiltersObject returns Filters with every field defined as
@@ -253,6 +261,9 @@ export const inactiveFiltersState: Required> = {
regions: "",
sessionStatus: "",
nodes: "",
+ workloadInsightType: "",
+ schemaInsightType: "",
+ executionStatus: "",
};
export const calculateActiveFilters = (filters: Filters): number => {
@@ -385,7 +396,9 @@ export class Filter extends React.Component {
dbNames,
usernames,
sessionStatuses,
+ executionStatuses,
schemaInsightTypes,
+ workloadInsightTypes,
regions,
nodes,
activeFilters,
@@ -399,7 +412,9 @@ export class Filter extends React.Component {
hideTimeLabel,
showUsername,
showSessionStatus,
+ showExecutionStatus,
showSchemaInsightTypes,
+ showWorkloadInsightTypes,
} = this.props;
const dropdownArea = hide ? hidden : dropdown;
const customStyles = {
@@ -530,6 +545,32 @@ export class Filter extends React.Component {
);
+ const executionStatusOptions = showExecutionStatus
+ ? executionStatuses.map(executionStatus => ({
+ label: executionStatus,
+ value: executionStatus,
+ isSelected: this.isOptionSelected(
+ executionStatus,
+ filters.executionStatus,
+ ),
+ }))
+ : [];
+ const executionStatusValue = executionStatusOptions.filter(option =>
+ filters.executionStatus.split(",").includes(option.label),
+ );
+ const executionStatusFilter = (
+
+ );
+
const schemaInsightTypeOptions = showSchemaInsightTypes
? schemaInsightTypes.map(schemaInsight => ({
label: schemaInsight,
@@ -556,6 +597,34 @@ export class Filter extends React.Component {
);
+ const workloadInsightTypeOptions = showWorkloadInsightTypes
+ ? workloadInsightTypes.map(workloadInsight => ({
+ label: workloadInsight,
+ value: workloadInsight,
+ isSelected: this.isOptionSelected(
+ workloadInsight,
+ filters.workloadInsightType,
+ ),
+ }))
+ : [];
+ const workloadInsightTypeValue = workloadInsightTypeOptions.filter(
+ option => {
+ return filters.workloadInsightType.split(",").includes(option.label);
+ },
+ );
+ const workloadInsightTypeFilter = (
+
+
Workload Insight Type
+
+
+ );
+
const regionsOptions = showRegions
? regions.map(region => ({
label: region,
@@ -671,7 +740,9 @@ export class Filter extends React.Component {
{showDB ? dbFilter : ""}
{showUsername ? usernameFilter : ""}
{showSessionStatus ? sessionStatusFilter : ""}
+ {showExecutionStatus ? executionStatusFilter : ""}
{showSchemaInsightTypes ? schemaInsightTypeFilter : ""}
+ {showWorkloadInsightTypes ? workloadInsightTypeFilter : ""}
{showSqlType ? sqlTypeFilter : ""}
{showRegions ? regionsFilter : ""}
{showNodes ? nodesFilter : ""}
diff --git a/pkg/ui/workspaces/cluster-ui/src/queryFilter/utils.ts b/pkg/ui/workspaces/cluster-ui/src/queryFilter/utils.ts
index d5e605963042..3775166c5524 100644
--- a/pkg/ui/workspaces/cluster-ui/src/queryFilter/utils.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/queryFilter/utils.ts
@@ -47,6 +47,7 @@ export function getRecentStatementFiltersFromURL(
const appFilters = {
app: filters.app,
+ executionStatus: filters.executionStatus,
};
// If every entry is null, there were no active stmt filters. Return null.
@@ -63,6 +64,7 @@ export function getRecentTransactionFiltersFromURL(
const appFilters = {
app: filters.app,
+ executionStatus: filters.executionStatus,
};
// If every entry is null, there were no active stmt filters. Return null.
@@ -79,6 +81,7 @@ export function getWorkloadInsightEventFiltersFromURL(
const appFilters = {
app: filters.app,
+ workloadInsightType: filters.workloadInsightType,
};
// If every entry is null, there were no active filters. Return null.
diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx
index 8789ceae29dd..260c1a109812 100644
--- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/execTableCommon.tsx
@@ -113,7 +113,8 @@ export const executionsTableTitles: ExecutionsTableTitleType = {
{`The status of the ${execType}'s execution. If
"Preparing", the ${execType} is being parsed and planned.
If "Executing", the ${execType} is currently being
- executed.`}
+ executed. If "Waiting", the ${execType} is currently
+ experiencing contention.`}
}
>
diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.spec.ts b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.spec.ts
index 9c8025b5bf6f..97377cf932c2 100644
--- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.spec.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.spec.ts
@@ -16,6 +16,7 @@ import {
SessionStatusType,
RecentStatementFilters,
RecentTransactionFilters,
+ ExecutionStatus,
} from "./types";
import * as protos from "@cockroachlabs/crdb-protobuf-client";
import moment from "moment";
@@ -50,7 +51,7 @@ const defaultActiveStatement: RecentStatement = {
transactionID: "transactionID",
sessionID: "sessionID",
query: defaultActiveQuery.sql,
- status: "Executing",
+ status: ExecutionStatus.Executing,
start: MOCK_START_TIME,
elapsedTime: moment.duration(60),
application: "test",
@@ -87,7 +88,7 @@ function makeActiveTxn(
lastAutoRetryReason: null,
priority: "Normal",
statementCount: 5,
- status: "Executing",
+ status: ExecutionStatus.Executing,
...props,
};
}
@@ -231,7 +232,7 @@ describe("test activeStatementUtils", () => {
fail(`stmt user should be foo or bar, got ${stmt.user}`);
}
// expect(stmt.transactionID).toBe(defaultActiveStatement.transactionID);
- expect(stmt.status).toBe("Executing");
+ expect(stmt.status).toBe(ExecutionStatus.Executing);
expect(stmt.start.unix()).toBe(
TimestampToMoment(defaultActiveQuery.start).unix(),
);
@@ -302,7 +303,7 @@ describe("test activeStatementUtils", () => {
expect(txn.application).toBe(
sessionsResponse.sessions[i].application_name,
);
- expect(txn.status).toBe("Executing");
+ expect(txn.status).toBe(ExecutionStatus.Executing);
expect(txn.query).toBeTruthy();
expect(txn.start.unix()).toBe(
TimestampToMoment(defaultActiveQuery.start).unix(),
diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.ts b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.ts
index c48cd6bbf350..c88d6a0eac04 100644
--- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementUtils.ts
@@ -65,6 +65,16 @@ export function filterRecentStatements(
);
}
+ if (filters.executionStatus) {
+ filteredStatements = filteredStatements.filter(
+ (statement: RecentStatement) => {
+ const executionStatuses = filters.executionStatus.toString().split(",");
+
+ return executionStatuses.includes(statement.status);
+ },
+ );
+ }
+
if (search) {
const searchCaseInsensitive = search.toLowerCase();
filteredStatements = filteredStatements.filter(stmt =>
@@ -113,8 +123,8 @@ export function getRecentExecutionsFromSessions(
query: query.sql?.length > 0 ? query.sql : query.sql_no_constants,
status:
query.phase === ActiveStatementPhase.EXECUTING
- ? "Executing"
- : "Preparing",
+ ? ExecutionStatus.Executing
+ : ExecutionStatus.Preparing,
start: TimestampToMoment(query.start),
elapsedTime: DurationToMomentDuration(query.elapsed_time),
application: session.application_name,
@@ -206,6 +216,14 @@ export function filterRecentTransactions(
filteredTxns = filteredTxns.filter(txn => !isInternal(txn));
}
+ if (filters.executionStatus) {
+ filteredTxns = filteredTxns.filter((txn: RecentTransaction) => {
+ const executionStatuses = filters.executionStatus.toString().split(",");
+
+ return executionStatuses.includes(txn.status);
+ });
+ }
+
if (search) {
const searchCaseInsensitive = search.toLowerCase();
filteredTxns = filteredTxns.filter(txn =>
diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementsSection.tsx b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementsSection.tsx
index b291f044af91..d5d6938cbfc8 100644
--- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementsSection.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentStatementsSection.tsx
@@ -101,7 +101,10 @@ export const RecentStatementsSection: React.FC<
onChangeSortSetting={onChangeSortSetting}
renderNoResult={
0 && statements.length > 0}
+ isEmptySearchResults={
+ (search?.length > 0 || activeFilters > 0) &&
+ statements.length === 0
+ }
statementView={StatementViewType.ACTIVE}
/>
}
diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsSection.tsx b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsSection.tsx
index 715ae90910f6..8c17547df108 100644
--- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsSection.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/recentTransactionsSection.tsx
@@ -11,8 +11,8 @@
import React, { useMemo } from "react";
import classNames from "classnames/bind";
import {
- RecentStatementFilters,
RecentTransaction,
+ RecentTransactionFilters,
} from "src/recentExecutions/types";
import ColumnsSelector, {
SelectOption,
@@ -36,7 +36,7 @@ import { SortedTable } from "src/sortedtable";
const sortableTableCx = classNames.bind(sortableTableStyles);
type RecentTransactionsSectionProps = {
- filters: RecentStatementFilters;
+ filters: RecentTransactionFilters;
isTenant?: boolean;
pagination: ISortedTablePagination;
search: string;
@@ -100,7 +100,10 @@ export const RecentTransactionsSection: React.FC<
onChangeSortSetting={onChangeSortSetting}
renderNoResult={
0 && transactions.length > 0}
+ isEmptySearchResults={
+ (search?.length > 0 || activeFilters > 0) &&
+ transactions.length === 0
+ }
transactionView={TransactionViewType.ACTIVE}
/>
}
diff --git a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/types.ts b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/types.ts
index 799f2f1315c4..931a23c8ac84 100644
--- a/pkg/ui/workspaces/cluster-ui/src/recentExecutions/types.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/recentExecutions/types.ts
@@ -16,7 +16,11 @@ export type SessionsResponse =
protos.cockroach.server.serverpb.ListSessionsResponse;
export type ActiveStatementResponse =
protos.cockroach.server.serverpb.ActiveQuery;
-export type ExecutionStatus = "Waiting" | "Executing" | "Preparing";
+export enum ExecutionStatus {
+ Waiting = "Waiting",
+ Executing = "Executing",
+ Preparing = "Preparing",
+}
export type ExecutionType = "statement" | "transaction";
export const ActiveStatementPhase =
diff --git a/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutions.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutions.selectors.ts
index 20e8f9e7cf21..db7b7da641ab 100644
--- a/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutions.selectors.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutions.selectors.ts
@@ -9,7 +9,11 @@
// licenses/APL.txt.
import { createSelector } from "reselect";
-import { RecentExecutions } from "src/recentExecutions/types";
+import {
+ RecentExecutions,
+ RecentTransaction,
+ ExecutionStatus,
+} from "src/recentExecutions/types";
import { AppState } from "src/store";
import { selectRecentExecutionsCombiner } from "src/selectors/recentExecutionsCommon.selectors";
import { selectExecutionID } from "src/selectors/common";
@@ -39,6 +43,14 @@ export const selectRecentStatements = createSelector(
(executions: RecentExecutions) => executions.statements,
);
+export const selectExecutionStatus = () => {
+ const execStatuses: string[] = [];
+ for (const execStatus in ExecutionStatus) {
+ execStatuses.push(execStatus);
+ }
+ return execStatuses;
+};
+
export const selecteRecentStatement = createSelector(
selectRecentStatements,
selectExecutionID,
diff --git a/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutionsCommon.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutionsCommon.selectors.ts
index 631c9b5ecae6..72ff0e46e7b4 100644
--- a/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutionsCommon.selectors.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutionsCommon.selectors.ts
@@ -8,8 +8,11 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
-import { RouteComponentProps } from "react-router";
-import { RecentExecutions, SessionsResponse } from "src/recentExecutions/types";
+import {
+ ExecutionStatus,
+ RecentExecutions,
+ SessionsResponse,
+} from "src/recentExecutions/types";
import { ClusterLocksResponse } from "src/api";
import {
getRecentExecutionsFromSessions,
@@ -32,12 +35,18 @@ export const selectRecentExecutionsCombiner = (
return {
statements: execs.statements.map(s => ({
...s,
- status: waitTimeByTxnID[s.transactionID] != null ? "Waiting" : s.status,
+ status:
+ waitTimeByTxnID[s.transactionID] != null
+ ? ExecutionStatus.Waiting
+ : s.status,
timeSpentWaiting: waitTimeByTxnID[s.transactionID],
})),
transactions: execs.transactions.map(t => ({
...t,
- status: waitTimeByTxnID[t.transactionID] != null ? "Waiting" : t.status,
+ status:
+ waitTimeByTxnID[t.transactionID] != null
+ ? ExecutionStatus.Waiting
+ : t.status,
timeSpentWaiting: waitTimeByTxnID[t.transactionID],
})),
};
diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsPage.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsPage.selectors.ts
index fd7393cf7dfc..e3198d9642d4 100644
--- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsPage.selectors.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsPage.selectors.ts
@@ -20,6 +20,7 @@ import {
import {
selectRecentStatements,
selectAppName,
+ selectExecutionStatus,
} from "src/selectors/recentExecutions.selectors";
import { actions as localStorageActions } from "src/store/localStorage";
import { actions as sessionsActions } from "src/store/sessions";
@@ -54,6 +55,7 @@ export const mapStateToRecentStatementsPageProps = (
selectedColumns: selectColumns(state),
sortSetting: selectSortSetting(state),
filters: selectFilters(state),
+ executionStatus: selectExecutionStatus(),
internalAppNamePrefix: selectAppName(state),
isTenant: selectIsTenant(state),
});
diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx
index 385716db859c..c52dcfbe78b2 100644
--- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx
@@ -28,6 +28,7 @@ import {
} from "../recentExecutions/recentStatementUtils";
import {
calculateActiveFilters,
+ defaultFilters,
getFullFiltersAsStringRecord,
} from "../queryFilter/filter";
import { RecentStatementsSection } from "../recentExecutions/recentStatementsSection";
@@ -53,6 +54,7 @@ export type RecentStatementsViewStateProps = {
sortSetting: SortSetting;
sessionsError: Error | null;
filters: RecentStatementFilters;
+ executionStatus: string[];
internalAppNamePrefix: string;
isTenant?: boolean;
};
@@ -70,6 +72,7 @@ export const RecentStatementsView: React.FC = ({
statements,
sessionsError,
filters,
+ executionStatus,
internalAppNamePrefix,
isTenant,
}: RecentStatementsViewProps) => {
@@ -154,7 +157,11 @@ export const RecentStatementsView: React.FC = ({
};
const clearSearch = () => onSubmitSearch("");
- const clearFilters = () => onSubmitFilters({ app: inactiveFiltersState.app });
+ const clearFilters = () =>
+ onSubmitFilters({
+ app: defaultFilters.app,
+ executionStatus: defaultFilters.executionStatus,
+ });
const apps = getAppsFromRecentExecutions(statements, internalAppNamePrefix);
const countActiveFilters = calculateActiveFilters(filters);
@@ -180,6 +187,8 @@ export const RecentStatementsView: React.FC = ({
diff --git a/pkg/ui/workspaces/cluster-ui/src/store/insights/statementInsights/statementInsights.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/store/insights/statementInsights/statementInsights.selectors.ts
index dbaae343c2fc..b2bba4137b80 100644
--- a/pkg/ui/workspaces/cluster-ui/src/store/insights/statementInsights/statementInsights.selectors.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/store/insights/statementInsights/statementInsights.selectors.ts
@@ -17,6 +17,7 @@ import {
selectStatementInsightDetailsCombiner,
} from "src/selectors/insightsCommon.selectors";
import { selectID } from "src/selectors/common";
+import { InsightEnumToLabel } from "src/insights";
export const selectExecutionInsights = createSelector(
(state: AppState) => state.adminUI.executionInsights?.data,
@@ -32,6 +33,14 @@ export const selectStatementInsightDetails = createSelector(
selectStatementInsightDetailsCombiner,
);
+export const selectInsightTypes = () => {
+ const insights: string[] = [];
+ InsightEnumToLabel.forEach(insight => {
+ insights.push(insight);
+ });
+ return insights;
+};
+
export const selectColumns = createSelector(
localStorageSelector,
localStorage =>
diff --git a/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts
index 194336e5a7c6..e91f13dbc49f 100644
--- a/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts
+++ b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts
@@ -80,10 +80,12 @@ const defaultSortSettingSchemaInsights: SortSetting = {
const defaultFiltersActiveExecutions = {
app: "",
+ executionStatus: "",
};
const defaultFiltersInsights = {
app: "",
+ workloadInsightType: "",
};
const defaultFiltersSchemaInsights = {
diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsPage.selectors.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsPage.selectors.tsx
index 7c70af49cc8d..ddd8cc718a83 100644
--- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsPage.selectors.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsPage.selectors.tsx
@@ -20,6 +20,7 @@ import {
import {
selectAppName,
selectRecentTransactions,
+ selectExecutionStatus,
} from "src/selectors/recentExecutions.selectors";
import { actions as localStorageActions } from "src/store/localStorage";
import { actions as sessionsActions } from "src/store/sessions";
@@ -54,6 +55,7 @@ export const mapStateToRecentTransactionsPageProps = (
selectedColumns: selectColumns(state),
sortSetting: selectSortSetting(state),
filters: selectFilters(state),
+ executionStatus: selectExecutionStatus(),
internalAppNamePrefix: selectAppName(state),
isTenant: selectIsTenant(state),
});
diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx
index fad329cc748b..de0a9b4b526b 100644
--- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx
+++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx
@@ -52,6 +52,7 @@ export type RecentTransactionsViewStateProps = {
transactions: RecentTransaction[];
sessionsError: Error | null;
filters: RecentTransactionFilters;
+ executionStatus: string[];
sortSetting: SortSetting;
internalAppNamePrefix: string;
isTenant?: boolean;
@@ -73,12 +74,14 @@ export const RecentTransactionsView: React.FC = ({
transactions,
sessionsError,
filters,
+ executionStatus,
internalAppNamePrefix,
}: RecentTransactionsViewProps) => {
const [pagination, setPagination] = useState({
current: 1,
pageSize: 20,
});
+
const history = useHistory();
const [search, setSearch] = useState(
queryByName(history.location, RECENT_TXN_SEARCH_PARAM),
@@ -155,7 +158,11 @@ export const RecentTransactionsView: React.FC = ({
};
const clearSearch = () => onSubmitSearch("");
- const clearFilters = () => onSubmitFilters({ app: inactiveFiltersState.app });
+ const clearFilters = () =>
+ onSubmitFilters({
+ app: inactiveFiltersState.app,
+ executionStatus: inactiveFiltersState.executionStatus,
+ });
const apps = getAppsFromRecentExecutions(transactions, internalAppNamePrefix);
const countActiveFilters = calculateActiveFilters(filters);
@@ -181,6 +188,8 @@ export const RecentTransactionsView: React.FC = ({
diff --git a/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts b/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts
index 713d3cd8c595..2b9ee48615f6 100644
--- a/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts
+++ b/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts
@@ -15,6 +15,7 @@ import {
getRecentTransaction,
getContentionDetailsFromLocksAndTxns,
selectExecutionID,
+ ExecutionStatus,
} from "@cockroachlabs/cluster-ui";
import { createSelector } from "reselect";
import { CachedDataReducerState } from "src/redux/apiReducers";
@@ -37,6 +38,14 @@ export const selectRecentStatements = createSelector(
(executions: RecentExecutions) => executions.statements,
);
+export const selectExecutionStatus = () => {
+ const execStatuses: string[] = [];
+ for (const execStatus in ExecutionStatus) {
+ execStatuses.push(execStatus);
+ }
+ return execStatuses;
+};
+
export const selectRecentStatement = createSelector(
selectRecentStatements,
selectExecutionID,
diff --git a/pkg/ui/workspaces/db-console/src/views/insights/insightsSelectors.ts b/pkg/ui/workspaces/db-console/src/views/insights/insightsSelectors.ts
index 976ae8c78687..8af84e880ff8 100644
--- a/pkg/ui/workspaces/db-console/src/views/insights/insightsSelectors.ts
+++ b/pkg/ui/workspaces/db-console/src/views/insights/insightsSelectors.ts
@@ -23,13 +23,15 @@ import {
selectTxnInsightsCombiner,
TxnContentionInsightDetails,
selectTxnInsightDetailsCombiner,
+ InsightEnumToLabel,
} from "@cockroachlabs/cluster-ui";
export const filtersLocalSetting = new LocalSetting<
AdminUIState,
WorkloadInsightEventFilters
>("filters/InsightsPage", (state: AdminUIState) => state.localSettings, {
- app: "",
+ app: defaultFilters.app,
+ workloadInsightType: defaultFilters.workloadInsightType,
});
export const sortSettingLocalSetting = new LocalSetting<
@@ -106,6 +108,14 @@ export const selectExecutionInsights = createSelector((state: AdminUIState) => {
} else return null;
}, selectFlattenedStmtInsightsCombiner);
+export const selectInsightTypes = () => {
+ const insights: string[] = [];
+ InsightEnumToLabel.forEach(insight => {
+ insights.push(insight);
+ });
+ return insights;
+};
+
export const selectStatementInsightDetails = createSelector(
selectExecutionInsights,
selectID,
diff --git a/pkg/ui/workspaces/db-console/src/views/insights/workloadInsightsPage.tsx b/pkg/ui/workspaces/db-console/src/views/insights/workloadInsightsPage.tsx
index 3a0e3b132026..cfa9750f06b1 100644
--- a/pkg/ui/workspaces/db-console/src/views/insights/workloadInsightsPage.tsx
+++ b/pkg/ui/workspaces/db-console/src/views/insights/workloadInsightsPage.tsx
@@ -32,6 +32,7 @@ import {
selectTransactionInsights,
selectExecutionInsightsLoading,
selectTransactionInsightsLoading,
+ selectInsightTypes,
} from "src/views/insights/insightsSelectors";
import { bindActionCreators } from "redux";
import { LocalSetting } from "src/redux/localsettings";
@@ -53,6 +54,7 @@ const transactionMapStateToProps = (
): TransactionInsightsViewStateProps => ({
transactions: selectTransactionInsights(state),
transactionsError: state.cachedData?.transactionInsights?.lastError,
+ insightTypes: selectInsightTypes(),
filters: filtersLocalSetting.selector(state),
sortSetting: sortSettingLocalSetting.selector(state),
timeScale: selectTimeScale(state),
@@ -66,6 +68,7 @@ const statementMapStateToProps = (
statements: selectExecutionInsights(state),
statementsError: state.cachedData?.executionInsights?.lastError,
filters: filtersLocalSetting.selector(state),
+ insightTypes: selectInsightTypes(),
sortSetting: sortSettingLocalSetting.selector(state),
selectedColumnNames:
insightStatementColumnsLocalSetting.selectorToArray(state),
diff --git a/pkg/ui/workspaces/db-console/src/views/statements/recentStatementsSelectors.tsx b/pkg/ui/workspaces/db-console/src/views/statements/recentStatementsSelectors.tsx
index dc19a9cc51a0..468e2cf53c27 100644
--- a/pkg/ui/workspaces/db-console/src/views/statements/recentStatementsSelectors.tsx
+++ b/pkg/ui/workspaces/db-console/src/views/statements/recentStatementsSelectors.tsx
@@ -13,7 +13,11 @@ import {
defaultFilters,
SortSetting,
} from "@cockroachlabs/cluster-ui";
-import { selectRecentStatements, selectAppName } from "src/selectors";
+import {
+ selectRecentStatements,
+ selectAppName,
+ selectExecutionStatus,
+} from "src/selectors";
import { refreshLiveWorkload } from "src/redux/apiReducers";
import { LocalSetting } from "src/redux/localsettings";
import { AdminUIState } from "src/redux/state";
@@ -27,7 +31,10 @@ const selectedColumnsLocalSetting = new LocalSetting<
null,
);
-const defaultActiveFilters = { app: defaultFilters.app };
+const defaultActiveFilters = {
+ app: defaultFilters.app,
+ executionStatus: defaultFilters.executionStatus,
+};
const filtersLocalSetting = new LocalSetting<
AdminUIState,
@@ -49,6 +56,7 @@ export const mapStateToRecentStatementViewProps = (state: AdminUIState) => ({
selectedColumns: selectedColumnsLocalSetting.selectorToArray(state),
sortSetting: sortSettingLocalSetting.selector(state),
statements: selectRecentStatements(state),
+ executionStatus: selectExecutionStatus(),
sessionsError: state.cachedData?.sessions.lastError,
internalAppNamePrefix: selectAppName(state),
});
diff --git a/pkg/ui/workspaces/db-console/src/views/transactions/recentTransactionsSelectors.tsx b/pkg/ui/workspaces/db-console/src/views/transactions/recentTransactionsSelectors.tsx
index 65706c651d54..5fa7ea3a5c50 100644
--- a/pkg/ui/workspaces/db-console/src/views/transactions/recentTransactionsSelectors.tsx
+++ b/pkg/ui/workspaces/db-console/src/views/transactions/recentTransactionsSelectors.tsx
@@ -14,7 +14,11 @@ import {
defaultFilters,
SortSetting,
} from "@cockroachlabs/cluster-ui";
-import { selectAppName, selectRecentTransactions } from "src/selectors";
+import {
+ selectAppName,
+ selectRecentTransactions,
+ selectExecutionStatus,
+} from "src/selectors";
import { refreshLiveWorkload } from "src/redux/apiReducers";
import { LocalSetting } from "src/redux/localsettings";
import { AdminUIState } from "src/redux/state";
@@ -28,7 +32,10 @@ const transactionsColumnsLocalSetting = new LocalSetting<
null,
);
-const defaultActiveTxnFilters = { app: defaultFilters.app };
+const defaultActiveTxnFilters = {
+ app: defaultFilters.app,
+ executionStatus: defaultFilters.executionStatus,
+};
const filtersLocalSetting = new LocalSetting<
AdminUIState,
@@ -50,6 +57,7 @@ export const mapStateToRecentTransactionsPageProps = (state: AdminUIState) => ({
transactions: selectRecentTransactions(state),
sessionsError: state.cachedData?.sessions.lastError,
filters: filtersLocalSetting.selector(state),
+ executionStatus: selectExecutionStatus(),
sortSetting: sortSettingLocalSetting.selector(state),
internalAppNamePrefix: selectAppName(state),
});