bitflicker64 commented on code in PR #3185:
URL: https://github.com/apache/hugegraph/pull/3185#discussion_r3947215693


##########
hugegraph-pd/hg-pd-core/src/main/java/org/apache/hugegraph/pd/raft/RaftEngine.java:
##########
@@ -203,7 +204,97 @@ public void shutDown() {
     }
 
     public boolean isLeader() {
-        return this.raftNode.isLeader(true);
+        Node node = this.raftNode;
+        return node != null && node.isLeader(true);
+    }
+
+    /**
+     * Whether this node currently sees a raft leader.
+     * <p>
+     * A follower only keeps its leader while heartbeats keep arriving inside 
the election
+     * timeout, and a leader only keeps its role while it can reach a quorum. 
Seeing a leader
+     * therefore means this node is part of a quorum from its own point of 
view, which is the
+     * signal a readiness probe needs. Served from the state machine 
callbacks, not from the
+     * raft node, so it never waits on the node lock.
+     */
+    public boolean hasLeader() {
+        return this.raftNode != null && 
this.stateMachine.getProbeView().seesLeader;
+    }
+
+    /**
+     * Take a view of the local raft state from the volatile copies the state 
machine
+     * callbacks maintain, never from the raft node itself. During an election 
jraft holds
+     * the node lock while it reconnects to peers, so a probe that read the 
node stalled for
+     * the connect timeout instead of answering its 503 promptly. All fields 
derive from one
+     * read of one immutable view, swapped in whole per callback, so they 
cannot contradict
+     * each other.
+     * <p>
+     * The state reported is the last one a callback announced: leader, 
follower, error or
+     * shutdown. jraft emits no callback for candidacy or leadership transfer, 
so a candidate
+     * reads as a follower that sees no leader, which yields the same 
not-ready answer. The
+     * view trails the node by whatever sits in the FSM queue ahead of the 
announcement,
+     * which is the price of never waiting on the node lock.
+     */
+    public RaftStatus getRaftStatus() {
+        if (this.raftNode == null) {
+            return new RaftStatus(false, State.STATE_UNINITIALIZED.name(), 
false);
+        }
+        RaftStateMachine.ProbeView view = this.stateMachine.getProbeView();
+        return new RaftStatus(view.state.isActive() && view.seesLeader,
+                              view.state.name(), State.STATE_LEADER == 
view.state);
+    }
+
+    /**
+     * Immutable view of the raft state behind {@code GET /v1/ready}. It 
carries no cluster
+     * addresses: the endpoint is unauthenticated, and the leader's address 
stays on the
+     * authenticated {@code /v1/members}.
+     */
+    public static final class RaftStatus {
+
+        private final boolean ready;
+        private final String state;
+        private final boolean localLeader;
+
+        RaftStatus(boolean ready, String state, boolean localLeader) {
+            this.ready = ready;
+            this.state = state;
+            this.localLeader = localLeader;
+        }
+
+        public boolean isReady() {
+            return this.ready;
+        }
+
+        /**
+         * @return the jraft node state name, never null
+         */
+        public String getState() {
+            return this.state;
+        }
+
+        public boolean isLocalLeader() {
+            return this.localLeader;
+        }
+    }
+
+    /**
+     * Number of raft peers, this node included, that the leader has heard 
from within the
+     * leader lease timeout, which jraft derives as 90% of the election 
timeout by default.
+     * Only the leader tracks replication state, so any other node returns -1. 
The leader
+     * check is the state machine's lock-free term flag, so a scrape on a 
non-leader never
+     * waits on the node lock; {@code listAlivePeers} itself only runs on a 
settled leader.
+     */
+    public int getAlivePeerCount() {

Review Comment:
   ⚠️ The javadoc promises that `listAlivePeers` "only runs on a settled 
leader", but no guard can give this method the lock-free property the rest of 
the readiness path has: `NodeImpl.listAlivePeers()` (jraft-core 1.3.13, 
`NodeImpl.java:2974-2984`) takes `readLock` *before* it checks state and throws.
   
   Two ways a scrape blocks there:
   
   - No FSM lag needed. `stepDown` runs under the caller's write lock 
(`NodeImpl.java:1268-1289`) and on a term bump writes raft metadata before 
releasing, so a scrape that passed the guard on a genuinely settled leader 
waits out the rest of it.
   - With FSM lag. `stateMachine.isLeader()` is `leaderTerm.get() > 0`, reset 
only in `onLeaderStop`, which jraft enqueues on the FSM disruptor, while 
`stepDown` sets `state = STATE_FOLLOWER` synchronously. Should that backlog 
outlive one election timeout, the scrape reaches `preVote()`, which holds the 
write lock across `rpcService.connect(...)` per peer (`NodeImpl.java:2698`); 
that call blocks for `rpcConnectTimeoutMs`, wired from `raft.rpc-timeout`, 
default 10000 ms (`PDConfig.java:153`).
   
   That is the stall the `getRaftStatus()` javadoc gives as its reason to serve 
from callbacks, on the one path that still reads the node, during the fault 
these gauges exist to observe.
   
   Requested change: drop the "settled leader" guarantee from the javadoc, and 
move the call off the request thread by refreshing the count into a volatile 
field that the gauge reads.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to