Skip to content

Commit

Permalink
ui: show correct zone config
Browse files Browse the repository at this point in the history
Previously on the table detail page in DB Console,
the default zone config for the db was shown, rather
than any table level settings. Additionally,
constraints and lease preferences were not being
serialized properly, displaying as
"...Object object..." This fixes both of those bugs.

The display for the zone configuration statement is
additionally updated to show the actual SQL statement
to replicate the zone config in the SQL shell.
Previously an invalid statement "CONFIGURE ZONE USING..."
was displayed.

Resolves cockroachdb#57896.
See also: cockroachlabs/support#737.
See also: cockroachlabs/support#727.

Release note (bug fix): Fixed a bug introduced in v20.1 in
DB Console where incorrect zone configuration values were
shown on the table details page and constraints and lease
preferences were not displayed.

Release note (ui change): Updates the table details page
to show table specific zone configuration values when set,
show constraints and lease preferences, and display
a valid statement to re-configure zone configuration
for that table.
  • Loading branch information
nkodali committed Jan 28, 2021
1 parent a36c594 commit c40ec31
Show file tree
Hide file tree
Showing 9 changed files with 443 additions and 392 deletions.
1 change: 1 addition & 0 deletions docs/generated/http/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -2893,6 +2893,7 @@ a table.
| zone_config | [cockroach.config.zonepb.ZoneConfig](#cockroach.server.serverpb.TableDetailsResponse-cockroach.config.zonepb.ZoneConfig) | | The zone configuration in effect for this table. | [reserved](#support-status) |
| zone_config_level | [ZoneConfigurationLevel](#cockroach.server.serverpb.TableDetailsResponse-cockroach.server.serverpb.ZoneConfigurationLevel) | | The level at which this object's zone configuration is set. | [reserved](#support-status) |
| descriptor_id | [int64](#cockroach.server.serverpb.TableDetailsResponse-int64) | | descriptor_id is an identifier used to uniquely identify this table. It can be used to find events pertaining to this table by filtering on the 'target_id' field of events. | [reserved](#support-status) |
| configure_zone_statement | [string](#cockroach.server.serverpb.TableDetailsResponse-string) | | configure_zone_statement is the output of "SHOW ZONE CONFIGURATION FOR TABLE" for this table. It is a SQL statement that would re-configure the table's current zone if executed. | [reserved](#support-status) |



Expand Down
25 changes: 25 additions & 0 deletions pkg/server/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,31 @@ func (s *adminServer) TableDetails(
resp.CreateTableStatement = createStmt
}

// Marshal SHOW ZONE CONFIGURATION result.
rows, cols, err = s.server.sqlServer.internalExecutor.QueryWithCols(
ctx, "admin-show-zone-config", nil, /* txn */
sessiondata.InternalExecutorOverride{User: userName},
fmt.Sprintf("SHOW ZONE CONFIGURATION FOR TABLE %s", escQualTable))
if s.isNotFoundError(err) {
return nil, status.Errorf(codes.NotFound, "%s", err)
}
if err != nil {
return nil, s.serverError(err)
}
{
const rawConfigSQLColName = "raw_config_sql"
if len(rows) == 1 {
scanner := makeResultScanner(cols)
var configureZoneStmt string
if err := scanner.Scan(rows[0], rawConfigSQLColName, &configureZoneStmt); err != nil {
return nil, err
}
resp.ConfigureZoneStatement = configureZoneStmt
} else {
resp.ConfigureZoneStatement = ""
}
}

var tableID descpb.ID
// Query the descriptor ID and zone configuration for this table.
{
Expand Down
706 changes: 375 additions & 331 deletions pkg/server/serverpb/admin.pb.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions pkg/server/serverpb/admin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ message TableDetailsResponse {
// It can be used to find events pertaining to this table by filtering on
// the 'target_id' field of events.
int64 descriptor_id = 8 [(gogoproto.customname) = "DescriptorID"];
// configure_zone_statement is the output of "SHOW ZONE CONFIGURATION FOR TABLE"
// for this table. It is a SQL statement that would re-configure the table's current
// zone if executed.
string configure_zone_statement = 9;
}

// TableStatsRequest is a request for detailed, computationally expensive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export const dbFullfilledProps: DatabaseSummaryProps = {
rangeCount: 1,
createStatement:
'CREATE TABLE comments (\n\ttype INT8 NOT NULL,\n\tobject_id INT8 NOT NULL,\n\tsub_id INT8 NOT NULL,\n\tcomment STRING NOT NULL,\n\tCONSTRAINT "primary" PRIMARY KEY (type ASC, object_id ASC, sub_id ASC),\n\tFAMILY "primary" (type, object_id, sub_id),\n\tFAMILY fam_4_comment (comment)\n)',
configureZoneStatement:
"ALTER RANGE default CONFIGURE ZONE USING\n\trange_min_bytes = 134217728,\n\trange_max_bytes = 536870912,\n\tgc.ttlseconds = 90000,\n\tnum_replicas = 3,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
grants: [
{ user: "admin", privileges: ["DELETE"] },
{ user: "admin", privileges: ["GRANT"] },
Expand Down Expand Up @@ -93,6 +95,8 @@ export const dbFullfilledProps: DatabaseSummaryProps = {
rangeCount: 0,
createStatement:
'CREATE TABLE descriptor (\n\tid INT8 NOT NULL,\n\tdescriptor BYTES NULL,\n\tCONSTRAINT "primary" PRIMARY KEY (id ASC),\n\tFAMILY "primary" (id),\n\tFAMILY fam_2_descriptor (descriptor)\n)',
configureZoneStatement:
"ALTER RANGE default CONFIGURE ZONE USING\n\trange_min_bytes = 134217728,\n\trange_max_bytes = 536870912,\n\tgc.ttlseconds = 90000,\n\tnum_replicas = 3,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
grants: [
{ user: "admin", privileges: ["GRANT"] },
{ user: "admin", privileges: ["SELECT"] },
Expand Down Expand Up @@ -126,6 +130,8 @@ export const dbFullfilledProps: DatabaseSummaryProps = {
rangeCount: 1,
createStatement:
'CREATE TABLE eventlog (\n\t"timestamp" TIMESTAMP NOT NULL,\n\t"eventType" STRING NOT NULL,\n\t"targetID" INT8 NOT NULL,\n\t"reportingID" INT8 NOT NULL,\n\tinfo STRING NULL,\n\t"uniqueID" BYTES NOT NULL DEFAULT uuid_v4(),\n\tCONSTRAINT "primary" PRIMARY KEY ("timestamp" ASC, "uniqueID" ASC),\n\tFAMILY "primary" ("timestamp", "uniqueID"),\n\tFAMILY "fam_2_eventType" ("eventType"),\n\tFAMILY "fam_3_targetID" ("targetID"),\n\tFAMILY "fam_4_reportingID" ("reportingID"),\n\tFAMILY fam_5_info (info)\n)',
configureZoneStatement:
"ALTER RANGE default CONFIGURE ZONE USING\n\trange_min_bytes = 134217728,\n\trange_max_bytes = 536870912,\n\tgc.ttlseconds = 90000,\n\tnum_replicas = 3,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
grants: [
{ user: "admin", privileges: ["DELETE"] },
{ user: "admin", privileges: ["GRANT"] },
Expand Down Expand Up @@ -165,6 +171,8 @@ export const dbFullfilledProps: DatabaseSummaryProps = {
rangeCount: 1,
createStatement:
"CREATE TABLE jobs (\n\tid INT8 NOT NULL DEFAULT unique_rowid(),\n\tstatus STRING NOT NULL,\n\tcreated TIMESTAMP NOT NULL DEFAULT now():::TIMESTAMP,\n\tpayload BYTES NOT NULL,\n\tprogress BYTES NULL,\n\tCONSTRAINT \"primary\" PRIMARY KEY (id ASC),\n\tINDEX jobs_status_created_idx (status ASC, created ASC),\n\tFAMILY fam_0_id_status_created_payload (id, status, created, payload),\n\tFAMILY progress (progress)\n);\nALTER TABLE system.public.jobs CONFIGURE ZONE USING\n\trange_min_bytes = 16777216,\n\trange_max_bytes = 67108864,\n\tgc.ttlseconds = 600,\n\tnum_replicas = 5,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
configureZoneStatement:
"ALTER TABLE system.public.jobs CONFIGURE ZONE USING\n\trange_min_bytes = 16777216,\n\trange_max_bytes = 67108864,\n\tgc.ttlseconds = 600,\n\tnum_replicas = 5,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
grants: [
{ user: "admin", privileges: ["DELETE"] },
{ user: "admin", privileges: ["GRANT"] },
Expand Down Expand Up @@ -204,6 +212,8 @@ export const dbFullfilledProps: DatabaseSummaryProps = {
rangeCount: 1,
createStatement:
'CREATE TABLE lease (\n\t"descID" INT8 NOT NULL,\n\tversion INT8 NOT NULL,\n\t"nodeID" INT8 NOT NULL,\n\texpiration TIMESTAMP NOT NULL,\n\tCONSTRAINT "primary" PRIMARY KEY ("descID" ASC, version ASC, expiration ASC, "nodeID" ASC),\n\tFAMILY "primary" ("descID", version, "nodeID", expiration)\n)',
configureZoneStatement:
"ALTER RANGE default CONFIGURE ZONE USING\n\trange_min_bytes = 134217728,\n\trange_max_bytes = 536870912,\n\tgc.ttlseconds = 90000,\n\tnum_replicas = 3,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
grants: [
{ user: "admin", privileges: ["DELETE"] },
{ user: "admin", privileges: ["GRANT"] },
Expand Down Expand Up @@ -243,6 +253,8 @@ export const dbFullfilledProps: DatabaseSummaryProps = {
rangeCount: 1,
createStatement:
'CREATE TABLE locations (\n\t"localityKey" STRING NOT NULL,\n\t"localityValue" STRING NOT NULL,\n\tlatitude DECIMAL(18,15) NOT NULL,\n\tlongitude DECIMAL(18,15) NOT NULL,\n\tCONSTRAINT "primary" PRIMARY KEY ("localityKey" ASC, "localityValue" ASC),\n\tFAMILY "fam_0_localityKey_localityValue_latitude_longitude" ("localityKey", "localityValue", latitude, longitude)\n)',
configureZoneStatement:
"ALTER RANGE default CONFIGURE ZONE USING\n\trange_min_bytes = 134217728,\n\trange_max_bytes = 536870912,\n\tgc.ttlseconds = 90000,\n\tnum_replicas = 3,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
grants: [
{ user: "admin", privileges: ["DELETE"] },
{ user: "admin", privileges: ["GRANT"] },
Expand Down Expand Up @@ -282,6 +294,8 @@ export const dbFullfilledProps: DatabaseSummaryProps = {
rangeCount: 1,
createStatement:
'CREATE TABLE namespace (\n\t"parentID" INT8 NOT NULL,\n\tname STRING NOT NULL,\n\tid INT8 NULL,\n\tCONSTRAINT "primary" PRIMARY KEY ("parentID" ASC, name ASC),\n\tFAMILY "primary" ("parentID", name),\n\tFAMILY fam_3_id (id)\n)',
configureZoneStatement:
"ALTER RANGE default CONFIGURE ZONE USING\n\trange_min_bytes = 134217728,\n\trange_max_bytes = 536870912,\n\tgc.ttlseconds = 90000,\n\tnum_replicas = 3,\n\tconstraints = '[]',\n\tlease_preferences = '[]'",
grants: [
{ user: "admin", privileges: ["GRANT"] },
{ user: "admin", privileges: ["SELECT"] },
Expand Down
23 changes: 8 additions & 15 deletions pkg/ui/src/views/databases/containers/tableDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import { SortSetting } from "src/views/shared/components/sortabletable";
import { SortedTable } from "src/views/shared/components/sortedtable";
const { TabPane } = Tabs;
import { getMatchParamByName } from "src/util/query";
import { databaseDetails } from "../databaseSummary";
import { Button } from "@cockroachlabs/cluster-ui";
import { ArrowLeft } from "@cockroachlabs/icons";
import SqlBox from "src/views/shared/components/sql/box";
Expand Down Expand Up @@ -61,7 +60,6 @@ interface TableMainActions {
refreshTableStats: typeof refreshTableStats;
refreshDatabaseDetails: typeof refreshDatabaseDetails;
setSort: typeof databaseTableGrantsSortSetting.set;
dbResponse: protos.cockroach.server.serverpb.DatabaseDetailsResponse;
}

/**
Expand Down Expand Up @@ -100,7 +98,7 @@ export class TableMain extends React.Component<TableMainProps, {}> {
prevPage = () => this.props.history.goBack();

render() {
const { tableInfo, grantsSortSetting, match, dbResponse } = this.props;
const { tableInfo, grantsSortSetting, match } = this.props;
const database = getMatchParamByName(match, databaseNameAttr);
const table = getMatchParamByName(match, tableNameAttr);

Expand Down Expand Up @@ -130,7 +128,7 @@ export class TableMain extends React.Component<TableMainProps, {}> {
<Col className="gutter-row" span={16}>
<SqlBox
value={tableInfo.createStatement || ""}
zone={dbResponse}
secondaryValue={tableInfo.configureZoneStatement || ""}
/>
</Col>
<Col className="gutter-row" span={8}>
Expand Down Expand Up @@ -221,17 +219,12 @@ export function selectTableInfo(
const mapStateToProps = (
state: AdminUIState,
ownProps: RouteComponentProps,
) => ({
tableInfo: selectTableInfo(state, ownProps),
grantsSortSetting: databaseTableGrantsSortSetting.selector(state),
dbResponse:
databaseDetails(state)[
getMatchParamByName(ownProps.match, databaseNameAttr)
] &&
databaseDetails(state)[
getMatchParamByName(ownProps.match, databaseNameAttr)
].data,
});
) => {
return {
tableInfo: selectTableInfo(state, ownProps),
grantsSortSetting: databaseTableGrantsSortSetting.selector(state),
};
};

const mapDispatchToProps = {
setSort: databaseTableGrantsSortSetting.set,
Expand Down
2 changes: 2 additions & 0 deletions pkg/ui/src/views/databases/data/tableInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class TableInfo {
public mvccSize: protos.cockroach.storage.enginepb.IMVCCStats;
public rangeCount: number;
public createStatement: string;
public configureZoneStatement: string;
public grants: protos.cockroach.server.serverpb.TableDetailsResponse.IGrant[];
public numReplicas: number;
constructor(
Expand All @@ -42,6 +43,7 @@ export class TableInfo {
this.rangeCount =
stats && stats.range_count && stats.range_count.toNumber();
this.createStatement = details && details.create_table_statement;
this.configureZoneStatement = details && details.configure_zone_statement;
this.grants = details && details.grants;
this.numReplicas =
details && details.zone_config && details.zone_config.num_replicas;
Expand Down
4 changes: 1 addition & 3 deletions pkg/ui/src/views/shared/components/sql/box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ import { Highlight } from "./highlight";
import classNames from "classnames/bind";

import styles from "./sqlhighlight.module.styl";
import * as protos from "src/js/protos";

export interface SqlBoxProps {
value: string;
zone?: protos.cockroach.server.serverpb.DatabaseDetailsResponse;
secondaryValue?: string;
}

const cx = classNames.bind(styles);

class SqlBox extends React.Component<SqlBoxProps> {
preNode: React.RefObject<HTMLPreElement> = React.createRef();
render() {
return (
<div className={cx("box-highlight")}>
Expand Down
56 changes: 13 additions & 43 deletions pkg/ui/src/views/shared/components/sql/highlight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const cx = classNames.bind(styles);

export class Highlight extends React.Component<SqlBoxProps> {
preNode: React.RefObject<HTMLPreElement> = React.createRef();
preNodeSecondary: React.RefObject<HTMLPreElement> = React.createRef();

shouldComponentUpdate(newProps: SqlBoxProps) {
return newProps.value !== this.props.value;
Expand All @@ -26,65 +27,34 @@ export class Highlight extends React.Component<SqlBoxProps> {
componentDidMount() {
hljs.configure({
tabReplace: " ",
languages: ["sql"],
});
hljs.highlightBlock(this.preNode.current);
if (this.preNodeSecondary.current) {
hljs.highlightBlock(this.preNodeSecondary.current);
}
}

componentDidUpdate() {
hljs.highlightBlock(this.preNode.current);
if (this.preNodeSecondary.current) {
hljs.highlightBlock(this.preNodeSecondary.current);
}
}

renderZone = () => {
const { zone } = this.props;
const zoneConfig = zone.zone_config;
return (
<span className={cx("sql-highlight", "hljs")}>
<span className="hljs-keyword">CONFIGURE ZONE USING</span>
<br />
<span className="hljs-label">range_min_bytes = </span>
<span className="hljs-built_in">{`${String(
zoneConfig.range_min_bytes,
)},`}</span>
<br />
<span className="hljs-label">range_max_bytes = </span>
<span className="hljs-built_in">{`${String(
zoneConfig.range_max_bytes,
)},`}</span>
<br />
{zoneConfig.gc?.ttl_seconds && (
<>
<span className="hljs-label">gc.ttlseconds = </span>
<span className="hljs-built_in">{`${zoneConfig.gc.ttl_seconds},`}</span>
<br />
</>
)}
<span className="hljs-label">num_replicas = </span>
<span className="hljs-built_in">{`${zoneConfig.num_replicas},`}</span>
<br />
<span className="hljs-label">constraints = ['</span>
<span className="hljs-built_in">{String(zoneConfig.constraints)}</span>
'],
<br />
<span className="hljs-label">lease_preferences = [['</span>
<span className="hljs-built_in">
{String(zoneConfig.lease_preferences)}
</span>
']]
</span>
);
};

render() {
const { value, zone } = this.props;
const { value, secondaryValue } = this.props;
return (
<>
<span className={cx("sql-highlight")} ref={this.preNode}>
{value}
</span>
{zone && (
{secondaryValue && (
<>
<div className={cx("higlight-divider")} />
{this.renderZone()}
<span className={cx("sql-highlight")} ref={this.preNodeSecondary}>
{secondaryValue}
</span>
</>
)}
</>
Expand Down

0 comments on commit c40ec31

Please sign in to comment.