Skip to content

Commit

Permalink
ui: add ability to disable expensive db and table replica/region data
Browse files Browse the repository at this point in the history
Retrieving replica counts and region data via the `SHOW RANGES FROM
DATABASE` and `SHOW RANGES FROM TABLE` queries is extremely costly,
especially on a large cluster.

Executing these queries should not be done automatically and it's
unclear if the cost of computation is worth the data that the customer
can see. It would be better for a customer to explicitly opt-in to
computing this data in special cases.

A cluster setting (`ui.database_locality_metadata.enabled`) has
been introduced to allow customers to turn this functionality off if
they're managing a large fleet where it's difficult to prevent users
from using this feature.

Release note (ops change, ui change): A new cluster setting
`ui.database_locality_metadata.enabled` allows operators to disable
loading extended database and table information in the DB Console
Database and Table pages. This information can cause significant CPU
load on large clusters with many ranges. Versions of this page from
24.3 onwards do not have this problem. If customers will require this
data, they can use the `SHOW RANGES FROM {DATABASE|TABLE}` query via
SQL to compute on-demand.
  • Loading branch information
dhartunian committed Oct 18, 2024
1 parent 661366d commit ffd337d
Show file tree
Hide file tree
Showing 19 changed files with 227 additions and 76 deletions.
1 change: 1 addition & 0 deletions docs/generated/settings/settings-for-tenants.txt
Original file line number Diff line number Diff line change
Expand Up @@ -399,5 +399,6 @@ trace.opentelemetry.collector string address of an OpenTelemetry trace collecto
trace.snapshot.rate duration 0s if non-zero, interval at which background trace snapshots are captured application
trace.span_registry.enabled boolean true if set, ongoing traces can be seen at https://<ui>/#/debug/tracez application
trace.zipkin.collector string the address of a Zipkin instance to receive traces, as <host>:<port>. If no port is specified, 9411 will be used. application
ui.disable_database_ranges boolean false the timezone used to format timestamps in the ui application
ui.display_timezone enumeration etc/utc the timezone used to format timestamps in the ui [etc/utc = 0, america/new_york = 1] application
version version 1000024.2-upgrading-to-1000024.3-step-022 set the active cluster version in the format '<major>.<minor>' application
1 change: 1 addition & 0 deletions docs/generated/settings/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@
<tr><td><div id="setting-trace-snapshot-rate" class="anchored"><code>trace.snapshot.rate</code></div></td><td>duration</td><td><code>0s</code></td><td>if non-zero, interval at which background trace snapshots are captured</td><td>Serverless/Dedicated/Self-Hosted</td></tr>
<tr><td><div id="setting-trace-span-registry-enabled" class="anchored"><code>trace.span_registry.enabled</code></div></td><td>boolean</td><td><code>true</code></td><td>if set, ongoing traces can be seen at https://&lt;ui&gt;/#/debug/tracez</td><td>Serverless/Dedicated/Self-Hosted</td></tr>
<tr><td><div id="setting-trace-zipkin-collector" class="anchored"><code>trace.zipkin.collector</code></div></td><td>string</td><td><code></code></td><td>the address of a Zipkin instance to receive traces, as &lt;host&gt;:&lt;port&gt;. If no port is specified, 9411 will be used.</td><td>Serverless/Dedicated/Self-Hosted</td></tr>
<tr><td><div id="setting-ui-disable-database-ranges" class="anchored"><code>ui.disable_database_ranges</code></div></td><td>boolean</td><td><code>false</code></td><td>the timezone used to format timestamps in the ui</td><td>Serverless/Dedicated/Self-Hosted</td></tr>
<tr><td><div id="setting-ui-display-timezone" class="anchored"><code>ui.display_timezone</code></div></td><td>enumeration</td><td><code>etc/utc</code></td><td>the timezone used to format timestamps in the ui [etc/utc = 0, america/new_york = 1]</td><td>Serverless/Dedicated/Self-Hosted</td></tr>
<tr><td><div id="setting-version" class="anchored"><code>version</code></div></td><td>version</td><td><code>1000024.2-upgrading-to-1000024.3-step-022</code></td><td>set the active cluster version in the format &#39;&lt;major&gt;.&lt;minor&gt;&#39;</td><td>Serverless/Dedicated/Self-Hosted</td></tr>
</tbody>
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/dev/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ Replaces 'make ui-watch'.`,
args := []string{
"build",
"//pkg/ui/workspaces/cluster-ui:cluster-ui-lib",
"//pkg/ui/workspaces/db-console/src/js:crdb-protobuf-client_files",
}
if !isOss {
args = append(args, "//pkg/ui/workspaces/db-console/ccl/src/js:crdb-protobuf-client-ccl-lib")
Expand Down
9 changes: 9 additions & 0 deletions pkg/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ var _ = settings.RegisterEnumSetting(
},
settings.WithPublic)

// TODO(davidh): This setting can be removed after 24.3 since it only
// affects legacy DB page.
var _ = settings.RegisterBoolSetting(
settings.ApplicationLevel,
"ui.database_locality_metadata.enabled",
"if enabled shows extended locality data about databases and tables in DB Console which can be expensive to compute",
true,
settings.WithPublic)

// Assets is used for embedded JS assets required for UI.
// In case the binary is built without UI, it provides single index.html file with
// the same content as indexHTML as a fallback.
Expand Down
75 changes: 52 additions & 23 deletions pkg/ui/workspaces/cluster-ui/src/api/databaseDetailsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type ZoneConfigLevelType = cockroach.server.serverpb.ZoneConfigurationLevel;
export type DatabaseDetailsReqParams = {
database: string;
csIndexUnusedDuration: string;
includeLocalityMetadata: boolean;
};

export type DatabaseDetailsSpanStatsReqParams = {
Expand Down Expand Up @@ -471,34 +472,58 @@ type DatabaseDetailsQuery<RowType> = {
) => Promise<boolean>;
};

const databaseDetailQueries: DatabaseDetailsQuery<DatabaseDetailsRow>[] = [
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseGrantsQuery,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseTablesQuery,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseReplicasAndRegions,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseIndexUsageStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseZoneConfig,
];
function databaseDetailQueries(
includeLocalityData: boolean,
): DatabaseDetailsQuery<DatabaseDetailsRow>[] {
if (includeLocalityData) {
return [
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseGrantsQuery,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseTablesQuery,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseReplicasAndRegions,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseIndexUsageStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseZoneConfig,
];
} else {
return [
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseGrantsQuery,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseTablesQuery,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseIndexUsageStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getDatabaseZoneConfig,
];
}
}

export function createDatabaseDetailsReq(
params: DatabaseDetailsReqParams,
): SqlExecutionRequest {
return {
...createSqlExecutionRequest(
params.database,
databaseDetailQueries.map(query =>
databaseDetailQueries(params.includeLocalityMetadata).map(query =>
query.createStmt(params.database, params.csIndexUnusedDuration),
),
),
Expand Down Expand Up @@ -554,7 +579,9 @@ async function fetchDatabaseDetails(
errs.push(txnResult.error);
}
const query: DatabaseDetailsQuery<DatabaseDetailsRow> =
databaseDetailQueries[txnResult.statement - 1];
databaseDetailQueries(params.includeLocalityMetadata)[
txnResult.statement - 1
];
query.addToDatabaseDetail(txnResult, detailsResponse);
});
if (resp.error) {
Expand All @@ -578,7 +605,9 @@ async function fetchSeparatelyDatabaseDetails(
): Promise<SqlApiResponse<DatabaseDetailsResponse>> {
const detailsResponse: DatabaseDetailsResponse = newDatabaseDetailsResponse();
const errs: Error[] = [];
for (const databaseDetailQuery of databaseDetailQueries) {
for (const databaseDetailQuery of databaseDetailQueries(
params.includeLocalityMetadata,
)) {
const req = createSqlExecutionRequest(params.database, [
databaseDetailQuery.createStmt(
params.database,
Expand Down
111 changes: 77 additions & 34 deletions pkg/ui/workspaces/cluster-ui/src/api/tableDetailsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,47 +545,84 @@ export type TableDetailsRow =
| TableZoneConfigRow
| TableReplicasRow;

const tableDetailQueries: TableDetailsQuery<TableDetailsRow>[] = [
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableGrants,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableSchemaDetails,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableCreateStatement,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableZoneConfigStmt,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableHeuristicsDetails,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableSpanStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableIndexUsageStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableZoneConfig,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableReplicaStoreIDs,
];
function tableDetailQueries(
includeLocalityData: boolean,
): TableDetailsQuery<TableDetailsRow>[] {
if (includeLocalityData) {
return [
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableGrants,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableSchemaDetails,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableCreateStatement,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableZoneConfigStmt,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableHeuristicsDetails,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableSpanStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableIndexUsageStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableZoneConfig,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableReplicaStoreIDs,
];
} else {
return [
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableGrants,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableSchemaDetails,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableCreateStatement,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableZoneConfigStmt,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableHeuristicsDetails,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableSpanStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableIndexUsageStats,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
getTableZoneConfig,
];
}
}

export function createTableDetailsReq(
dbName: string,
tableName: string,
csIndexUnusedDuration: string,
includeLocalityData: boolean,
): SqlExecutionRequest {
return {
execute: true,
statements: tableDetailQueries.map(query =>
statements: tableDetailQueries(includeLocalityData).map(query =>
query.createStmt(dbName, tableName, csIndexUnusedDuration),
),
max_result_size: LARGE_RESULT_SIZE,
Expand All @@ -600,6 +637,7 @@ export type TableDetailsReqParams = {
// Note: table name is expected in the following format: "schemaName"."tableName"
table: string;
csIndexUnusedDuration: string;
includeLocalityData: boolean;
};

export async function getTableDetails(
Expand All @@ -611,6 +649,7 @@ export async function getTableDetails(
params.database,
params.table,
params.csIndexUnusedDuration,
params.includeLocalityData,
),
timeout,
);
Expand All @@ -620,20 +659,24 @@ async function fetchTableDetails(
databaseName: string,
tableName: string,
csIndexUnusedDuration: string,
includeLocalityMetaata: boolean,
): Promise<SqlApiResponse<TableDetailsResponse>> {
const detailsResponse: TableDetailsResponse = newTableDetailsResponse();
const req: SqlExecutionRequest = createTableDetailsReq(
databaseName,
tableName,
csIndexUnusedDuration,
includeLocalityMetaata,
);
const resp = await executeInternalSql<TableDetailsRow>(req);
const errs: Error[] = [];
resp.execution.txn_results.forEach(txnResult => {
if (txnResult.error) {
errs.push(txnResult.error);
}
const query = tableDetailQueries[txnResult.statement - 1];
const query = tableDetailQueries(includeLocalityMetaata)[
txnResult.statement - 1
];
query.addToTableDetail(txnResult, detailsResponse);
});
if (resp.error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ const mapDispatchToProps = (
): DatabaseDetailsPageActions => ({
refreshDatabaseDetails: (database: string, csIndexUnusedDuration: string) => {
dispatch(
databaseDetailsActions.refresh({ database, csIndexUnusedDuration }),
databaseDetailsActions.refresh({
database,
csIndexUnusedDuration,
// When running in cloud, we always load this information. The
// configurability is for SH customers.
includeLocalityMetadata: true,
}),
);
},
refreshTableDetails: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export interface DatabaseDetailsPageData {
showNodeRegionsColumn?: boolean;
showIndexRecommendations: boolean;
csIndexUnusedDuration: string;
includeLocalityMetadataSetting?: boolean;
}

export interface DatabaseDetailsPageDataTable {
Expand Down Expand Up @@ -159,11 +160,13 @@ export interface DatabaseDetailsPageActions {
refreshDatabaseDetails: (
database: string,
csIndexUnusedDuration: string,
includeLocalityMetadata: boolean,
) => void;
refreshTableDetails: (
database: string,
table: string,
csIndexUnusedDuration: string,
includeLocalityMetadata: boolean,
) => void;
onFilterChange?: (value: Filters) => void;
onSearchComplete?: (query: string) => void;
Expand Down Expand Up @@ -267,6 +270,7 @@ export class DatabaseDetailsPage extends React.Component<
this.props.refreshDatabaseDetails(
this.props.name,
this.props.csIndexUnusedDuration,
this.props.includeLocalityMetadataSetting,
);
} else {
// If the props are already loaded then componentDidUpdate
Expand Down Expand Up @@ -352,6 +356,7 @@ export class DatabaseDetailsPage extends React.Component<
this.props.name,
table.name.qualifiedNameWithSchemaAndTable,
this.props.csIndexUnusedDuration,
this.props.includeLocalityMetadataSetting,
);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ export const TableNameCell = ({
linkURL += `?viewMode=${ViewMode.Grants}`;
}
} else {
linkURL = EncodeDatabaseTableUri(
linkURL = `/legacy${EncodeDatabaseTableUri(
dbDetails.name,
table.name.qualifiedNameWithSchemaAndTable,
);
)}`;
if (dbDetails.viewMode === ViewMode.Grants) {
linkURL += `?tab=grants`;
}
Expand Down Expand Up @@ -182,7 +182,7 @@ export const DbDetailsBreadcrumbs = ({ dbName }: { dbName: string }) => {
{
link: isCockroachCloud
? `/databases/${EncodeUriName(dbName)}`
: EncodeDatabaseUri(dbName),
: `/legacy${EncodeDatabaseUri(dbName)}`,
name: "Tables",
},
]}
Expand Down
Loading

0 comments on commit ffd337d

Please sign in to comment.