diff --git a/pkg/ui/workspaces/cluster-ui/src/api/clusterSettingsApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/clusterSettingsApi.ts index 443ae92406ba..670f2b7c9b02 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/clusterSettingsApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/clusterSettingsApi.ts @@ -4,9 +4,13 @@ // included in the /LICENSE file. import { cockroach } from "@cockroachlabs/crdb-protobuf-client"; +import moment from "moment-timezone"; +import useSWRImmutable from "swr/immutable"; import { fetchData } from "src/api"; +import { TimestampToMoment } from "../util"; + import { ADMIN_API_PREFIX } from "./util"; export type SettingsRequestMessage = cockroach.server.serverpb.SettingsRequest; @@ -20,11 +24,97 @@ export function getClusterSettings( req: SettingsRequestMessage, timeout: string, ): Promise { + const urlParams = new URLSearchParams(); + urlParams.append("unredacted_values", "true"); + if (req.keys) { + urlParams.append("keys", req.keys.join(",")); + } + return fetchData( cockroach.server.serverpb.SettingsResponse, - `${ADMIN_API_PREFIX}/settings?unredacted_values=true`, - cockroach.server.serverpb.SettingsRequest, - req, + `${ADMIN_API_PREFIX}/settings?` + urlParams.toString(), + null, + null, timeout, ); } + +// Usage of this request with the useClusterSettings hook +// is preferred over using getClusterSettings and its corresponding +// redux values and sagas. +export type GetClusterSettingRequest = { + names: string[]; +}; + +enum ClusterSettingType { + BOOLEAN = "b", + DURATION = "d", + BYTE_SIZE = "z", + INTEGER = "i", + ENUM = "e", + FLOAT = "f,", + STRING = "s", + VERSION = "m", + UNKNOWN = "", +} + +export type ClusterSetting = { + name: string; + value: string; + type: ClusterSettingType; + description: string; + lastUpdated: moment.Moment | null; +}; + +const formatProtoResponse = ( + data: SettingsResponseMessage, +): Record => { + const settingsMap = {} as Record; + const entries = Object.values(data?.key_values ?? {}); + + entries?.forEach(kv => { + settingsMap[kv.name] = { + name: kv.name, + value: kv.value, + type: kv.type as ClusterSettingType, + description: kv.description, + lastUpdated: TimestampToMoment(kv.last_updated), + }; + }); + + return settingsMap; +}; + +const emptyClusterSetting: ClusterSetting = { + name: "", + value: "", + type: ClusterSettingType.UNKNOWN, + description: "", + lastUpdated: null, +}; + +export const useClusterSettings = (req: GetClusterSettingRequest) => { + const protoReq = new cockroach.server.serverpb.SettingsRequest({ + keys: req.names, + }); + const { data, isLoading, error } = useSWRImmutable(req.names, () => + getClusterSettings(protoReq, "1M").then(formatProtoResponse), + ); + + // If we don't have data we'll return a map of empty settings. + const settingValues = + data ?? + req.names.reduce( + (acc, name) => { + acc[name] = { ...emptyClusterSetting }; + return acc; + }, + {} as Record, + ); + + return { + settingValues, + isLoading, + error, + }; +}; diff --git a/pkg/ui/workspaces/cluster-ui/src/constants/tooltipMessages.tsx b/pkg/ui/workspaces/cluster-ui/src/constants/tooltipMessages.tsx new file mode 100644 index 000000000000..7ad7ca83daa1 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/constants/tooltipMessages.tsx @@ -0,0 +1,18 @@ +// Copyright 2024 The Cockroach Authors. +// +// Use of this software is governed by the CockroachDB Software License +// included in the /LICENSE file. +import Link from "antd/es/typography/Link"; +import React from "react"; + +import { tableStatsClusterSetting } from "src/util"; + +export const AUTO_STATS_COLLECTION_HELP = ( + + Automatic statistics can help improve query performance. Learn how to{" "} + + manage statistics collection + {" "} + . + +); diff --git a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx index d25fe18c8307..bb21c7bcc624 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databaseDetailsV2/tablesView.tsx @@ -17,6 +17,7 @@ import { NodeRegionsSelector } from "src/components/nodeRegionsSelector/nodeRegi import { RegionNodesLabel } from "src/components/regionNodesLabel"; import { TableMetadataJobControl } from "src/components/tableMetadataLastUpdated/tableMetadataJobControl"; import { Tooltip } from "src/components/tooltip"; +import { AUTO_STATS_COLLECTION_HELP } from "src/constants/tooltipMessages"; import { useRouteParams } from "src/hooks/useRouteParams"; import { PageSection } from "src/layouts"; import { PageConfig, PageConfigItem } from "src/pageConfig"; @@ -31,12 +32,7 @@ import { import useTable, { TableParams } from "src/sharedFromCloud/useTable"; import { Timestamp } from "src/timestamp"; import { StoreID } from "src/types/clusterTypes"; -import { - Bytes, - DATE_WITH_SECONDS_FORMAT_24_TZ, - tabAttr, - tableStatsClusterSetting, -} from "src/util"; +import { Bytes, DATE_WITH_SECONDS_FORMAT_24_TZ, tabAttr } from "src/util"; import { TableColName } from "./constants"; import { TableRow } from "./types"; @@ -141,17 +137,7 @@ const COLUMNS: (TableColumnProps & { sortKey?: TableSortOption })[] = }, { title: ( - - Automatic statistics can help improve query performance. Learn how - to{" "} - - manage statistics collection. - - - } - > + {TableColName.AUTO_STATS_ENABLED} ), diff --git a/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx b/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx index 11a9d014ecff..a08cc9e63121 100644 --- a/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/databasesV2/index.tsx @@ -8,6 +8,7 @@ import React, { useMemo } from "react"; import { Link } from "react-router-dom"; import { useNodeStatuses } from "src/api"; +import { useClusterSettings } from "src/api/clusterSettingsApi"; import { DatabaseMetadataRequest, DatabaseSortOptions, @@ -19,6 +20,7 @@ import { TableMetadataJobControl } from "src/components/tableMetadataLastUpdated import { Tooltip } from "src/components/tooltip"; import { PageLayout, PageSection } from "src/layouts"; import { PageConfig, PageConfigItem } from "src/pageConfig"; +import { BooleanSetting } from "src/settings"; import PageCount from "src/sharedFromCloud/pageCount"; import { PageHeader } from "src/sharedFromCloud/pageHeader"; import { Search } from "src/sharedFromCloud/search"; @@ -32,10 +34,14 @@ import useTable, { TableParams } from "src/sharedFromCloud/useTable"; import { StoreID } from "src/types/clusterTypes"; import { Bytes } from "src/util"; +import { AUTO_STATS_COLLECTION_HELP } from "../constants/tooltipMessages"; + import { DatabaseColName } from "./constants"; import { DatabaseRow } from "./databaseTypes"; import { rawDatabaseMetadataToDatabaseRows } from "./utils"; +const AUTO_STATS_ENABLED_CS = "sql.stats.automatic_collection.enabled"; + const COLUMNS: (TableColumnProps & { sortKey?: DatabaseSortOptions; })[] = [ @@ -141,6 +147,9 @@ export const DatabasesPageV2 = () => { createDatabaseMetadataRequestFromParams(params), ); const nodesResp = useNodeStatuses(); + const { settingValues, isLoading: settingsLoading } = useClusterSettings({ + names: [AUTO_STATS_ENABLED_CS], + }); const onNodeRegionsChange = (storeIDs: StoreID[]) => { setFilters({ @@ -194,7 +203,18 @@ export const DatabasesPageV2 = () => { return ( - + + + + } + />