You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ja...@apache.org on 2019/03/15 12:31:51 UTC

[lucene-solr] branch branch_8x updated: SOLR-13244: Nodes view fails when a node is temporarily down

This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/branch_8x by this push:
     new 4540fa4  SOLR-13244: Nodes view fails when a node is temporarily down
4540fa4 is described below

commit 4540fa427a4dfaf3ae7947c6aa9b6d6d456e43cc
Author: Jan Høydahl <ja...@apache.org>
AuthorDate: Fri Mar 15 13:30:02 2019 +0100

    SOLR-13244: Nodes view fails when a node is temporarily down
    
    (cherry picked from commit 8f29d1eaadce5ca2c79b1f48161a8dda696d9952)
---
 solr/CHANGES.txt                                |  2 +
 solr/webapp/web/css/angular/cloud.css           | 11 +++
 solr/webapp/web/js/angular/controllers/cloud.js | 27 +++++++-
 solr/webapp/web/partials/cloud.html             | 90 +++++++++++++------------
 4 files changed, 85 insertions(+), 45 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 9769f64..5c5f752 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -99,6 +99,8 @@ Bug Fixes
 * 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)
 
+* SOLR-13244: Admin UI Nodes view fails and is empty when a node is temporarily down (janhoy)
+
 Improvements
 ----------------------
 * SOLR-12999: Index replication could delete segments before downloading segments from master if there is not enough
diff --git a/solr/webapp/web/css/angular/cloud.css b/solr/webapp/web/css/angular/cloud.css
index 5c8ce45..c702c7a 100644
--- a/solr/webapp/web/css/angular/cloud.css
+++ b/solr/webapp/web/css/angular/cloud.css
@@ -535,6 +535,17 @@ limitations under the License.
   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 */
 #content #cloud #controls,
 #content #cloud #frame #zk-status-content #zk-controls
diff --git a/solr/webapp/web/js/angular/controllers/cloud.js b/solr/webapp/web/js/angular/controllers/cloud.js
index d6730d5..0d49df2 100644
--- a/solr/webapp/web/js/angular/controllers/cloud.js
+++ b/solr/webapp/web/js/angular/controllers/cloud.js
@@ -107,6 +107,10 @@ function isNumeric(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) {
   $scope.pageSize = 10;
   $scope.showNodes = true;
@@ -160,6 +164,17 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
     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
   $scope.initClusterState = function() {
     var nodes = {};
@@ -183,6 +198,7 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
           for (var replicaName in replicas) {
             var core = replicas[replicaName];
             core.name = replicaName;
+            core.label = coreNameToLabel(core['core']);
             core.collection = collection.name;
             core.shard = shard.name;
             core.shard_state = shard.state;
@@ -320,7 +336,15 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
         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.prevEnabled = $scope.from - pageSize >= 0;
     nodesToShow.sort();
@@ -410,7 +434,6 @@ var nodesSubController = function($scope, Collections, System, Metrics) {
                   size = (typeof size !== 'undefined') ? size : 0;
                   core['sizeInBytes'] = 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 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'];
diff --git a/solr/webapp/web/partials/cloud.html b/solr/webapp/web/partials/cloud.html
index 59e9a40..1e64d29 100644
--- a/solr/webapp/web/partials/cloud.html
+++ b/solr/webapp/web/partials/cloud.html
@@ -149,88 +149,92 @@ limitations under the License.
           </tr>
         </thead>
         <tbody>
-          <tr ng-repeat="key in nodesToShow | orderBy:'key.order'" ng-init="n = nodes[key]">
-              <td rowspan="{{hosts[n.host].nodes.length}}" ng-show="isFirstNodeForHost(key)">
-                <div class="host-name">{{n.host}}</div>
-                <span class="host-spec" ng-show="!showDetails[n.host]">
-                  <span title="{{n.system.system.uptime}}">{{n.system.system.name}}</span> 
-                  <span title="free: {{n.memFree}}">{{n.memTotal}}</span>
-                  <span title="{{n.system.jvm.name}} {{n.system.jvm.version}}">Java {{n.system.jvm.spec.version}}</span>
-                  <br/>Load: {{n.loadAvg}}
+          <tr ng-repeat="key in nodesToShow | orderBy:'key.order'" ng-init="n = nodes[key]; h = nodes[firstLiveNodeForHost(key)]">
+              <td rowspan="{{hosts[h.host].nodes.length}}" ng-show="isFirstNodeForHost(key)">
+                <div class="host-name">{{h.host}}</div>
+                <span class="host-spec" ng-show="!showDetails[h.host]">
+                  <span title="{{h.system.system.uptime}}">{{h.system.system.name}}</span> 
+                  <span title="free: {{h.memFree}}">{{h.memTotal}}</span>
+                  <span title="{{h.system.jvm.name}} {{h.system.jvm.version}}">Java {{h.system.jvm.spec.version}}</span>
+                  <br/>Load: {{h.loadAvg}}
                 </span>
-                <div class="host-spec" ng-show="showDetails[n.host]">
-                  {{n.system.system.name}} {{n.system.system.version}}, {{n.system.system.availableProcessors}}cpu<br/>
-                  Uptime: {{n.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/> 
-                  File descriptors: {{n.openFileDescriptorCount}}/{{n.maxFileDescriptorCount}}<br/>
-                  Disk: <span class="{{n.diskUsedPctStyle}}" title="Nodes may use other disks too">{{n.diskTotal}} used: {{n.diskUsedPct}}%</span><br/>
-                  Load: {{n.loadAvg}}
+                <div class="host-spec" ng-show="showDetails[h.host]">
+                  {{h.system.system.name}} {{h.system.system.version}}, {{h.system.system.availableProcessors}}cpu<br/>
+                  Uptime: {{h.uptime}}<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: {{h.openFileDescriptorCount}}/{{h.maxFileDescriptorCount}}<br/>
+                  Disk: <span class="{{h.diskUsedPctStyle}}" title="Nodes may use other disks too">{{h.diskTotal}} used: {{h.diskUsedPct}}%</span><br/>
+                  Load: {{h.loadAvg}}
                 </div>
-                <div class="node-spec" ng-click="toggleHostDetails(n.host)">
-                  <a ng-show="showDetails[n.host]">hide details...</a>
-                  <a ng-show="!showDetails[n.host]">show details...</a>
+                <div class="node-spec" ng-click="toggleHostDetails(h.host)">
+                  <a ng-show="showDetails[h.host]">hide details...</a>
+                  <a ng-show="!showDetails[h.host]">show details...</a>
                 </div>
               </td>
-              <td><div class="node-name"><a href="{{n.base_url}}">{{key.replace(n.host+':', '')}}</a></div>
-                Uptime: {{n.jvmUptime}}<br/>
-                <div class="node-spec" ng-show="showDetails[key]">
-                  Java <span title="{{n.system.jvm.jre.vendor}}">{{n.system.jvm.jre.version}}</span><br/>
-                  Solr {{n.system.lucene['solr-impl-version'].split(" ")[0]}}<br>
-                </div>
-                <div class="node-spec" ng-click="toggleDetails(key)">
-                  <a ng-show="showDetails[key]">hide details...</a>
-                  <a ng-show="!showDetails[key]">show details...</a>
+              <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/>
+                  <div class="node-spec" ng-show="showDetails[key]">
+                    Java <span title="{{n.system.jvm.jre.vendor}}">{{n.system.jvm.jre.version}}</span><br/>
+                    Solr {{n.system.lucene['solr-impl-version'].split(" ")[0]}}<br>
+                  </div>
+                  <div class="node-spec" ng-click="toggleDetails(key)">
+                    <a ng-show="showDetails[key]">hide details...</a>
+                    <a ng-show="!showDetails[key]">show details...</a>
+                  </div>
                 </div>
               </td>
-              <td>
-                <div class="node-cpu">
+              <td ng-class="{'dead-node': n.dead}">
+                <div class="node-cpu" ng-show="!n.dead">
                   <span class="{{n.cpuPctStyle}}">{{n.cpuPct}}%</span>
                 </div>
               </td>
-              <td>
-                <div class="node-heap" title="total: {{n.heapTotal}} free: {{n.heapFree}} used%: {{n.heapUsedPct}}%">
+              <td ng-class="{'dead-node': n.dead}">
+                <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>
                 </div>
-                <div class="node-spec" ng-show="showDetails[key]">
+                <div class="node-spec" ng-show="showDetails[key] && !n.dead">
                   Max: {{n.heapTotal}}<br/>
                   Used: {{n.heapUsed}}
                 </div>
               </td>
-              <td class="scroll-height-250">
+              <td class="scroll-height-250" ng-class="{'dead-node': n.dead}">
                 <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}}
                   </div>
-                  <div class="node-spec" ng-show="showDetails[key]">
+                  <div class="node-spec" ng-show="showDetails[key] && !n.dead">
                     Total #docs: {{n.numDocs}}<br/>
                     Avg size/doc: {{n.sizePerDoc}}
                   </div>
-                  <div id="chart{{n.id}}" ng-show="showDetails[key]"></div>
+                  <div id="chart{{n.id}}" ng-show="showDetails[key] && !n.dead"></div>
                 </div>
               </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>
               </td>
-              <td>
+              <td ng-class="{'dead-node': n.dead}">
                 <div ng-show="!n.collections">(none)</div>
                 <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 class="more" ng-show="n.collections.length > 2 && !showDetails[key]">
                   <a ng-click="toggleDetails(key)">({{n.collections.length - 2}} more...)</a>
                 </div>
               </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-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)
-                    <ul class="core-details" ng-show="showDetails[key]">
+                    <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>
+                    <div ng-show="n.dead">{{ core.label }}</div>
+                    <ul class="core-details" ng-show="showDetails[key] && !n.dead" >
                       <li>deleted: {{core.deletedDocsHuman}}</li>
                       <li>warmupTime: {{core.warmupTime}}</li>
                       <li ng-show="core.numDocs > 0">avg size/doc: {{core.avgSizePerDoc}}</li>
                     </ul>
                   </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>
                   </div>
               </td>