Skip to content

Commit

Permalink
cluster-ui: handle partial response errors on the database table page
Browse files Browse the repository at this point in the history
Part of: cockroachdb#102386

This change applies the same error handling ideas from cockroachdb#109245 to the
database table page, enabling non-admin users to use the database table
page and providing better transparency to data fetching issues.

`unavailable` fields provide a tooltip that displays the error impacting
that field.

Release note (ui change): Non-admin users are able to use the database
table page.
  • Loading branch information
Thomas Hardy committed Sep 14, 2023
1 parent 2694d15 commit 0408c76
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,10 @@ export class DatabaseDetailsPage extends React.Component<
checkInfoAvailable(
table.requestError,
null,
table.details.nodesByRegionString || "None",
table.details.nodesByRegionString &&
table.details.nodesByRegionString.length > 0
? table.details.nodesByRegionString
: null,
),
sort: table => table.details.nodesByRegionString,
className: cx("database-table__col--regions"),
Expand Down Expand Up @@ -669,13 +672,16 @@ export class DatabaseDetailsPage extends React.Component<
Table Stats Last Updated <Timezone />
</Tooltip>
),
cell: table => (
<Timestamp
time={table.details.statsLastUpdated?.stats_last_created_at}
format={DATE_FORMAT}
fallback={"No table statistics found"}
/>
),
cell: table =>
checkInfoAvailable(
table.requestError,
table.details.statsLastUpdated?.error,
<Timestamp
time={table.details.statsLastUpdated?.stats_last_created_at}
format={DATE_FORMAT}
fallback={"No table statistics found"}
/>,
),
sort: table => table.details.statsLastUpdated,
className: cx("database-table__col--table-stats"),
name: "tableStatsUpdated",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,22 @@ import {
ActionCell,
DbTablesBreadcrumbs,
FormatMVCCInfo,
getCreateStmt,
IndexRecCell,
LastReset,
LastUsed,
NameCell,
} from "./helperComponents";
import {
SqlApiQueryResponse,
SqlExecutionErrorMessage,
TableCreateStatementRow,
TableHeuristicDetailsRow,
TableReplicaData,
TableSchemaDetailsRow,
TableSpanStatsRow,
} from "../api";
import { checkInfoAvailable } from "../databases";

const cx = classNames.bind(styles);
const booleanSettingCx = classnames.bind(booleanSettingStyles);
Expand Down Expand Up @@ -122,17 +133,16 @@ export interface DatabaseTablePageData {
export interface DatabaseTablePageDataDetails {
loading: boolean;
loaded: boolean;
lastError: Error;
createStatement: string;
replicaCount: number;
indexNames: string[];
grants: Grant[];
statsLastUpdated?: Moment;
totalBytes: number;
liveBytes: number;
livePercentage: number;
sizeInBytes: number;
rangeCount: number;
// Request error getting table details
requestError: Error;
// Query error getting table details
queryError: SqlExecutionErrorMessage;
createStatement: SqlApiQueryResponse<TableCreateStatementRow>;
replicaData: SqlApiQueryResponse<TableReplicaData>;
spanStats: SqlApiQueryResponse<TableSpanStatsRow>;
indexData: SqlApiQueryResponse<TableSchemaDetailsRow>;
grants: SqlApiQueryResponse<AllGrants>;
statsLastUpdated?: SqlApiQueryResponse<TableHeuristicDetailsRow>;
nodesByRegionString?: string;
}

Expand All @@ -157,6 +167,10 @@ interface IndexRecommendation {
reason: string;
}

interface AllGrants {
all: Grant[];
}

interface Grant {
user: string;
privileges: string[];
Expand Down Expand Up @@ -274,7 +288,7 @@ export class DatabaseTablePage extends React.Component<
if (
!this.props.details.loaded &&
!this.props.details.loading &&
this.props.details.lastError === undefined
this.props.details.requestError === undefined
) {
return this.props.refreshTableDetails(
this.props.databaseName,
Expand Down Expand Up @@ -412,6 +426,7 @@ export class DatabaseTablePage extends React.Component<

render(): React.ReactElement {
const { hasAdminRole } = this.props;
const details: DatabaseTablePageDataDetails = this.props.details;
return (
<div className="root table-area">
<section className={baseHeadingClasses.wrapper}>
Expand Down Expand Up @@ -442,47 +457,70 @@ export class DatabaseTablePage extends React.Component<
className={cx("tab-pane")}
>
<Loading
loading={this.props.details.loading}
loading={details.loading}
page={"table_details"}
error={this.props.details.lastError}
error={details.requestError}
render={() => (
<>
<Row gutter={18}>
<Col className="gutter-row" span={18}>
<SqlBox value={this.props.details.createStatement} />
<SqlBox value={getCreateStmt(details)} />
</Col>
</Row>

<Row gutter={18}>
<Col className="gutter-row" span={8}>
<SummaryCard className={cx("summary-card")}>
<SummaryCardItem
label="Size"
value={format.Bytes(this.props.details.sizeInBytes)}
value={checkInfoAvailable(
details.requestError,
details.spanStats?.error,
details.spanStats?.approximate_disk_bytes
? format.Bytes(
details.spanStats?.approximate_disk_bytes,
)
: null,
)}
/>
<SummaryCardItem
label="Replicas"
value={this.props.details.replicaCount}
value={checkInfoAvailable(
details.requestError,
details.replicaData?.error,
details.replicaData?.replicaCount,
)}
/>
<SummaryCardItem
label="Ranges"
value={this.props.details.rangeCount}
value={checkInfoAvailable(
details.requestError,
details.spanStats?.error,
details.spanStats?.range_count,
)}
/>
<SummaryCardItem
label="% of Live Data"
value={
<FormatMVCCInfo details={this.props.details} />
}
value={checkInfoAvailable(
details.requestError,
details.spanStats?.error,
<FormatMVCCInfo details={details} />,
)}
/>
{this.props.details.statsLastUpdated && (
{details.statsLastUpdated && (
<SummaryCardItem
label="Table Stats Last Updated"
value={
value={checkInfoAvailable(
details.requestError,
details.statsLastUpdated?.error,
<Timestamp
time={this.props.details.statsLastUpdated}
time={
details.statsLastUpdated
?.stats_last_created_at
}
format={DATE_FORMAT_24_TZ}
/>
}
fallback={"No table statistics found"}
/>,
)}
/>
)}
{this.props.automaticStatsCollectionEnabled !=
Expand Down Expand Up @@ -517,7 +555,14 @@ export class DatabaseTablePage extends React.Component<
{this.props.showNodeRegionsSection && (
<SummaryCardItem
label="Regions/Nodes"
value={this.props.details.nodesByRegionString}
value={checkInfoAvailable(
details.requestError,
null,
details.nodesByRegionString &&
details.nodesByRegionString?.length
? details.nodesByRegionString
: null,
)}
/>
)}
<SummaryCardItem
Expand All @@ -526,7 +571,11 @@ export class DatabaseTablePage extends React.Component<
/>
<SummaryCardItem
label="Indexes"
value={this.props.details.indexNames.join(", ")}
value={checkInfoAvailable(
details.requestError,
details.indexData?.error,
details.indexData?.indexes?.join(", "),
)}
className={cx(
"database-table-page__indexes--value",
)}
Expand Down Expand Up @@ -599,30 +648,30 @@ export class DatabaseTablePage extends React.Component<
renderError={() =>
LoadingError({
statsType: "databases",
error: this.props.details.lastError,
error: details.requestError,
})
}
/>
</TabPane>
<TabPane tab="Grants" key={grantsTabKey} className={cx("tab-pane")}>
<Loading
loading={this.props.details.loading}
loading={details.loading}
page={"table_details_grants"}
error={this.props.details.lastError}
error={details.requestError}
render={() => (
<DatabaseTableGrantsTable
data={this.props.details.grants}
data={details.grants?.all}
columns={this.grantsColumns}
sortSetting={this.state.grantSortSetting}
onChangeSortSetting={this.changeGrantSortSetting.bind(this)}
loading={this.props.details.loading}
loading={details.loading}
tableWrapperClassName={cx("sorted-table")}
/>
)}
renderError={() =>
LoadingError({
statsType: "databases",
error: this.props.details.lastError,
error: details.requestError,
})
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { Search as IndexIcon } from "@cockroachlabs/icons";
import { Breadcrumbs } from "../breadcrumbs";
import { CaretRight } from "../icon/caretRight";
import { CockroachCloudContext } from "../contexts";
import { sqlApiErrorMessage } from "../api";
const cx = classNames.bind(styles);

export const NameCell = ({
Expand Down Expand Up @@ -213,12 +214,25 @@ export const FormatMVCCInfo = ({
}): JSX.Element => {
return (
<>
{format.Percentage(details.livePercentage, 1, 1)}
{format.Percentage(details.spanStats?.live_percentage, 1, 1)}
{" ("}
<span className={cx("bold")}>{format.Bytes(details.liveBytes)}</span> live
data /{" "}
<span className={cx("bold")}>{format.Bytes(details.totalBytes)}</span>
<span className={cx("bold")}>
{format.Bytes(details.spanStats?.live_bytes)}
</span>{" "}
live data /{" "}
<span className={cx("bold")}>
{format.Bytes(details.spanStats?.total_bytes)}
</span>
{" total)"}
</>
);
};

export const getCreateStmt = ({
createStatement,
}: DatabaseTablePageDataDetails): string => {
return createStatement?.create_statement
? createStatement?.create_statement
: "(unavailable)\n" +
sqlApiErrorMessage(createStatement?.error?.message || "");
};
20 changes: 8 additions & 12 deletions pkg/ui/workspaces/cluster-ui/src/databases/combiners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,18 +179,14 @@ export const deriveTablePageDetailsMemoized = createSelector(
return {
loading: !!details?.inFlight,
loaded: !!details?.valid,
lastError: details?.lastError,
createStatement: results?.createStmtResp.create_statement || "",
replicaCount: results?.stats.replicaData.replicaCount || 0,
indexNames: results?.schemaDetails.indexes || [],
grants: normalizedGrants,
statsLastUpdated:
results?.heuristicsDetails.stats_last_created_at || null,
totalBytes: results?.stats.spanStats.total_bytes || 0,
liveBytes: results?.stats.spanStats.live_bytes || 0,
livePercentage: results?.stats.spanStats.live_percentage || 0,
sizeInBytes: results?.stats.spanStats.approximate_disk_bytes || 0,
rangeCount: results?.stats.spanStats.range_count || 0,
requestError: details?.lastError,
queryError: results?.error,
createStatement: results?.createStmtResp,
replicaData: results?.stats?.replicaData,
indexData: results?.schemaDetails,
grants: { all: normalizedGrants, error: results?.grantsResp?.error },
statsLastUpdated: results?.heuristicsDetails,
spanStats: results?.stats?.spanStats,
nodesByRegionString: getNodesByRegionString(nodes, nodeRegions, isTenant),
};
},
Expand Down
Loading

0 comments on commit 0408c76

Please sign in to comment.