diff --git a/pkg/ui/workspaces/cluster-ui/src/api/clusterLocksApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/clusterLocksApi.ts index d45570f7216d..9a8eea4611e7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/clusterLocksApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/clusterLocksApi.ts @@ -14,6 +14,9 @@ import { LONG_TIMEOUT, SqlExecutionRequest, sqlResultsAreEmpty, + LARGE_RESULT_SIZE, + SqlApiResponse, + formatApiResult, } from "./sqlApi"; export type ClusterLockState = { @@ -48,7 +51,9 @@ type ClusterLockColumns = { * getClusterLocksState returns information from crdb_internal.cluster_locks * regarding the state of range locks in the cluster. */ -export function getClusterLocksState(): Promise { +export function getClusterLocksState(): Promise< + SqlApiResponse +> { const request: SqlExecutionRequest = { statements: [ { @@ -71,12 +76,16 @@ WHERE ], execute: true, timeout: LONG_TIMEOUT, + max_result_size: LARGE_RESULT_SIZE, }; return executeInternalSql(request).then(result => { if (sqlResultsAreEmpty(result)) { - // No data. - return []; + return formatApiResult( + [], + result.error, + "retrieving cluster locks information", + ); } const locks: Record = {}; @@ -117,6 +126,10 @@ WHERE } }); - return Object.values(locks); + return formatApiResult( + Object.values(locks), + result.error, + "retrieving luster locks information", + ); }); } diff --git a/pkg/ui/workspaces/cluster-ui/src/api/contentionApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/contentionApi.ts index 13665eca94e7..454afc505933 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/contentionApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/contentionApi.ts @@ -12,9 +12,10 @@ import { executeInternalSql, LARGE_RESULT_SIZE, LONG_TIMEOUT, - sqlApiErrorMessage, + SqlApiResponse, SqlExecutionRequest, sqlResultsAreEmpty, + formatApiResult, } from "./sqlApi"; import { ContentionDetails } from "src/insights"; import moment from "moment"; @@ -44,7 +45,7 @@ export type ContentionResponseColumns = { export async function getContentionDetailsApi( filters?: ContentionFilters, -): Promise { +): Promise> { const request: SqlExecutionRequest = { statements: [ { @@ -57,16 +58,13 @@ export async function getContentionDetailsApi( }; const result = await executeInternalSql(request); - if (result.error) { - throw new Error( - `Error while retrieving insights information: ${sqlApiErrorMessage( - result.error.message, - )}`, - ); - } if (sqlResultsAreEmpty(result)) { - return []; + return formatApiResult( + [], + result.error, + "retrieving contention information", + ); } const contentionDetails: ContentionDetails[] = []; @@ -96,7 +94,11 @@ export async function getContentionDetailsApi( }); }); - return contentionDetails; + return formatApiResult( + contentionDetails, + result.error, + "retrieving insights information", + ); } function isFiltered(filters: ContentionFilters): boolean { diff --git a/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts index fbb28f153f1f..2bdf2a885406 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts @@ -180,12 +180,14 @@ async function addStmtContentionInfoApi( continue; } - event.contentionEvents = await getContentionDetailsApi({ + const contentionResults = await getContentionDetailsApi({ waitingTxnID: null, waitingStmtID: event.statementExecutionID, start: null, end: null, }); + + event.contentionEvents = contentionResults.results; } } diff --git a/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts index 2aa02f3f24b4..445f1dcbe5c9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts @@ -170,12 +170,13 @@ export async function getTxnInsightsContentionDetailsApi( // Get contention results for requested transaction. - const contentionResults = await getContentionDetailsApi({ + const contentionResponse = await getContentionDetailsApi({ waitingTxnID: req.txnExecutionID, waitingStmtID: null, start: null, end: null, }); + const contentionResults = contentionResponse.results; if (contentionResults.length === 0) { return; diff --git a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx index 1a370e9bd844..2486362ae226 100644 --- a/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/queryFilter/filter.tsx @@ -113,7 +113,7 @@ export function getFullFiltersAsStringRecord( filterKey in partialFilters && partialFilters[filterKey] !== inactiveFiltersState[filterKey] ) { - filters[filterKey] = partialFilters[filterKey].toString(); + filters[filterKey] = partialFilters[filterKey]?.toString(); return; } filters[filterKey] = null; 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 db7b7da641ab..e6927cdb77cb 100644 --- a/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutions.selectors.ts +++ b/pkg/ui/workspaces/cluster-ui/src/selectors/recentExecutions.selectors.ts @@ -30,7 +30,10 @@ import { const selectSessions = (state: AppState) => state.adminUI.sessions?.data; const selectClusterLocks = (state: AppState) => - state.adminUI.clusterLocks?.data; + state.adminUI.clusterLocks?.data?.results; + +export const selectClusterLocksMaxApiSizeReached = (state: AppState) => + !!state.adminUI.clusterLocks?.data?.maxSizeReached; export const selectRecentExecutions = createSelector( selectSessions, 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 e3198d9642d4..62fc664b66d4 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsPage.selectors.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsPage.selectors.ts @@ -21,6 +21,7 @@ import { selectRecentStatements, selectAppName, selectExecutionStatus, + selectClusterLocksMaxApiSizeReached, } from "src/selectors/recentExecutions.selectors"; import { actions as localStorageActions } from "src/store/localStorage"; import { actions as sessionsActions } from "src/store/sessions"; @@ -58,6 +59,7 @@ export const mapStateToRecentStatementsPageProps = ( executionStatus: selectExecutionStatus(), internalAppNamePrefix: selectAppName(state), isTenant: selectIsTenant(state), + maxSizeApiReached: selectClusterLocksMaxApiSizeReached(state), }); export const mapDispatchToRecentStatementsPageProps = ( diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx b/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx index 07fc610ede85..aac3683ddb64 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/recentStatementsView.tsx @@ -30,12 +30,13 @@ import { calculateActiveFilters, defaultFilters, getFullFiltersAsStringRecord, -} from "../queryFilter/filter"; +} from "../queryFilter"; import { RecentStatementsSection } from "../recentExecutions/recentStatementsSection"; import { queryByName, syncHistory } from "src/util/query"; import { getTableSortFromURL } from "../sortedtable/getTableSortFromURL"; import { getRecentStatementFiltersFromURL } from "src/queryFilter/utils"; import { Pagination } from "src/pagination"; +import { InlineAlert } from "@cockroachlabs/ui-components"; import styles from "./statementsPage.module.scss"; @@ -58,6 +59,7 @@ export type RecentStatementsViewStateProps = { executionStatus: string[]; internalAppNamePrefix: string; isTenant?: boolean; + maxSizeApiReached?: boolean; }; export type RecentStatementsViewProps = RecentStatementsViewStateProps & @@ -76,6 +78,7 @@ export const RecentStatementsView: React.FC = ({ executionStatus, internalAppNamePrefix, isTenant, + maxSizeApiReached, }: RecentStatementsViewProps) => { const [pagination, setPagination] = useState({ current: 1, @@ -231,6 +234,17 @@ export const RecentStatementsView: React.FC = ({ total={filteredStatements?.length} onChange={onChangePage} /> + {maxSizeApiReached && ( + + Not all contention events are displayed because the maximum + number of contention events was reached in the console. + + } + /> + )} diff --git a/pkg/ui/workspaces/cluster-ui/src/store/clusterLocks/clusterLocks.reducer.ts b/pkg/ui/workspaces/cluster-ui/src/store/clusterLocks/clusterLocks.reducer.ts index cb01e88ed350..0090b3ececf3 100644 --- a/pkg/ui/workspaces/cluster-ui/src/store/clusterLocks/clusterLocks.reducer.ts +++ b/pkg/ui/workspaces/cluster-ui/src/store/clusterLocks/clusterLocks.reducer.ts @@ -10,10 +10,10 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { DOMAIN_NAME, noopReducer } from "../utils"; -import { ClusterLocksResponse } from "src/api"; +import { ClusterLocksResponse, SqlApiResponse } from "src/api"; export type ClusterLocksReqState = { - data: ClusterLocksResponse; + data: SqlApiResponse; lastError: Error; valid: boolean; }; @@ -28,7 +28,10 @@ const clusterLocksSlice = createSlice({ name: `${DOMAIN_NAME}/clusterLocks`, initialState, reducers: { - received: (state, action: PayloadAction) => { + received: ( + state, + action: PayloadAction>, + ) => { state.data = action.payload; state.valid = true; state.lastError = null; 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 ddd8cc718a83..339ef580e160 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsPage.selectors.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsPage.selectors.tsx @@ -21,6 +21,7 @@ import { selectAppName, selectRecentTransactions, selectExecutionStatus, + selectClusterLocksMaxApiSizeReached, } from "src/selectors/recentExecutions.selectors"; import { actions as localStorageActions } from "src/store/localStorage"; import { actions as sessionsActions } from "src/store/sessions"; @@ -58,6 +59,7 @@ export const mapStateToRecentTransactionsPageProps = ( executionStatus: selectExecutionStatus(), internalAppNamePrefix: selectAppName(state), isTenant: selectIsTenant(state), + maxSizeApiReached: selectClusterLocksMaxApiSizeReached(state), }); export const mapDispatchToRecentTransactionsPageProps = ( diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx index fe3cd2a33d0c..6b13bc980756 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/recentTransactionsView.tsx @@ -28,9 +28,9 @@ import { calculateActiveFilters, Filter, getFullFiltersAsStringRecord, -} from "../queryFilter/filter"; + inactiveFiltersState, +} from "../queryFilter"; import { getAppsFromRecentExecutions } from "../recentExecutions/recentStatementUtils"; -import { inactiveFiltersState } from "../queryFilter/filter"; import { RecentTransactionsSection } from "src/recentExecutions/recentTransactionsSection"; import { Pagination } from "src/pagination"; @@ -39,6 +39,7 @@ import { queryByName, syncHistory } from "src/util/query"; import { getTableSortFromURL } from "src/sortedtable/getTableSortFromURL"; import { getRecentTransactionFiltersFromURL } from "src/queryFilter/utils"; import { filterRecentTransactions } from "../recentExecutions/recentStatementUtils"; +import { InlineAlert } from "@cockroachlabs/ui-components"; const cx = classNames.bind(styles); export type RecentTransactionsViewDispatchProps = { @@ -57,6 +58,7 @@ export type RecentTransactionsViewStateProps = { sortSetting: SortSetting; internalAppNamePrefix: string; isTenant?: boolean; + maxSizeApiReached?: boolean; }; export type RecentTransactionsViewProps = RecentTransactionsViewStateProps & @@ -78,6 +80,7 @@ export const RecentTransactionsView: React.FC = ({ filters, executionStatus, internalAppNamePrefix, + maxSizeApiReached, }: RecentTransactionsViewProps) => { const [pagination, setPagination] = useState({ current: 1, @@ -234,6 +237,17 @@ export const RecentTransactionsView: React.FC = ({ total={filteredTransactions?.length} onChange={onChangePage} /> + {maxSizeApiReached && ( + + Not all contention events are displayed because the maximum + number of contention events was reached in the console. + + } + /> + )} diff --git a/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts b/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts index 0f1807bcfa04..b5b5cf15bf85 100644 --- a/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts +++ b/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts @@ -556,7 +556,9 @@ export interface APIReducersState { statementDiagnosticsReports: CachedDataReducerState; userSQLRoles: CachedDataReducerState; hotRanges: PaginatedCachedDataReducerState; - clusterLocks: CachedDataReducerState; + clusterLocks: CachedDataReducerState< + clusterUiApi.SqlApiResponse + >; stmtInsights: CachedDataReducerState< clusterUiApi.SqlApiResponse >; diff --git a/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts b/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts index 2b9ee48615f6..3012a28c7cc3 100644 --- a/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts +++ b/pkg/ui/workspaces/db-console/src/selectors/recentExecutionsSelectors.ts @@ -25,7 +25,13 @@ import { SessionsResponseMessage } from "src/util/api"; const selectSessions = (state: AdminUIState) => state.cachedData.sessions?.data; const selectClusterLocks = (state: AdminUIState) => - state.cachedData.clusterLocks?.data; + state.cachedData.clusterLocks?.data?.results; + +export const selectClusterLocksMaxApiSizeReached = ( + state: AdminUIState, +): boolean => { + return !!state.cachedData.clusterLocks?.data?.maxSizeReached; +}; export const selectRecentExecutions = createSelector( selectSessions, 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 468e2cf53c27..2747dc814082 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/recentStatementsSelectors.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/recentStatementsSelectors.tsx @@ -17,6 +17,7 @@ import { selectRecentStatements, selectAppName, selectExecutionStatus, + selectClusterLocksMaxApiSizeReached, } from "src/selectors"; import { refreshLiveWorkload } from "src/redux/apiReducers"; import { LocalSetting } from "src/redux/localsettings"; @@ -59,6 +60,7 @@ export const mapStateToRecentStatementViewProps = (state: AdminUIState) => ({ executionStatus: selectExecutionStatus(), sessionsError: state.cachedData?.sessions.lastError, internalAppNamePrefix: selectAppName(state), + maxSizeApiReached: selectClusterLocksMaxApiSizeReached(state), }); export const recentStatementsViewActions = { 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 5fa7ea3a5c50..ea4dc701f011 100644 --- a/pkg/ui/workspaces/db-console/src/views/transactions/recentTransactionsSelectors.tsx +++ b/pkg/ui/workspaces/db-console/src/views/transactions/recentTransactionsSelectors.tsx @@ -18,6 +18,7 @@ import { selectAppName, selectRecentTransactions, selectExecutionStatus, + selectClusterLocksMaxApiSizeReached, } from "src/selectors"; import { refreshLiveWorkload } from "src/redux/apiReducers"; import { LocalSetting } from "src/redux/localsettings"; @@ -60,6 +61,7 @@ export const mapStateToRecentTransactionsPageProps = (state: AdminUIState) => ({ executionStatus: selectExecutionStatus(), sortSetting: sortSettingLocalSetting.selector(state), internalAppNamePrefix: selectAppName(state), + maxSizeApiReached: selectClusterLocksMaxApiSizeReached(state), }); // This object is just for convenience so we don't need to supply dispatch to