diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx index c00140b4b5f9..cf776311a094 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.stories.tsx @@ -31,6 +31,7 @@ const history = H.createHashHistory(); const withLoadingIndicator: DatabaseDetailsPageProps = { loading: true, loaded: false, + lastError: undefined, name: randomName(), tables: [], viewMode: ViewMode.Tables, @@ -60,6 +61,7 @@ const withLoadingIndicator: DatabaseDetailsPageProps = { const withoutData: DatabaseDetailsPageProps = { loading: false, loaded: true, + lastError: null, name: randomName(), tables: [], viewMode: ViewMode.Tables, @@ -89,6 +91,7 @@ const withoutData: DatabaseDetailsPageProps = { const withData: DatabaseDetailsPageProps = { loading: false, loaded: true, + lastError: null, name: randomName(), tables: _.map(Array(42), _item => { const roles = _.uniq( @@ -103,6 +106,7 @@ const withData: DatabaseDetailsPageProps = { details: { loading: false, loaded: true, + lastError: null, columnCount: _.random(5, 42), indexCount: _.random(1, 6), userCount: roles.length, @@ -114,6 +118,7 @@ const withData: DatabaseDetailsPageProps = { stats: { loading: false, loaded: true, + lastError: null, replicationSizeInBytes: _.random(1000.0) * 1024 ** _.random(1, 2), rangeCount: _.random(50, 500), nodesByRegionString: diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx index a4311ee7f8f0..f8298c22f35f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsPage/databaseDetailsPage.tsx @@ -12,7 +12,6 @@ import React from "react"; import { Link, RouteComponentProps } from "react-router-dom"; import { Tooltip } from "antd"; import classNames from "classnames/bind"; -import _ from "lodash"; import { Breadcrumbs } from "src/breadcrumbs"; import { Dropdown, DropdownOption } from "src/dropdown"; @@ -38,6 +37,8 @@ import { } from "src/transactionsPage/transactionsPageClasses"; import { Moment } from "moment"; import { DATE_FORMAT } from "src/util/format"; +import LoadingError from "../sqlActivity/errorComponent"; +import { Loading } from "../loading"; const cx = classNames.bind(styles); const sortableTableCx = classNames.bind(sortableTableStyles); @@ -54,6 +55,7 @@ const sortableTableCx = classNames.bind(sortableTableStyles); // interface DatabaseDetailsPageData { // loading: boolean; // loaded: boolean; +// lastError: Error; // name: string; // sortSettingTables: SortSetting; // sortSettingGrants: SortSetting; @@ -63,6 +65,7 @@ const sortableTableCx = classNames.bind(sortableTableStyles); // details: { // DatabaseDetailsPageDataTableDetails // loading: boolean; // loaded: boolean; +// lastError: Error; // columnCount: number; // indexCount: number; // userCount: number; @@ -72,6 +75,7 @@ const sortableTableCx = classNames.bind(sortableTableStyles); // stats: { // DatabaseDetailsPageDataTableStats // loading: boolean; // loaded: boolean; +// lastError: Error; // replicationSizeInBytes: number; // rangeCount: number; // nodesByRegionString: string; @@ -81,6 +85,7 @@ const sortableTableCx = classNames.bind(sortableTableStyles); export interface DatabaseDetailsPageData { loading: boolean; loaded: boolean; + lastError: Error; name: string; tables: DatabaseDetailsPageDataTable[]; sortSettingTables: SortSetting; @@ -98,6 +103,7 @@ export interface DatabaseDetailsPageDataTable { export interface DatabaseDetailsPageDataTableDetails { loading: boolean; loaded: boolean; + lastError: Error; columnCount: number; indexCount: number; userCount: number; @@ -109,6 +115,7 @@ export interface DatabaseDetailsPageDataTableDetails { export interface DatabaseDetailsPageDataTableStats { loading: boolean; loaded: boolean; + lastError: Error; replicationSizeInBytes: number; rangeCount: number; nodesByRegionString?: string; @@ -134,6 +141,8 @@ export enum ViewMode { interface DatabaseDetailsPageState { pagination: ISortedTablePagination; + lastStatsError: Error; + lastDetailsError: Error; } class DatabaseSortedTable extends SortedTable {} @@ -149,6 +158,8 @@ export class DatabaseDetailsPage extends React.Component< current: 1, pageSize: 20, }, + lastDetailsError: null, + lastStatsError: null, }; const { history } = this.props; @@ -199,16 +210,49 @@ export class DatabaseDetailsPage extends React.Component< } private refresh(): void { - if (!this.props.loaded && !this.props.loading) { + if ( + !this.props.loaded && + !this.props.loading && + this.props.lastError === undefined + ) { return this.props.refreshDatabaseDetails(this.props.name); } - _.forEach(this.props.tables, table => { - if (!table.details.loaded && !table.details.loading) { + let lastDetailsError: Error; + let lastStatsError: Error; + this.props.tables.forEach(table => { + if (table.details.lastError !== undefined) { + lastDetailsError = table.details.lastError; + } + if ( + lastDetailsError && + this.state.lastDetailsError?.name != lastDetailsError?.name + ) { + this.setState({ lastDetailsError: lastDetailsError }); + } + + if ( + !table.details.loaded && + !table.details.loading && + table.details.lastError === undefined + ) { return this.props.refreshTableDetails(this.props.name, table.name); } - if (!table.stats.loaded && !table.stats.loading) { + if (table.stats.lastError !== undefined) { + lastStatsError = table.stats.lastError; + } + if ( + lastStatsError && + this.state.lastStatsError?.name != lastStatsError?.name + ) { + this.setState({ lastStatsError: lastStatsError }); + } + if ( + !table.stats.loaded && + !table.stats.loading && + table.stats.lastError === undefined + ) { return this.props.refreshTableStats(this.props.name, table.name); } }); @@ -259,6 +303,16 @@ export class DatabaseDetailsPage extends React.Component< } } + checkInfoAvailable = ( + error: Error, + cell: React.ReactNode, + ): React.ReactNode => { + if (error) { + return "(unavailable)"; + } + return cell; + }; + private columnsForTablesViewMode(): ColumnDescriptor< DatabaseDetailsPageDataTable >[] { @@ -291,7 +345,11 @@ export class DatabaseDetailsPage extends React.Component< Replication Size ), - cell: table => format.Bytes(table.stats.replicationSizeInBytes), + cell: table => + this.checkInfoAvailable( + table.stats.lastError, + format.Bytes(table.stats.replicationSizeInBytes), + ), sort: table => table.stats.replicationSizeInBytes, className: cx("database-table__col-size"), name: "replicationSize", @@ -305,7 +363,11 @@ export class DatabaseDetailsPage extends React.Component< Ranges ), - cell: table => table.stats.rangeCount, + cell: table => + this.checkInfoAvailable( + table.stats.lastError, + table.stats.rangeCount, + ), sort: table => table.stats.rangeCount, className: cx("database-table__col-range-count"), name: "rangeCount", @@ -319,7 +381,11 @@ export class DatabaseDetailsPage extends React.Component< Columns ), - cell: table => table.details.columnCount, + cell: table => + this.checkInfoAvailable( + table.stats.lastError, + table.details.columnCount, + ), sort: table => table.details.columnCount, className: cx("database-table__col-column-count"), name: "columnCount", @@ -333,7 +399,11 @@ export class DatabaseDetailsPage extends React.Component< Indexes ), - cell: table => table.details.indexCount, + cell: table => + this.checkInfoAvailable( + table.stats.lastError, + table.details.indexCount, + ), sort: table => table.details.indexCount, className: cx("database-table__col-index-count"), name: "indexCount", @@ -347,7 +417,11 @@ export class DatabaseDetailsPage extends React.Component< Regions ), - cell: table => table.stats.nodesByRegionString || "None", + cell: table => + this.checkInfoAvailable( + table.stats.lastError, + table.stats.nodesByRegionString || "None", + ), sort: table => table.stats.nodesByRegionString, className: cx("database-table__col--regions"), name: "regions", @@ -403,7 +477,11 @@ export class DatabaseDetailsPage extends React.Component< Users ), - cell: table => table.details.userCount, + cell: table => + this.checkInfoAvailable( + table.details.lastError, + table.details.userCount, + ), sort: table => table.details.userCount, className: cx("database-table__col-user-count"), name: "userCount", @@ -414,8 +492,12 @@ export class DatabaseDetailsPage extends React.Component< Roles ), - cell: table => _.join(table.details.roles, ", "), - sort: table => _.join(table.details.roles, ", "), + cell: table => + this.checkInfoAvailable( + table.details.lastError, + table.details.roles.join(", "), + ), + sort: table => table.details.roles.join(", "), className: cx("database-table__col-roles"), name: "roles", }, @@ -425,8 +507,12 @@ export class DatabaseDetailsPage extends React.Component< Grants ), - cell: table => _.join(table.details.grants, ", "), - sort: table => _.join(table.details.grants, ", "), + cell: table => + this.checkInfoAvailable( + table.details.lastError, + table.details.grants.join(", "), + ), + sort: table => table.details.grants.join(", "), className: cx("database-table__col-grants"), name: "grants", }, @@ -498,24 +584,61 @@ export class DatabaseDetailsPage extends React.Component< /> - - - - This database has no tables. - + page={"databases"} + error={this.props.lastError} + render={() => ( + + + This database has no tables. + + } + /> + )} + renderError={() => + LoadingError({ + statsType: "databases", + timeout: this.props.lastError?.name + ?.toLowerCase() + .includes("timeout"), + }) } /> + {!this.props.loading && ( + <>} + renderError={() => + LoadingError({ + statsType: "part of the information", + timeout: + this.state.lastDetailsError?.name + ?.toLowerCase() + .includes("timeout") || + this.state.lastStatsError?.name + ?.toLowerCase() + .includes("timeout"), + }) + } + /> + )} this.getLastUsedString(indexStat), sort: indexStat => indexStat.lastUsed, }, - // TODO(lindseyjin): add index recommendations column ]; private grantsColumns: ColumnDescriptor[] = [ @@ -407,136 +420,185 @@ export class DatabaseTablePage extends React.Component< key={indexTabKey} className={cx("tab-pane")} > - - - - - - - - - - - - - {this.props.details.statsLastUpdated && ( - - )} - {this.props.automaticStatsCollectionEnabled != null && ( - - {" "} - Automatic statistics can help improve query - performance. Learn how to{" "} - ( + <> + + + + + + + + + + + + + {this.props.details.statsLastUpdated && ( + - manage statistics collection - - . - - } - /> - )} - - - - - - {this.props.showNodeRegionsSection && ( - - )} - - - - - - - -
- Index Stats -
- + )} + {this.props.automaticStatsCollectionEnabled != + null && ( + + {" "} + Automatic statistics can help improve query + performance. Learn how to{" "} + + manage statistics collection + + . + + } + /> + )} + + + + + + {this.props.showNodeRegionsSection && ( + + )} + + + + + + + - -
- - - + loading={this.props.indexStats.loading} + /> + + + + )} + renderError={() => + LoadingError({ + statsType: "databases", + timeout: + this.props.details.lastError?.name + ?.toLowerCase() + .includes("timeout") || + this.props.stats.lastError?.name + ?.toLowerCase() + .includes("timeout"), + }) + } + /> - ( + + )} + renderError={() => + LoadingError({ + statsType: "databases", + timeout: this.props.details.lastError?.name + ?.toLowerCase() + .includes("timeout"), + }) + } /> diff --git a/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.stories.tsx b/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.stories.tsx index 5107bbb00052..1ef347c9d6e9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.stories.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.stories.tsx @@ -22,6 +22,7 @@ const history = H.createHashHistory(); const withLoadingIndicator: DatabasesPageProps = { loading: true, loaded: false, + lastError: undefined, automaticStatsCollectionEnabled: true, databases: [], sortSetting: { @@ -46,6 +47,7 @@ const withLoadingIndicator: DatabasesPageProps = { const withoutData: DatabasesPageProps = { loading: false, loaded: true, + lastError: null, automaticStatsCollectionEnabled: true, databases: [], sortSetting: { @@ -70,6 +72,7 @@ const withoutData: DatabasesPageProps = { const withData: DatabasesPageProps = { loading: false, loaded: true, + lastError: null, showNodeRegionsColumn: true, automaticStatsCollectionEnabled: true, sortSetting: { @@ -80,6 +83,7 @@ const withData: DatabasesPageProps = { return { loading: false, loaded: true, + lastError: null, name: randomName(), sizeInBytes: _.random(1000.0) * 1024 ** _.random(1, 2), tableCount: _.random(5, 100), diff --git a/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.tsx b/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.tsx index 603fe046288f..35955da61e54 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databasesPage/databasesPage.tsx @@ -35,6 +35,8 @@ import { import { syncHistory, tableStatsClusterSetting } from "src/util"; import classnames from "classnames/bind"; import booleanSettingStyles from "../settings/booleanSetting.module.scss"; +import LoadingError from "../sqlActivity/errorComponent"; +import { Loading } from "../loading"; const cx = classNames.bind(styles); const sortableTableCx = classNames.bind(sortableTableStyles); @@ -52,6 +54,7 @@ const booleanSettingCx = classnames.bind(booleanSettingStyles); // interface DatabasesPageData { // loading: boolean; // loaded: boolean; +// lastError: Error; // sortSetting: SortSetting; // databases: { // DatabasesPageDataDatabase[] // loading: boolean; @@ -70,6 +73,7 @@ const booleanSettingCx = classnames.bind(booleanSettingStyles); export interface DatabasesPageData { loading: boolean; loaded: boolean; + lastError: Error; databases: DatabasesPageDataDatabase[]; sortSetting: SortSetting; automaticStatsCollectionEnabled?: boolean; @@ -79,6 +83,7 @@ export interface DatabasesPageData { export interface DatabasesPageDataDatabase { loading: boolean; loaded: boolean; + lastError: Error; name: string; sizeInBytes: number; tableCount: number; @@ -120,6 +125,7 @@ export type DatabasesPageProps = DatabasesPageData & interface DatabasesPageState { pagination: ISortedTablePagination; + lastDetailsError: Error; } class DatabasesSortedTable extends SortedTable {} @@ -136,6 +142,7 @@ export class DatabasesPage extends React.Component< current: 1, pageSize: 20, }, + lastDetailsError: null, }; const { history } = this.props; @@ -171,12 +178,30 @@ export class DatabasesPage extends React.Component< this.props.refreshSettings(); } - if (!this.props.loaded && !this.props.loading) { + if ( + !this.props.loaded && + !this.props.loading && + this.props.lastError === undefined + ) { return this.props.refreshDatabases(); } - _.forEach(this.props.databases, database => { - if (!database.loaded && !database.loading) { + let lastDetailsError: Error; + this.props.databases.forEach(database => { + if (database.lastError !== undefined) { + lastDetailsError = database.lastError; + } + if ( + lastDetailsError && + this.state.lastDetailsError?.name != lastDetailsError?.name + ) { + this.setState({ lastDetailsError: lastDetailsError }); + } + if ( + !database.loaded && + !database.loading && + database.lastError === undefined + ) { return this.props.refreshDatabaseDetails(database.name); } @@ -205,6 +230,16 @@ export class DatabasesPage extends React.Component< } }; + checkInfoAvailable = ( + database: DatabasesPageDataDatabase, + cell: React.ReactNode, + ): React.ReactNode => { + if (database.lastError) { + return "(unavailable)"; + } + return cell; + }; + private columns: ColumnDescriptor[] = [ { title: ( @@ -234,7 +269,8 @@ export class DatabasesPage extends React.Component< Size ), - cell: database => format.Bytes(database.sizeInBytes), + cell: database => + this.checkInfoAvailable(database, format.Bytes(database.sizeInBytes)), sort: database => database.sizeInBytes, className: cx("databases-table__col-size"), name: "size", @@ -248,7 +284,7 @@ export class DatabasesPage extends React.Component< Tables ), - cell: database => database.tableCount, + cell: database => this.checkInfoAvailable(database, database.tableCount), sort: database => database.tableCount, className: cx("databases-table__col-table-count"), name: "tableCount", @@ -262,7 +298,7 @@ export class DatabasesPage extends React.Component< Range count ), - cell: database => database.rangeCount, + cell: database => this.checkInfoAvailable(database, database.rangeCount), sort: database => database.rangeCount, className: cx("databases-table__col-range-count"), name: "rangeCount", @@ -276,7 +312,11 @@ export class DatabasesPage extends React.Component< Regions/nodes ), - cell: database => database.nodesByRegionString || "None", + cell: database => + this.checkInfoAvailable( + database, + database.nodesByRegionString || "None", + ), sort: database => database.nodesByRegionString, className: cx("databases-table__col-node-regions"), name: "nodeRegions", @@ -332,23 +372,57 @@ export class DatabasesPage extends React.Component<
- - - This cluster has no databases. - + page={"databases"} + error={this.props.lastError} + render={() => ( + + + This cluster has no databases. + + } + /> + )} + renderError={() => + LoadingError({ + statsType: "databases", + timeout: this.props.lastError?.name + ?.toLowerCase() + .includes("timeout"), + }) } /> + {!this.props.loading && ( + <>} + renderError={() => + LoadingError({ + statsType: "part of the information", + timeout: this.state.lastDetailsError?.name + ?.toLowerCase() + .includes("timeout"), + }) + } + /> + )} { error={this.props.sessionError} render={this.renderContent} renderError={() => - SQLActivityError({ + LoadingError({ statsType: "sessions", }) } diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx index 0864dd5447ad..45e1a8e8039b 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsPage.tsx @@ -22,7 +22,7 @@ import classNames from "classnames/bind"; import { sessionsTable } from "src/util/docs"; import emptyTableResultsIcon from "../assets/emptyState/empty-table-results.svg"; -import SQLActivityError from "../sqlActivity/errorComponent"; +import LoadingError from "../sqlActivity/errorComponent"; import { Pagination } from "src/pagination"; import { SortSetting, @@ -419,7 +419,7 @@ export class SessionsPage extends React.Component< error={this.props.sessionsError} render={this.renderSessions} renderError={() => - SQLActivityError({ + LoadingError({ statsType: "sessions", }) } diff --git a/pkg/ui/workspaces/cluster-ui/src/sqlActivity/errorComponent.tsx b/pkg/ui/workspaces/cluster-ui/src/sqlActivity/errorComponent.tsx index d7edf00cd72d..dc053af8c690 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sqlActivity/errorComponent.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sqlActivity/errorComponent.tsx @@ -19,7 +19,7 @@ interface SQLActivityErrorProps { timeout?: boolean; } -const SQLActivityError: React.FC = props => { +const LoadingError: React.FC = props => { const error = props.timeout ? "a timeout" : "an unexpected error"; return (
@@ -37,4 +37,4 @@ const SQLActivityError: React.FC = props => { ); }; -export default SQLActivityError; +export default LoadingError; diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index 077d0bb8849a..fea493077106 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -55,7 +55,7 @@ import { timeScaleToString, toRoundedDateRange, } from "../timeScaleDropdown"; -import SQLActivityError from "../sqlActivity/errorComponent"; +import LoadingError from "../sqlActivity/errorComponent"; import { ActivateDiagnosticsModalRef, ActivateStatementDiagnosticsModal, @@ -396,7 +396,7 @@ export class StatementDetails extends React.Component< error={error} render={this.renderTabs} renderError={() => - SQLActivityError({ + LoadingError({ statsType: "statements", }) } @@ -484,7 +484,7 @@ export class StatementDetails extends React.Component< {hasTimeout && ( this.renderStatements(regions)} renderError={() => - SQLActivityError({ + LoadingError({ statsType: "statements", timeout: this.props.statementsError?.name ?.toLowerCase() diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx index 0f276601b112..3164e84cd0e1 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx @@ -43,7 +43,7 @@ import { formatNumberForDisplay, } from "src/util"; import { UIConfigState } from "../store"; -import SQLActivityError from "../sqlActivity/errorComponent"; +import LoadingError from "../sqlActivity/errorComponent"; import summaryCardStyles from "../summaryCard/summaryCard.module.scss"; import transactionDetailsStyles from "./transactionDetails.modules.scss"; @@ -493,7 +493,7 @@ export class TransactionDetails extends React.Component< ); }} renderError={() => - SQLActivityError({ + LoadingError({ statsType: "transactions", }) } diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx index 6a3fde433bc5..e5be4054dcd0 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactionsPage.tsx @@ -59,7 +59,7 @@ import { StatisticTableColumnKeys, } from "../statsTableUtil/statsTableUtil"; import ClearStats from "../sqlActivity/clearStats"; -import SQLActivityError from "../sqlActivity/errorComponent"; +import LoadingError from "../sqlActivity/errorComponent"; import { commonStyles } from "../common"; import { TimeScaleDropdown, @@ -545,7 +545,7 @@ export class TransactionsPage extends React.Component< ); }} renderError={() => - SQLActivityError({ + LoadingError({ statsType: "transactions", timeout: this.props?.error?.name ?.toLowerCase() diff --git a/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts b/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts index 63e70c77f387..708cca89d4a4 100644 --- a/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts +++ b/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts @@ -91,6 +91,8 @@ export const refreshLocations = locationsReducerObj.refresh; const databasesReducerObj = new CachedDataReducer( api.getDatabaseList, "databases", + null, + moment.duration(10, "m"), ); export const refreshDatabases = databasesReducerObj.refresh; @@ -102,6 +104,8 @@ const databaseDetailsReducerObj = new KeyedCachedDataReducer( api.getDatabaseDetails, "databaseDetails", databaseRequestToID, + null, + moment.duration(10, "m"), ); const hotRangesRequestToID = (req: api.HotRangesRequestMessage) => @@ -134,6 +138,8 @@ const tableDetailsReducerObj = new KeyedCachedDataReducer( api.getTableDetails, "tableDetails", tableRequestToID, + null, + moment.duration(10, "m"), ); export const refreshTableDetails = tableDetailsReducerObj.refresh; @@ -141,6 +147,8 @@ const tableStatsReducerObj = new KeyedCachedDataReducer( api.getTableStats, "tableStats", tableRequestToID, + null, + moment.duration(10, "m"), ); export const refreshTableStats = tableStatsReducerObj.refresh; diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts index 75d43dd9a2e4..e37dcd267c65 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.spec.ts @@ -126,6 +126,7 @@ describe("Database Details Page", function() { driver.assertProperties({ loading: false, loaded: false, + lastError: undefined, name: "things", showNodeRegionsColumn: false, viewMode: ViewMode.Tables, @@ -145,6 +146,7 @@ describe("Database Details Page", function() { driver.assertProperties({ loading: false, loaded: true, + lastError: null, name: "things", showNodeRegionsColumn: false, viewMode: ViewMode.Tables, @@ -156,6 +158,7 @@ describe("Database Details Page", function() { details: { loading: false, loaded: false, + lastError: undefined, columnCount: 0, indexCount: 0, userCount: 0, @@ -166,6 +169,7 @@ describe("Database Details Page", function() { stats: { loading: false, loaded: false, + lastError: undefined, replicationSizeInBytes: 0, rangeCount: 0, nodesByRegionString: "", @@ -176,6 +180,7 @@ describe("Database Details Page", function() { details: { loading: false, loaded: false, + lastError: undefined, columnCount: 0, indexCount: 0, userCount: 0, @@ -186,6 +191,7 @@ describe("Database Details Page", function() { stats: { loading: false, loaded: false, + lastError: undefined, replicationSizeInBytes: 0, rangeCount: 0, nodesByRegionString: "", @@ -313,6 +319,7 @@ describe("Database Details Page", function() { driver.assertTableDetails("foo", { loading: false, loaded: true, + lastError: null, columnCount: 5, indexCount: 3, userCount: 2, @@ -326,6 +333,7 @@ describe("Database Details Page", function() { driver.assertTableDetails("bar", { loading: false, loaded: true, + lastError: null, columnCount: 4, indexCount: 1, userCount: 3, @@ -421,6 +429,7 @@ describe("Database Details Page", function() { driver.assertTableStats("foo", { loading: false, loaded: true, + lastError: null, replicationSizeInBytes: 44040192, rangeCount: 4200, nodesByRegionString: "", @@ -429,6 +438,7 @@ describe("Database Details Page", function() { driver.assertTableStats("bar", { loading: false, loaded: true, + lastError: null, replicationSizeInBytes: 8675309, rangeCount: 1023, nodesByRegionString: "", diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts index bcad3caf069e..49fe0234508d 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databaseDetailsPage/redux.ts @@ -118,6 +118,7 @@ export const mapStateToProps = createSelector( return { loading: !!databaseDetails[database]?.inFlight, loaded: !!databaseDetails[database]?.valid, + lastError: databaseDetails[database]?.lastError, name: database, showNodeRegionsColumn, viewMode, @@ -142,6 +143,7 @@ export const mapStateToProps = createSelector( details: { loading: !!details?.inFlight, loaded: !!details?.valid, + lastError: details?.lastError, columnCount: details?.data?.columns?.length || 0, indexCount: numIndexes, userCount: roles.length, @@ -154,6 +156,7 @@ export const mapStateToProps = createSelector( stats: { loading: !!stats?.inFlight, loaded: !!stats?.valid, + lastError: stats?.lastError, replicationSizeInBytes: FixLong( stats?.data?.approximate_disk_bytes || 0, ).toNumber(), diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.spec.ts b/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.spec.ts index 42ab5bf7686f..a5fb599c1ae9 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.spec.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.spec.ts @@ -175,6 +175,7 @@ describe("Database Table Page", function() { details: { loading: false, loaded: false, + lastError: undefined, createStatement: "", replicaCount: 0, indexNames: [], @@ -185,6 +186,7 @@ describe("Database Table Page", function() { stats: { loading: false, loaded: false, + lastError: undefined, sizeInBytes: 0, rangeCount: 0, nodesByRegionString: "", @@ -192,6 +194,7 @@ describe("Database Table Page", function() { indexStats: { loading: false, loaded: false, + lastError: undefined, stats: [], lastReset: null, }, @@ -223,6 +226,7 @@ describe("Database Table Page", function() { driver.assertTableDetails({ loading: false, loaded: true, + lastError: null, createStatement: "CREATE TABLE foo", replicaCount: 5, indexNames: ["primary", "another_index"], @@ -248,6 +252,7 @@ describe("Database Table Page", function() { driver.assertTableStats({ loading: false, loaded: true, + lastError: null, sizeInBytes: 44040192, rangeCount: 4200, nodesByRegionString: "", @@ -284,6 +289,7 @@ describe("Database Table Page", function() { driver.assertIndexStats({ loading: false, loaded: true, + lastError: null, stats: [ { indexName: "jobs_status_created_idx", diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.ts b/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.ts index 9b7de1ecee7f..be4aaa61232f 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databaseTablePage/redux.ts @@ -102,6 +102,7 @@ export const mapStateToProps = createSelector( details: { loading: !!details?.inFlight, loaded: !!details?.valid, + lastError: details?.lastError, createStatement: details?.data?.create_table_statement || "", replicaCount: details?.data?.zone_config?.num_replicas || 0, indexNames: _.uniq(_.map(details?.data?.indexes, index => index.name)), @@ -115,6 +116,7 @@ export const mapStateToProps = createSelector( stats: { loading: !!stats?.inFlight, loaded: !!stats?.valid, + lastError: stats?.lastError, sizeInBytes: FixLong( stats?.data?.approximate_disk_bytes || 0, ).toNumber(), @@ -124,6 +126,7 @@ export const mapStateToProps = createSelector( indexStats: { loading: !!indexStats?.inFlight, loaded: !!indexStats?.valid, + lastError: indexStats?.lastError, stats: indexStatsData, lastReset: lastReset, }, diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.spec.ts b/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.spec.ts index fe1540f68320..1152aac6c66e 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.spec.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.spec.ts @@ -106,6 +106,7 @@ describe("Databases Page", function() { driver.assertProperties({ loading: false, loaded: false, + lastError: undefined, databases: [], sortSetting: { ascending: true, columnTitle: "name" }, automaticStatsCollectionEnabled: true, @@ -129,10 +130,12 @@ describe("Databases Page", function() { driver.assertProperties({ loading: false, loaded: true, + lastError: null, databases: [ { loading: false, loaded: false, + lastError: undefined, name: "system", sizeInBytes: 0, tableCount: 0, @@ -143,6 +146,7 @@ describe("Databases Page", function() { { loading: false, loaded: false, + lastError: undefined, name: "test", sizeInBytes: 0, tableCount: 0, @@ -187,6 +191,7 @@ describe("Databases Page", function() { driver.assertDatabaseProperties("system", { loading: false, loaded: true, + lastError: null, name: "system", sizeInBytes: 7168, tableCount: 2, @@ -198,6 +203,7 @@ describe("Databases Page", function() { driver.assertDatabaseProperties("test", { loading: false, loaded: true, + lastError: null, name: "test", sizeInBytes: 1234, tableCount: 1, @@ -229,6 +235,7 @@ describe("Databases Page", function() { driver.assertDatabaseProperties("system", { loading: false, loaded: true, + lastError: null, name: "system", sizeInBytes: 7168, tableCount: 2, @@ -264,6 +271,7 @@ describe("Databases Page", function() { driver.assertDatabaseProperties("system", { loading: false, loaded: true, + lastError: null, name: "system", sizeInBytes: 8192, tableCount: 2, @@ -290,6 +298,7 @@ describe("Databases Page", function() { driver.assertDatabaseProperties("system", { loading: false, loaded: true, + lastError: null, name: "system", sizeInBytes: 0, tableCount: 2, @@ -328,6 +337,7 @@ describe("Databases Page", function() { driver.assertDatabaseProperties("system", { loading: false, loaded: true, + lastError: null, name: "system", sizeInBytes: 7168, tableCount: 2, @@ -341,6 +351,7 @@ describe("Databases Page", function() { driver.assertDatabaseProperties("system", { loading: false, loaded: true, + lastError: null, name: "system", sizeInBytes: 8192, tableCount: 2, diff --git a/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.ts b/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.ts index 7879a2a778d8..791878d4783e 100644 --- a/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.ts +++ b/pkg/ui/workspaces/db-console/src/views/databases/databasesPage/redux.ts @@ -46,6 +46,11 @@ const selectLoaded = createSelector( databases => databases.valid, ); +const selectLastError = createSelector( + (state: AdminUIState) => state.cachedData.databases, + databases => databases.lastError, +); + const sortSettingLocalSetting = new LocalSetting( "sortSetting/DatabasesPage", (state: AdminUIState) => state.localSettings, @@ -105,6 +110,7 @@ const selectDatabases = createSelector( return { loading: !!details?.inFlight, loaded: !!details?.valid, + lastError: details?.lastError, name: database, sizeInBytes: sizeInBytes, tableCount: details?.data?.table_names?.length || 0, @@ -123,6 +129,7 @@ const selectDatabases = createSelector( export const mapStateToProps = (state: AdminUIState): DatabasesPageData => ({ loading: selectLoading(state), loaded: selectLoaded(state), + lastError: selectLastError(state), databases: selectDatabases(state), sortSetting: sortSettingLocalSetting.selector(state), automaticStatsCollectionEnabled: selectAutomaticStatsCollectionEnabled(state),