github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/devtools/containers/raftMessages/index.tsx (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import _ from "lodash"; 12 import React from "react"; 13 import { connect } from "react-redux"; 14 import { createSelector } from "reselect"; 15 import {RouteComponentProps, withRouter} from "react-router-dom"; 16 17 import { refreshLiveness, refreshNodes } from "src/redux/apiReducers"; 18 import { hoverOff as hoverOffAction, hoverOn as hoverOnAction, hoverStateSelector, HoverState } from "src/redux/hover"; 19 import { NodesSummary, nodesSummarySelector } from "src/redux/nodes"; 20 import { AdminUIState } from "src/redux/state"; 21 import { nodeIDAttr } from "src/util/constants"; 22 import { GraphDashboardProps, storeIDsForNode } from "src/views/cluster/containers/nodeGraphs/dashboards/dashboardUtils"; 23 import TimeScaleDropdown from "src/views/cluster/containers/timescale"; 24 import Dropdown, { DropdownOption } from "src/views/shared/components/dropdown"; 25 import { PageConfig, PageConfigItem } from "src/views/shared/components/pageconfig"; 26 import { MetricsDataProvider } from "src/views/shared/containers/metricDataProvider"; 27 import messagesDashboard from "./messages"; 28 import { getMatchParamByName } from "src/util/query"; 29 30 interface NodeGraphsOwnProps { 31 refreshNodes: typeof refreshNodes; 32 refreshLiveness: typeof refreshLiveness; 33 hoverOn: typeof hoverOnAction; 34 hoverOff: typeof hoverOffAction; 35 nodesQueryValid: boolean; 36 livenessQueryValid: boolean; 37 nodesSummary: NodesSummary; 38 hoverState: HoverState; 39 } 40 41 type RaftMessagesProps = NodeGraphsOwnProps & RouteComponentProps; 42 43 export class RaftMessages extends React.Component<RaftMessagesProps> { 44 /** 45 * Selector to compute node dropdown options from the current node summary 46 * collection. 47 */ 48 private nodeDropdownOptions = createSelector( 49 (summary: NodesSummary) => summary.nodeStatuses, 50 (summary: NodesSummary) => summary.nodeDisplayNameByID, 51 (nodeStatuses, nodeDisplayNameByID): DropdownOption[] => { 52 const base = [{value: "", label: "Cluster"}]; 53 return base.concat(_.map(nodeStatuses, (ns) => { 54 return { 55 value: ns.desc.node_id.toString(), 56 label: nodeDisplayNameByID[ns.desc.node_id], 57 }; 58 })); 59 }, 60 ); 61 62 refresh(props = this.props) { 63 if (!props.nodesQueryValid) { 64 props.refreshNodes(); 65 } 66 if (!props.livenessQueryValid) { 67 props.refreshLiveness(); 68 } 69 } 70 71 setClusterPath(nodeID: string) { 72 const push = this.props.history.push; 73 if (!_.isString(nodeID) || nodeID === "") { 74 push("/raft/messages/all/"); 75 } else { 76 push(`/raft/messages/node/${nodeID}`); 77 } 78 } 79 80 nodeChange = (selected: DropdownOption) => { 81 this.setClusterPath(selected.value); 82 } 83 84 componentDidMount() { 85 this.refresh(); 86 } 87 88 componentDidUpdate(props: RaftMessagesProps) { 89 this.refresh(props); 90 } 91 92 render() { 93 const { match, nodesSummary, hoverState, hoverOn, hoverOff } = this.props; 94 95 const selectedNode = getMatchParamByName(match, nodeIDAttr) || ""; 96 const nodeSources = (selectedNode !== "") ? [selectedNode] : null; 97 98 // When "all" is the selected source, some graphs display a line for every 99 // node in the cluster using the nodeIDs collection. However, if a specific 100 // node is already selected, these per-node graphs should only display data 101 // only for the selected node. 102 const nodeIDs = nodeSources ? nodeSources : nodesSummary.nodeIDs; 103 104 // If a single node is selected, we need to restrict the set of stores 105 // queried for per-store metrics (only stores that belong to that node will 106 // be queried). 107 const storeSources = nodeSources ? storeIDsForNode(nodesSummary, nodeSources[0]) : null; 108 109 // tooltipSelection is a string used in tooltips to reference the currently 110 // selected nodes. This is a prepositional phrase, currently either "across 111 // all nodes" or "on node X". 112 const tooltipSelection = (nodeSources && nodeSources.length === 1) 113 ? `on node ${nodeSources[0]}` 114 : "across all nodes"; 115 116 const dashboardProps: GraphDashboardProps = { 117 nodeIDs, 118 nodesSummary, 119 nodeSources, 120 storeSources, 121 tooltipSelection, 122 }; 123 124 // Generate graphs for the current dashboard, wrapping each one in a 125 // MetricsDataProvider with a unique key. 126 const graphs = messagesDashboard(dashboardProps); 127 const graphComponents = _.map(graphs, (graph, idx) => { 128 const key = `nodes.raftMessages.${idx}`; 129 return ( 130 <div key={key}> 131 <MetricsDataProvider id={key}> 132 { React.cloneElement(graph, { hoverOn, hoverOff, hoverState }) } 133 </MetricsDataProvider> 134 </div> 135 ); 136 }); 137 138 return ( 139 <div> 140 <PageConfig> 141 <PageConfigItem> 142 <Dropdown 143 title="Graph" 144 options={this.nodeDropdownOptions(this.props.nodesSummary)} 145 selected={selectedNode} 146 onChange={this.nodeChange} 147 /> 148 </PageConfigItem> 149 <PageConfigItem> 150 <TimeScaleDropdown /> 151 </PageConfigItem> 152 </PageConfig> 153 <div className="section l-columns"> 154 <div className="chart-group l-columns__left"> 155 { graphComponents } 156 </div> 157 </div> 158 </div> 159 ); 160 } 161 } 162 163 const mapStateToProps = (state: AdminUIState) => ({ // RootState contains declaration for whole state 164 nodesSummary: nodesSummarySelector(state), 165 nodesQueryValid: state.cachedData.nodes.valid, 166 livenessQueryValid: state.cachedData.nodes.valid, 167 hoverState: hoverStateSelector(state), 168 }); 169 170 const mapDispatchToProps = { 171 refreshNodes, 172 refreshLiveness, 173 hoverOn: hoverOnAction, 174 hoverOff: hoverOffAction, 175 }; 176 177 export default withRouter(connect(mapStateToProps, mapDispatchToProps)(RaftMessages));