-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ui: add data distribution screen (aka replica matrix)
with collapsible tree matrix widget Release note (admin ui change): Add "data distribution" debug page, showing how table data is distributed across nodes, as well as the zone configs which are affecting that distribution.
- Loading branch information
Pete Vilter
committed
Jun 14, 2018
1 parent
2a2f765
commit 721de4c
Showing
11 changed files
with
1,261 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
pkg/ui/src/views/cluster/containers/dataDistribution/index.styl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
.data-distribution | ||
display: flex | ||
|
||
&__zone-config-sidebar | ||
padding-right 20px | ||
|
||
.zone-config | ||
padding-top 10px | ||
|
||
&__raw-yaml | ||
padding-top 5px |
216 changes: 216 additions & 0 deletions
216
pkg/ui/src/views/cluster/containers/dataDistribution/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
import _ from "lodash"; | ||
import React from "react"; | ||
import { createSelector } from "reselect"; | ||
import { connect } from "react-redux"; | ||
import Helmet from "react-helmet"; | ||
|
||
import Loading from "src/views/shared/components/loading"; | ||
import spinner from "assets/spinner.gif"; | ||
import { ToolTipWrapper } from "src/views/shared/components/toolTip"; | ||
import * as docsURL from "src/util/docs"; | ||
import { FixLong } from "src/util/fixLong"; | ||
import { cockroach } from "src/js/protos"; | ||
import { AdminUIState } from "src/redux/state"; | ||
import { refreshDataDistribution, refreshNodes, refreshLiveness } from "src/redux/apiReducers"; | ||
import { LocalityTree, selectLocalityTree } from "src/redux/localities"; | ||
import ReplicaMatrix, { SchemaObject } from "./replicaMatrix"; | ||
import { TreeNode, TreePath } from "./tree"; | ||
import "./index.styl"; | ||
|
||
type DataDistributionResponse = cockroach.server.serverpb.DataDistributionResponse; | ||
type NodeDescriptor = cockroach.roachpb.NodeDescriptor$Properties; | ||
type ZoneConfig$Properties = cockroach.server.serverpb.DataDistributionResponse.ZoneConfig$Properties; | ||
|
||
const ZONE_CONFIG_TEXT = ( | ||
<span> | ||
Zone configurations | ||
(<a href={docsURL.configureReplicationZones} target="_blank">see documentation</a>) | ||
control how CockroachDB distributes data across nodes. | ||
</span> | ||
); | ||
|
||
interface DataDistributionProps { | ||
dataDistribution: DataDistributionResponse; | ||
localityTree: LocalityTree; | ||
sortedZoneConfigs: ZoneConfig$Properties[]; | ||
} | ||
|
||
class DataDistribution extends React.Component<DataDistributionProps> { | ||
|
||
renderZoneConfigs() { | ||
return ( | ||
<div className="zone-config-list"> | ||
<ul> | ||
{this.props.sortedZoneConfigs.map((zoneConfig) => ( | ||
<li key={zoneConfig.cli_specifier} className="zone-config"> | ||
<h3>{zoneConfig.cli_specifier}</h3> | ||
<pre className="zone-config__raw-yaml"> | ||
{zoneConfig.config_yaml} | ||
</pre> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
} | ||
|
||
getCellValue = (dbPath: TreePath, nodePath: TreePath): number => { | ||
const [dbName, tableName] = dbPath; | ||
const nodeID = nodePath[nodePath.length - 1]; | ||
const databaseInfo = this.props.dataDistribution.database_info; | ||
|
||
const res = databaseInfo[dbName].table_info[tableName].replica_count_by_node_id[nodeID]; | ||
if (!res) { | ||
return 0; | ||
} | ||
return FixLong(res).toInt(); | ||
} | ||
|
||
render() { | ||
const nodeTree = nodeTreeFromLocalityTree("Cluster", this.props.localityTree); | ||
|
||
const databaseInfo = this.props.dataDistribution.database_info; | ||
const dbTree: TreeNode<SchemaObject> = { | ||
name: "Cluster", | ||
data: { dbName: null, tableName: null }, | ||
children: _.map(databaseInfo, (dbInfo, dbName) => ({ | ||
name: dbName, | ||
data: { dbName }, | ||
children: _.map(dbInfo.table_info, (_tableInfo, tableName) => ({ | ||
name: tableName, | ||
data: { dbName, tableName }, | ||
})), | ||
})), | ||
}; | ||
|
||
return ( | ||
<div className="data-distribution"> | ||
<div className="data-distribution__zone-config-sidebar"> | ||
<h2> | ||
Zone Configs{" "} | ||
<div className="section-heading__tooltip"> | ||
<ToolTipWrapper text={ZONE_CONFIG_TEXT}> | ||
<div className="section-heading__tooltip-hover-area"> | ||
<div className="section-heading__info-icon">i</div> | ||
</div> | ||
</ToolTipWrapper> | ||
</div> | ||
</h2> | ||
{this.renderZoneConfigs()} | ||
</div> | ||
<div> | ||
<ReplicaMatrix | ||
cols={nodeTree} | ||
rows={dbTree} | ||
getValue={this.getCellValue} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
interface DataDistributionPageProps { | ||
dataDistribution: DataDistributionResponse; | ||
localityTree: LocalityTree; | ||
sortedZoneConfigs: ZoneConfig$Properties[]; | ||
refreshDataDistribution: typeof refreshDataDistribution; | ||
refreshNodes: typeof refreshNodes; | ||
refreshLiveness: typeof refreshLiveness; | ||
} | ||
|
||
class DataDistributionPage extends React.Component<DataDistributionPageProps> { | ||
|
||
componentDidMount() { | ||
this.props.refreshDataDistribution(); | ||
this.props.refreshNodes(); | ||
this.props.refreshLiveness(); | ||
} | ||
|
||
componentWillReceiveProps() { | ||
this.props.refreshDataDistribution(); | ||
this.props.refreshNodes(); | ||
this.props.refreshLiveness(); | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<Helmet> | ||
<title>Data Distribution</title> | ||
</Helmet> | ||
<section className="section"> | ||
<h1>Data Distribution</h1> | ||
</section> | ||
<section className="section"> | ||
<Loading | ||
className="loading-image loading-image__spinner-left" | ||
loading={!this.props.dataDistribution || !this.props.localityTree} | ||
image={spinner} | ||
> | ||
<DataDistribution | ||
localityTree={this.props.localityTree} | ||
dataDistribution={this.props.dataDistribution} | ||
sortedZoneConfigs={this.props.sortedZoneConfigs} | ||
/> | ||
</Loading> | ||
</section> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
const sortedZoneConfigs = createSelector( | ||
(state: AdminUIState) => state.cachedData.dataDistribution, | ||
(dataDistributionState) => { | ||
if (!dataDistributionState.data) { | ||
return null; | ||
} | ||
return _.sortBy(dataDistributionState.data.zone_configs, (zc) => zc.cli_specifier); | ||
}, | ||
); | ||
|
||
// tslint:disable-next-line:variable-name | ||
const DataDistributionPageConnected = connect( | ||
(state: AdminUIState) => ({ | ||
dataDistribution: state.cachedData.dataDistribution.data, | ||
sortedZoneConfigs: sortedZoneConfigs(state), | ||
localityTree: selectLocalityTree(state), | ||
}), | ||
{ | ||
refreshDataDistribution, | ||
refreshNodes, | ||
refreshLiveness, | ||
}, | ||
)(DataDistributionPage); | ||
|
||
export default DataDistributionPageConnected; | ||
|
||
// Helpers | ||
|
||
function nodeTreeFromLocalityTree( | ||
rootName: string, | ||
localityTree: LocalityTree, | ||
): TreeNode<NodeDescriptor> { | ||
const children: TreeNode<any>[] = []; | ||
|
||
// Add child localities. | ||
_.forEach(localityTree.localities, (valuesForKey, key) => { | ||
_.forEach(valuesForKey, (subLocalityTree, value) => { | ||
children.push(nodeTreeFromLocalityTree(`${key}=${value}`, subLocalityTree)); | ||
}); | ||
}); | ||
|
||
// Add child nodes. | ||
_.forEach(localityTree.nodes, (node) => { | ||
children.push({ | ||
name: node.desc.node_id.toString(), | ||
data: node.desc, | ||
}); | ||
}); | ||
|
||
return { | ||
name: rootName, | ||
children: children, | ||
}; | ||
} |
43 changes: 43 additions & 0 deletions
43
pkg/ui/src/views/cluster/containers/dataDistribution/replicaMatrix.styl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
.matrix | ||
background-color white | ||
border-collapse collapse | ||
border-bottom 1px solid lightgrey | ||
border-top 1px solid lightgrey | ||
|
||
thead | ||
border-bottom 1px solid black | ||
|
||
th, td | ||
padding 5px | ||
border-left 1px solid lightgrey | ||
border-right 1px solid lightgrey | ||
|
||
td.value | ||
text-align center | ||
|
||
&__metric-label | ||
font-style italic | ||
|
||
&__row--internal-node | ||
cursor pointer | ||
|
||
&__row--internal-node:hover | ||
background-color rgb(240, 240, 240) | ||
|
||
&__row-header | ||
text-align left | ||
|
||
&__row-header--internal-node | ||
font-weight bold | ||
|
||
&__column-header | ||
font-weight bold | ||
|
||
&__column-header--internal-node | ||
cursor pointer | ||
|
||
&__column-header--internal-node:hover | ||
background-color rgb(240, 240, 240) | ||
|
||
&__cell-value | ||
text-align right |
Oops, something went wrong.