SOLR-13244: Nodes view fails when a node is temporarily down

This commit is contained in:
Jan Høydahl 2019-03-15 13:30:02 +01:00 committed by GitHub
parent 571b307266
commit 8f29d1eaad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 45 deletions

View File

@ -120,6 +120,8 @@ Bug Fixes
* SOLR-13284: NullPointerException with 500 http status on omitted or wrong wt param. * SOLR-13284: NullPointerException with 500 http status on omitted or wrong wt param.
It's fixed by fallback to json (Munendra S N via Mikhail Khludnev) It's fixed by fallback to json (Munendra S N via Mikhail Khludnev)
* SOLR-13244: Admin UI Nodes view fails and is empty when a node is temporarily down (janhoy)
Improvements Improvements
---------------------- ----------------------
* SOLR-12999: Index replication could delete segments before downloading segments from master if there is not enough * SOLR-12999: Index replication could delete segments before downloading segments from master if there is not enough

View File

@ -535,6 +535,17 @@ limitations under the License.
color: red; color: red;
} }
#content #cloud #nodes-content .node-down
{
font-weight: bold;
font-size: 12px;
}
#content #cloud #nodes-content .dead-node
{
background-color: salmon;
}
/* Styling of reload and details buttons */ /* Styling of reload and details buttons */
#content #cloud #controls, #content #cloud #controls,
#content #cloud #frame #zk-status-content #zk-controls #content #cloud #frame #zk-status-content #zk-controls

View File

@ -107,6 +107,10 @@ function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n); return !isNaN(parseFloat(n)) && isFinite(n);
} }
function coreNameToLabel(name) {
return name.replace(/(.*?)_shard((\d+_?)+)_replica_?[ntp]?(\d+)/, '\$1_s\$2r\$4');
}
var nodesSubController = function($scope, Collections, System, Metrics) { var nodesSubController = function($scope, Collections, System, Metrics) {
$scope.pageSize = 10; $scope.pageSize = 10;
$scope.showNodes = true; $scope.showNodes = true;
@ -160,6 +164,17 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
return nodesInHost[0] === node; return nodesInHost[0] === node;
}; };
// Returns the first live node for this host, to make sure we pick host-level metrics from a live node
$scope.firstLiveNodeForHost = function(key) {
var hostName = key.split(":")[0];
var liveNodesInHost = $scope.filteredNodes.filter(function (key) {
return key.startsWith(hostName);
}).filter(function (key) {
return $scope.live_nodes.includes(key);
});
return liveNodesInHost.length > 0 ? liveNodesInHost[0] : key;
};
// Initializes the cluster state, list of nodes, collections etc // Initializes the cluster state, list of nodes, collections etc
$scope.initClusterState = function() { $scope.initClusterState = function() {
var nodes = {}; var nodes = {};
@ -183,6 +198,7 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
for (var replicaName in replicas) { for (var replicaName in replicas) {
var core = replicas[replicaName]; var core = replicas[replicaName];
core.name = replicaName; core.name = replicaName;
core.label = coreNameToLabel(core['core']);
core.collection = collection.name; core.collection = collection.name;
core.shard = shard.name; core.shard = shard.name;
core.shard_state = shard.state; core.shard_state = shard.state;
@ -320,7 +336,15 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
nodesToShow = nodesToShow.concat(hosts[hostName]['nodes']); nodesToShow = nodesToShow.concat(hosts[hostName]['nodes']);
} }
} }
nodesParam = nodesToShow.join(','); nodesParam = nodesToShow.filter(function (node) {
return live_nodes.includes(node);
}).join(',');
var deadNodes = nodesToShow.filter(function (node) {
return !live_nodes.includes(node);
});
deadNodes.forEach(function (node) {
nodes[node]['dead'] = true;
});
$scope.nextEnabled = $scope.from + pageSize < filteredHosts.length; $scope.nextEnabled = $scope.from + pageSize < filteredHosts.length;
$scope.prevEnabled = $scope.from - pageSize >= 0; $scope.prevEnabled = $scope.from - pageSize >= 0;
nodesToShow.sort(); nodesToShow.sort();
@ -410,7 +434,6 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
size = (typeof size !== 'undefined') ? size : 0; size = (typeof size !== 'undefined') ? size : 0;
core['sizeInBytes'] = size; core['sizeInBytes'] = size;
core['size'] = bytesToSize(size); core['size'] = bytesToSize(size);
core['label'] = core['core'].replace(/(.*?)_shard((\d+_?)+)_replica_?[ntp]?(\d+)/, '\$1_s\$2r\$4');
if (core['shard_state'] !== 'active' || core['state'] !== 'active') { if (core['shard_state'] !== 'active' || core['state'] !== 'active') {
// If core state is not active, display the real state, or if shard is inactive, display that // If core state is not active, display the real state, or if shard is inactive, display that
var labelState = (core['state'] !== 'active') ? core['state'] : core['shard_state']; var labelState = (core['state'] !== 'active') ? core['state'] : core['shard_state'];

View File

@ -149,29 +149,31 @@ limitations under the License.
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="key in nodesToShow | orderBy:'key.order'" ng-init="n = nodes[key]"> <tr ng-repeat="key in nodesToShow | orderBy:'key.order'" ng-init="n = nodes[key]; h = nodes[firstLiveNodeForHost(key)]">
<td rowspan="{{hosts[n.host].nodes.length}}" ng-show="isFirstNodeForHost(key)"> <td rowspan="{{hosts[h.host].nodes.length}}" ng-show="isFirstNodeForHost(key)">
<div class="host-name">{{n.host}}</div> <div class="host-name">{{h.host}}</div>
<span class="host-spec" ng-show="!showDetails[n.host]"> <span class="host-spec" ng-show="!showDetails[h.host]">
<span title="{{n.system.system.uptime}}">{{n.system.system.name}}</span> <span title="{{h.system.system.uptime}}">{{h.system.system.name}}</span>
<span title="free: {{n.memFree}}">{{n.memTotal}}</span> <span title="free: {{h.memFree}}">{{h.memTotal}}</span>
<span title="{{n.system.jvm.name}} {{n.system.jvm.version}}">Java {{n.system.jvm.spec.version}}</span> <span title="{{h.system.jvm.name}} {{h.system.jvm.version}}">Java {{h.system.jvm.spec.version}}</span>
<br/>Load: {{n.loadAvg}} <br/>Load: {{h.loadAvg}}
</span> </span>
<div class="host-spec" ng-show="showDetails[n.host]"> <div class="host-spec" ng-show="showDetails[h.host]">
{{n.system.system.name}} {{n.system.system.version}}, {{n.system.system.availableProcessors}}cpu<br/> {{h.system.system.name}} {{h.system.system.version}}, {{h.system.system.availableProcessors}}cpu<br/>
Uptime: {{n.uptime}}<br/> Uptime: {{h.uptime}}<br/>
<span title="Used: {{n.memUsed}} - includes OS file-cache, and it is normal for it to approach 100%">Memory: {{n.memTotal}}</span><br/> <span title="Used: {{h.memUsed}} - includes OS file-cache, and it is normal for it to approach 100%">Memory: {{h.memTotal}}</span><br/>
File descriptors: {{n.openFileDescriptorCount}}/{{n.maxFileDescriptorCount}}<br/> File descriptors: {{h.openFileDescriptorCount}}/{{h.maxFileDescriptorCount}}<br/>
Disk: <span class="{{n.diskUsedPctStyle}}" title="Nodes may use other disks too">{{n.diskTotal}} used: {{n.diskUsedPct}}%</span><br/> Disk: <span class="{{h.diskUsedPctStyle}}" title="Nodes may use other disks too">{{h.diskTotal}} used: {{h.diskUsedPct}}%</span><br/>
Load: {{n.loadAvg}} Load: {{h.loadAvg}}
</div> </div>
<div class="node-spec" ng-click="toggleHostDetails(n.host)"> <div class="node-spec" ng-click="toggleHostDetails(h.host)">
<a ng-show="showDetails[n.host]">hide details...</a> <a ng-show="showDetails[h.host]">hide details...</a>
<a ng-show="!showDetails[n.host]">show details...</a> <a ng-show="!showDetails[h.host]">show details...</a>
</div> </div>
</td> </td>
<td><div class="node-name"><a href="{{n.base_url}}">{{key.replace(n.host+':', '')}}</a></div> <td ng-class="{'dead-node': n.dead}"><div class="node-name"><a href="{{n.base_url}}">{{key.replace(n.host+':', '')}}</a></div>
<div ng-show="n.dead" class="node-down">(DEAD)</div>
<div ng-show="!n.dead">
Uptime: {{n.jvmUptime}}<br/> Uptime: {{n.jvmUptime}}<br/>
<div class="node-spec" ng-show="showDetails[key]"> <div class="node-spec" ng-show="showDetails[key]">
Java <span title="{{n.system.jvm.jre.vendor}}">{{n.system.jvm.jre.version}}</span><br/> Java <span title="{{n.system.jvm.jre.vendor}}">{{n.system.jvm.jre.version}}</span><br/>
@ -181,56 +183,58 @@ limitations under the License.
<a ng-show="showDetails[key]">hide details...</a> <a ng-show="showDetails[key]">hide details...</a>
<a ng-show="!showDetails[key]">show details...</a> <a ng-show="!showDetails[key]">show details...</a>
</div> </div>
</div>
</td> </td>
<td> <td ng-class="{'dead-node': n.dead}">
<div class="node-cpu"> <div class="node-cpu" ng-show="!n.dead">
<span class="{{n.cpuPctStyle}}">{{n.cpuPct}}%</span> <span class="{{n.cpuPctStyle}}">{{n.cpuPct}}%</span>
</div> </div>
</td> </td>
<td> <td ng-class="{'dead-node': n.dead}">
<div class="node-heap" title="total: {{n.heapTotal}} free: {{n.heapFree}} used%: {{n.heapUsedPct}}%"> <div class="node-heap" title="total: {{n.heapTotal}} free: {{n.heapFree}} used%: {{n.heapUsedPct}}%" ng-show="!n.dead">
<span class="{{n.heapUsedPctStyle}}">{{n.heapUsedPct}}%</span> <span class="{{n.heapUsedPctStyle}}">{{n.heapUsedPct}}%</span>
</div> </div>
<div class="node-spec" ng-show="showDetails[key]"> <div class="node-spec" ng-show="showDetails[key] && !n.dead">
Max: {{n.heapTotal}}<br/> Max: {{n.heapTotal}}<br/>
Used: {{n.heapUsed}} Used: {{n.heapUsed}}
</div> </div>
</td> </td>
<td class="scroll-height-250"> <td class="scroll-height-250" ng-class="{'dead-node': n.dead}">
<div> <div>
<div class="node-disk" title="Available disk: {{n.diskTotal}} free: {{n.diskFree}} used by this node: {{n.size}}"> <div class="node-disk" title="Available disk: {{n.diskTotal}} free: {{n.diskFree}} used by this node: {{n.size}}" ng-show="!n.dead">
{{n.size}} {{n.size}}
</div> </div>
<div class="node-spec" ng-show="showDetails[key]"> <div class="node-spec" ng-show="showDetails[key] && !n.dead">
Total #docs: {{n.numDocs}}<br/> Total #docs: {{n.numDocs}}<br/>
Avg size/doc: {{n.sizePerDoc}} Avg size/doc: {{n.sizePerDoc}}
</div> </div>
<div id="chart{{n.id}}" ng-show="showDetails[key]"></div> <div id="chart{{n.id}}" ng-show="showDetails[key] && !n.dead"></div>
</div> </div>
</td> </td>
<td><div class="node-requests" title="1minRate: {{n.req1minRate}} 5minRate: {{n.req5minRate}} 15minRate: {{n.req15minRate}} p75: {{n.reqp75_ms}} p99: {{n.reqp99_ms}}"> <td ng-class="{'dead-node': n.dead}"><div class="node-requests" title="1minRate: {{n.req1minRate}} 5minRate: {{n.req5minRate}} 15minRate: {{n.req15minRate}} p75: {{n.reqp75_ms}} p99: {{n.reqp99_ms}}" ng-show="!n.dead">
RPM: {{n.req15minRate}}<br/>p95: {{n.reqp95_ms}}ms</div> RPM: {{n.req15minRate}}<br/>p95: {{n.reqp95_ms}}ms</div>
</td> </td>
<td> <td ng-class="{'dead-node': n.dead}">
<div ng-show="!n.collections">(none)</div> <div ng-show="!n.collections">(none)</div>
<div ng-repeat="c in n.collections | limitTo:showDetails[key]?999:2 track by $index"> <div ng-repeat="c in n.collections | limitTo:showDetails[key]?999:2 track by $index">
<a href="{{n.base_url + '/#/~collections/' + c}}">{{ c }}</a> <a href="{{h.base_url + '/#/~collections/' + c}}">{{ c }}</a>
</div> </div>
<div class="more" ng-show="n.collections.length > 2 && !showDetails[key]"> <div class="more" ng-show="n.collections.length > 2 && !showDetails[key]">
<a ng-click="toggleDetails(key)">({{n.collections.length - 2}} more...)</a> <a ng-click="toggleDetails(key)">({{n.collections.length - 2}} more...)</a>
</div> </div>
</td> </td>
<td class="scroll-height-250"> <td class="scroll-height-250" ng-class="{'dead-node': n.dead}">
<div ng-show="!n.cores">(none)</div> <div ng-show="!n.cores">(none)</div>
<div ng-repeat="core in n.cores | limitTo:showDetails[key]?999:2 track by $index"> <div ng-repeat="core in n.cores | limitTo:showDetails[key]?999:2 track by $index">
<a class="{{core.leader ? 'leader' : 'replica'}}" href="{{core.base_url + '/#/' + core.core}}">{{ core.label }}</a> ({{core.numDocsHuman}} docs) <div ng-show="!n.dead"><a class="{{core.leader ? 'leader' : 'replica'}}" href="{{core.base_url + '/#/' + core.core + '/core-overview'}}">{{ core.label }}</a> ({{core.numDocsHuman}} docs)</div>
<ul class="core-details" ng-show="showDetails[key]"> <div ng-show="n.dead">{{ core.label }}</div>
<ul class="core-details" ng-show="showDetails[key] && !n.dead" >
<li>deleted: {{core.deletedDocsHuman}}</li> <li>deleted: {{core.deletedDocsHuman}}</li>
<li>warmupTime: {{core.warmupTime}}</li> <li>warmupTime: {{core.warmupTime}}</li>
<li ng-show="core.numDocs > 0">avg size/doc: {{core.avgSizePerDoc}}</li> <li ng-show="core.numDocs > 0">avg size/doc: {{core.avgSizePerDoc}}</li>
</ul> </ul>
</div> </div>
<div class="more" ng-show="n.cores.length > 2 && !showDetails[key]"> <div class="more" ng-show="n.cores.length > 2 && !showDetails[key] && !n.dead">
<a ng-click="toggleDetails(key)">({{n.cores.length - 2}} more...)</a> <a ng-click="toggleDetails(key)">({{n.cores.length - 2}} more...)</a>
</div> </div>
</td> </td>