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

ddiederen pushed a commit to branch branch-3.6
in repository https://gitbox.apache.org/repos/asf/zookeeper.git


The following commit(s) were added to refs/heads/branch-3.6 by this push:
     new 5c59841  ZOOKEEPER-4360: Avoid NPE during gauge execution if the 
leader is not set on FOLLOWER node
5c59841 is described below

commit 5c598414a67d67f3b5cbbb41c7ca5888ccc454d7
Author: Nicolò Boschi <[email protected]>
AuthorDate: Thu Sep 2 16:44:43 2021 +0000

    ZOOKEEPER-4360: Avoid NPE during gauge execution if the leader is not set 
on FOLLOWER node
    
    On a follower node, we had this error
    ```
    ago 20, 2021 1:46:28 PM org.apache.catalina.core.StandardWrapperValve invoke
    GRAVE: Servlet.service() for servlet [metrics] in context with path 
[/metrics] threw exception
    java.lang.NullPointerException: Cannot invoke 
"org.apache.zookeeper.server.quorum.Leader.getProposalStats()" because the 
return value of 
"org.apache.zookeeper.server.quorum.LeaderZooKeeperServer.getLeader()" is null
            at 
org.apache.zookeeper.server.quorum.LeaderZooKeeperServer.lambda$registerMetrics$5(LeaderZooKeeperServer.java:122)
            at 
magnews.zookeeper.ZooKeeperMetricsProviderAdapter$MetricsContextImpl.lambda$registerGauge$0(ZooKeeperMetricsProviderAdapter.java:91)
    ```
    
    Unfortunately, I'm not able to reproduce this error deterministically
    
    I've added the null check on leader variable, this will cause the gauge 
return value to be not registered (on `DefaultMetricsProvider`) and considered 
as zero on prometheus
    
    Not sure if tests were needed even if the fix is so simple, I can them if 
requested from reviewers
    
    Author: Nicolò Boschi <[email protected]>
    
    Reviewers: Enrico Olivelli <[email protected]>, Damien Diederen 
<[email protected]>
    
    Closes #1743 from nicoloboschi/fix/npe-metrics
    
    (cherry picked from commit 4f515673464e540fa8555b7a3ac85c5d1abe41fb)
    Signed-off-by: Damien Diederen <[email protected]>
---
 .../server/quorum/LeaderZooKeeperServer.java       | 60 ++++++++++++----------
 1 file changed, 34 insertions(+), 26 deletions(-)

diff --git 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java
 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java
index 8834b5f..5a51bc2 100644
--- 
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java
+++ 
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java
@@ -20,6 +20,7 @@ package org.apache.zookeeper.server.quorum;
 
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
 import javax.management.JMException;
 import org.apache.zookeeper.KeeperException.SessionExpiredException;
 import org.apache.zookeeper.jmx.MBeanRegistry;
@@ -99,34 +100,41 @@ public class LeaderZooKeeperServer extends 
QuorumZooKeeperServer {
         super.registerMetrics();
 
         MetricsContext rootContext = 
ServerMetrics.getMetrics().getMetricsProvider().getRootContext();
-
-        rootContext.registerGauge("learners", () -> {
-            return getLeader().getLearners().size();
-        });
-        rootContext.registerGauge("synced_followers", () -> {
-            return getLeader().getForwardingFollowers().size();
-        });
-        rootContext.registerGauge("synced_non_voting_followers", () -> {
-            return getLeader().getNonVotingFollowers().size();
-        });
-
+        rootContext.registerGauge("learners", gaugeWithLeader(
+                (leader) -> leader.getLearners().size())
+        );
+        rootContext.registerGauge("synced_followers", gaugeWithLeader(
+                (leader) -> leader.getForwardingFollowers().size()
+        ));
+        rootContext.registerGauge("synced_non_voting_followers", 
gaugeWithLeader(
+                (leader) -> leader.getNonVotingFollowers().size()
+        ));
         rootContext.registerGauge("synced_observers", 
self::getSynced_observers_metric);
+        rootContext.registerGauge("pending_syncs", gaugeWithLeader(
+                (leader) -> leader.getNumPendingSyncs()
+        ));
+        rootContext.registerGauge("leader_uptime", gaugeWithLeader(
+                (leader) -> leader.getUptime()
+        ));
+        rootContext.registerGauge("last_proposal_size", gaugeWithLeader(
+                (leader) -> leader.getProposalStats().getLastBufferSize()
+        ));
+        rootContext.registerGauge("max_proposal_size", gaugeWithLeader(
+                (leader) -> leader.getProposalStats().getMaxBufferSize()
+        ));
+        rootContext.registerGauge("min_proposal_size", gaugeWithLeader(
+                (leader) -> leader.getProposalStats().getMinBufferSize()
+        ));
+    }
 
-        rootContext.registerGauge("pending_syncs", () -> {
-            return getLeader().getNumPendingSyncs();
-        });
-        rootContext.registerGauge("leader_uptime", () -> {
-            return getLeader().getUptime();
-        });
-        rootContext.registerGauge("last_proposal_size", () -> {
-            return getLeader().getProposalStats().getLastBufferSize();
-        });
-        rootContext.registerGauge("max_proposal_size", () -> {
-            return getLeader().getProposalStats().getMaxBufferSize();
-        });
-        rootContext.registerGauge("min_proposal_size", () -> {
-            return getLeader().getProposalStats().getMinBufferSize();
-        });
+    private org.apache.zookeeper.metrics.Gauge 
gaugeWithLeader(Function<Leader, Number> supplier) {
+        return () -> {
+            final Leader leader = getLeader();
+            if (leader == null) {
+                return null;
+            }
+            return supplier.apply(leader);
+        };
     }
 
     @Override

Reply via email to