Skip to content

RATIS-1558. Support Listener in RaftServerImpl#627

Merged
szetszwo merged 2 commits into
apache:masterfrom
codings-dan:1558
Mar 24, 2022
Merged

RATIS-1558. Support Listener in RaftServerImpl#627
szetszwo merged 2 commits into
apache:masterfrom
codings-dan:1558

Conversation

@codings-dan
Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

subtask to support listener: Support Listener in RaftServerImpl

What is the link to the Apache JIRA

https://issues.apache.org/jira/projects/RATIS/issues/RATIS-1558?filter=reportedbyme

How was this patch tested?

UT

@codings-dan
Copy link
Copy Markdown
Contributor Author

@szetszwo Can you help review this patch, thanks!

Copy link
Copy Markdown
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codings-dan , thanks a lot for working on this. It looks good in general. Some comments inlined.

Comment on lines +345 to +348
if (roles.equals(RaftPeerRole.FOLLOWER)) {
reason = "startAsFollower";
setRole(RaftPeerRole.FOLLOWER, reason);
} else if (roles.equals(RaftPeerRole.LISTENER)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since RaftPeerRole is enum, use == . Also, let's add an else-clause to throw an exception.

    if (newRole == RaftPeerRole.FOLLOWER) {
      reason = "startAsFollower";
      setRole(RaftPeerRole.FOLLOWER, reason);
    } else if (newRole == RaftPeerRole.LISTENER) {
      reason = "startAsListener";
      setRole(RaftPeerRole.LISTENER, reason);
    } else {
      throw new IllegalArgumentException("Unexpected role " + newRole);
    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


if (old != RaftPeerRole.FOLLOWER || force) {
setRole(RaftPeerRole.FOLLOWER, reason);
if (old != RaftPeerRole.LISTENER) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is illegal to have old == LISTENER. Let's throw an exception in the beginning. Then we don't have to change other code in this method.

@@ -485,6 +498,9 @@ class RaftServerImpl implements RaftServer.Division,
    */
   private synchronized boolean changeToFollower(long newTerm, boolean force, Object reason) {
     final RaftPeerRole old = role.getCurrentRole();
+    if (old == RaftPeerRole.LISTENER) {
+      throw new IllegalStateException("Unexpected role " + old);
+    }
     final boolean metadataUpdated = state.updateCurrentTerm(newTerm);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out this problem, in theory, old will not be a Listener, I have fixed it

followerState = updateLastRpcTime(FollowerState.UpdateType.APPEND_START);

// Check that the append entries are not inconsistent. There are 3
// Check that the append ents are not inconsistent. There are 3
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for causing this typo , i have fixed it

public void run() {
final TimeDuration sleepDeviationThreshold = server.getSleepDeviationThreshold();
while (isRunning && server.getInfo().isFollower()) {
while (isRunning && (server.getInfo().isFollower() || server.getInfo().isListener())) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a shouldRun() method.

@@ -109,10 +110,19 @@ class FollowerState extends Daemon {
     return true;
   }
 
+  private boolean shouldRun() {
+    final DivisionInfo info = server.getInfo();
+    final boolean run = isRunning && (info.isFollower() || info.isListener());
+    if (!run) {
+      LOG.info("{}: Stopping now (isRunning? {}, role = {})", this, isRunning, info.getCurrentRole());
+    }
+    return run;
+  }
+
   @Override
   public  void run() {
     final TimeDuration sleepDeviationThreshold = server.getSleepDeviationThreshold();
-    while (isRunning && server.getInfo().isFollower()) {
+    while (shouldRun()) {
       final TimeDuration electionTimeout = server.getRandomElectionTimeout();
       try {
         final TimeDuration extraSleep = electionTimeout.sleep();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines +125 to +127
final boolean isPeer = (server.getInfo().isFollower() || server.getInfo().isListener());
if (!isRunning || !isPeer) {
LOG.info("{}: Stopping now (isRunning? {}, isPeer? {})", this, isRunning, isPeer);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use shouldRun() from above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@codings-dan
Copy link
Copy Markdown
Contributor Author

@szetszwo Thanks for reviewing the code, I change the code according to the comment you left, PTAL again, thank you! BTW, in my opinion, apart from adding a admin command to change the listener to follower using the setConfiguration API , the development work related to the listener has been completed. If I missed any part of the task, Can you help point it out? I can follow your suggestion to continue the follow-up development work.

@szetszwo
Copy link
Copy Markdown
Contributor

@codings-dan , thanks a lot for working on the Listener! We should add some tests to make sure the following.

  • A Listener won't start leader election.
  • A Listener will reject requestVote. (this seems currently missing.)
  • A Listener can receive log entires and snapshots.
  • The leader won't count Listeners for majority.
  • setConfiguration can add Listeners, remove Listeners and, change a voting member to Listener and vice versa.

Copy link
Copy Markdown
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 the change looks good.

@szetszwo szetszwo merged commit 77f21ae into apache:master Mar 24, 2022
@codings-dan
Copy link
Copy Markdown
Contributor Author

@codings-dan , thanks a lot for working on the Listener! We should add some tests to make sure the following.

  • A Listener won't start leader election.
  • A Listener will reject requestVote. (this seems currently missing.)
  • A Listener can receive log entires and snapshots.
  • The leader won't count Listeners for majority.
  • setConfiguration can add Listeners, remove Listeners and, change a voting member to Listener and vice versa.

will do, thanks for the guidance!

@codings-dan codings-dan deleted the 1558 branch March 24, 2022 13:10
@codings-dan
Copy link
Copy Markdown
Contributor Author

  • A Listener will reject requestVote. (this seems currently missing.)

@szetszwo This part doesn't seem to do anything since we don't send a requestVote request to the listener, see

final Collection<RaftPeer> others = conf.getOtherPeers(server.getId());

Thanks again for the guidance. I add unit test related to leaderElection, see #737
Combined with RATIS-1640 all unit test tasks have been completed.

@szetszwo
Copy link
Copy Markdown
Contributor

szetszwo commented Sep 6, 2022

@codings-dan , thanks a lot for working on the listener feature!

... since we don't send a requestVote request to the listener, ...

We should not depend on that. A patient problem is that a server may be misconfigured or do not have the up-to-date configuration. Then, it may send a requestVote to listeners, which it thinks they are voting members. If the listeners replied yes, the server will become a leader.

symious pushed a commit to symious/ratis that referenced this pull request Mar 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants