You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ratis.apache.org by sz...@apache.org on 2019/07/18 17:59:45 UTC

[incubator-ratis] branch master updated: RATIS-611. Add a test for InstallSnapshotNotification with follower restart.

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

szetszwo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ratis.git


The following commit(s) were added to refs/heads/master by this push:
     new 0ddf4ba  RATIS-611. Add a test for InstallSnapshotNotification with follower restart.
0ddf4ba is described below

commit 0ddf4ba5bf0ff8a0b0404e121f6fed6fe2dfc6ca
Author: Tsz Wo Nicholas Sze <sz...@apache.org>
AuthorDate: Thu Jul 18 10:58:49 2019 -0700

    RATIS-611. Add a test for InstallSnapshotNotification with follower restart.
---
 .../apache/ratis/grpc/server/GrpcLogAppender.java  |  66 +++++----
 .../grpc/server/GrpcServerProtocolService.java     | 164 +++++++++++++++------
 .../org/apache/ratis/server/impl/FollowerInfo.java |   7 +-
 .../org/apache/ratis/server/impl/LogAppender.java  |  38 ++---
 .../apache/ratis/server/impl/RaftServerImpl.java   |  10 +-
 .../apache/ratis/server/impl/ServerProtoUtils.java |   8 +-
 .../ratis/server/protocol/RaftServerProtocol.java  |   3 +-
 .../ratis/InstallSnapshotNotificationTests.java    | 130 ++++++++++------
 .../java/org/apache/ratis/MiniRaftCluster.java     |  12 +-
 .../impl/RaftStateMachineExceptionTests.java       |   2 +-
 .../ratis/statemachine/RaftSnapshotBaseTest.java   |   2 +-
 ...> TestInstallSnapshotNotificationWithGrpc.java} |  14 +-
 .../grpc/TestRaftReconfigurationWithGrpc.java      |   8 +-
 13 files changed, 289 insertions(+), 175 deletions(-)

diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcLogAppender.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcLogAppender.java
index aa719bb..549426b 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcLogAppender.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcLogAppender.java
@@ -211,8 +211,9 @@ public class GrpcLogAppender extends LogAppender {
   /**
    * StreamObserver for handling responses from the follower
    */
-  private class AppendLogResponseHandler
-      implements StreamObserver<AppendEntriesReplyProto> {
+  private class AppendLogResponseHandler implements StreamObserver<AppendEntriesReplyProto> {
+    private final String name = follower.getName() + "-" + getClass().getSimpleName();
+
     /**
      * After receiving a appendEntries reply, do the following:
      * 1. If the reply is success, update the follower's match index and submit
@@ -226,7 +227,7 @@ public class GrpcLogAppender extends LogAppender {
       final AppendEntriesRequestProto request = pendingRequests.remove(reply.getServerReply().getCallId());
       if (LOG.isDebugEnabled()) {
         LOG.debug("{}: received {} reply {}, request={}",
-            follower.getName(), firstResponseReceived? "a": "the first",
+            this, firstResponseReceived? "a": "the first",
             ServerProtoUtils.toString(reply), ServerProtoUtils.toString(request));
       }
 
@@ -281,7 +282,7 @@ public class GrpcLogAppender extends LogAppender {
         LOG.info("{} is stopped", GrpcLogAppender.this);
         return;
       }
-      GrpcUtil.warn(LOG, () -> getFollower().getName() + ": Failed appendEntries", t);
+      GrpcUtil.warn(LOG, () -> this + ": Failed appendEntries", t);
 
       long callId = GrpcUtil.getCallId(t);
       resetClient(pendingRequests.remove(callId));
@@ -289,9 +290,14 @@ public class GrpcLogAppender extends LogAppender {
 
     @Override
     public void onCompleted() {
-      LOG.info("{}: follower responses appendEntries COMPLETED", getFollower().getName());
+      LOG.info("{}: follower responses appendEntries COMPLETED", this);
       resetClient(null);
     }
+
+    @Override
+    public String toString() {
+      return name;
+    }
   }
 
   private boolean checkAndUpdateMatchIndex(AppendEntriesRequestProto request) {
@@ -305,8 +311,8 @@ public class GrpcLogAppender extends LogAppender {
     follower.updateNextIndex(replyNextIndex);
   }
 
-  private class InstallSnapshotResponseHandler
-      implements StreamObserver<InstallSnapshotReplyProto> {
+  private class InstallSnapshotResponseHandler implements StreamObserver<InstallSnapshotReplyProto> {
+    private final String name = follower.getName() + "-" + getClass().getSimpleName();
     private final Queue<Integer> pending;
     private final AtomicBoolean done = new AtomicBoolean(false);
 
@@ -339,7 +345,10 @@ public class GrpcLogAppender extends LogAppender {
 
     @Override
     public void onNext(InstallSnapshotReplyProto reply) {
-      LOG.debug("{}: received {} response", getFollower().getName(), firstResponseReceived? "a": "the first");
+      if (LOG.isInfoEnabled()) {
+        LOG.info("{}: received {} reply {}", this, firstResponseReceived ? "a" : "the first",
+            ServerProtoUtils.toString(reply));
+      }
 
       // update the last rpc time
       follower.updateLastRpcResponseTime();
@@ -354,10 +363,9 @@ public class GrpcLogAppender extends LogAppender {
           removePending(reply);
           break;
         case ALREADY_INSTALLED:
-          long followerLatestSnapshotIndex = reply.getSnapshotIndex();
-          LOG.info("{}: Latest snapshot index on follower {} is {}.",
-              server.getId(), follower.getPeer(), followerLatestSnapshotIndex);
-          follower.setSnapshotIndex(followerLatestSnapshotIndex);
+          final long followerSnapshotIndex = reply.getSnapshotIndex();
+          LOG.info("{}: set follower snapshotIndex to {}.", this, followerSnapshotIndex);
+          follower.setSnapshotIndex(followerSnapshotIndex);
           removePending(reply);
           break;
         case NOT_LEADER:
@@ -365,7 +373,7 @@ public class GrpcLogAppender extends LogAppender {
           break;
         case CONF_MISMATCH:
           LOG.error("{}: Configuration Mismatch ({}): Leader {} has it set to {} but follower {} has it set to {}",
-              server.getId(), RaftServerConfigKeys.Log.Appender.INSTALL_SNAPSHOT_ENABLED_KEY,
+              this, RaftServerConfigKeys.Log.Appender.INSTALL_SNAPSHOT_ENABLED_KEY,
               server.getId(), installSnapshotEnabled, getFollowerId(), !installSnapshotEnabled);
         case UNRECOGNIZED:
           break;
@@ -375,19 +383,24 @@ public class GrpcLogAppender extends LogAppender {
     @Override
     public void onError(Throwable t) {
       if (!isAppenderRunning()) {
-        LOG.info("{} is stopped", GrpcLogAppender.this);
+        LOG.info("{} is stopped", this);
         return;
       }
-      LOG.info("{}: got error when installing snapshot: {}", getFollower().getName(), t);
+      LOG.error("{}: Failed installSnapshot: {}", this, t);
       resetClient(null);
       close();
     }
 
     @Override
     public void onCompleted() {
-      LOG.info("{}: follower responses installSnapshot Completed", getFollower().getName());
+      LOG.info("{}: follower responses installSnapshot COMPLETED", this);
       close();
     }
+
+    @Override
+    public String toString() {
+      return name;
+    }
   }
 
   /**
@@ -395,8 +408,8 @@ public class GrpcLogAppender extends LogAppender {
    * @param snapshot the snapshot to be sent to Follower
    */
   private void installSnapshot(SnapshotInfo snapshot) {
-    LOG.info("{}: follower's next index is {}, log's start index is {}, will install snapshot",
-        getFollower().getName(), follower.getNextIndex(), raftLog.getStartIndex());
+    LOG.info("{}: followerNextIndex = {} but logStartIndex = {}, send snapshot {} to follower",
+        this, follower.getNextIndex(), raftLog.getStartIndex(), snapshot);
 
     final InstallSnapshotResponseHandler responseHandler = new InstallSnapshotResponseHandler();
     StreamObserver<InstallSnapshotRequestProto> snapshotRequestObserver = null;
@@ -433,7 +446,7 @@ public class GrpcLogAppender extends LogAppender {
 
     if (responseHandler.hasAllResponse()) {
       follower.setSnapshotIndex(snapshot.getTermIndex().getIndex());
-      LOG.info("{}: install snapshot-{} successfully", getFollower().getName(), snapshot.getTermIndex().getIndex());
+      LOG.info("{}: installed snapshot {} successfully", this, snapshot);
     }
   }
 
@@ -442,16 +455,16 @@ public class GrpcLogAppender extends LogAppender {
    * @param firstAvailableLogTermIndex the first available log's index on the Leader
    */
   private void installSnapshot(TermIndex firstAvailableLogTermIndex) {
-    LOG.info("{}: follower {}'s next index is {}, log's start index is {}, " +
-            "need to notify follower to install snapshot",
-        server.getId(), follower.getPeer(), follower.getNextIndex(),
-        raftLog.getStartIndex());
+    LOG.info("{}: followerNextIndex = {} but logStartIndex = {}, notify follower to install snapshot-{}",
+        this, follower.getNextIndex(), raftLog.getStartIndex(), firstAvailableLogTermIndex);
 
     final InstallSnapshotResponseHandler responseHandler = new InstallSnapshotResponseHandler();
     StreamObserver<InstallSnapshotRequestProto> snapshotRequestObserver = null;
     // prepare and enqueue the notify install snapshot request.
-    InstallSnapshotRequestProto request =
-        createInstallSnapshotNotificationRequest(firstAvailableLogTermIndex);
+    final InstallSnapshotRequestProto request = createInstallSnapshotNotificationRequest(firstAvailableLogTermIndex);
+    if (LOG.isInfoEnabled()) {
+      LOG.info("{}: send {}", this, ServerProtoUtils.toString(request));
+    }
     try {
       snapshotRequestObserver = getClient().installSnapshot(responseHandler);
       snapshotRequestObserver.onNext(request);
@@ -459,8 +472,7 @@ public class GrpcLogAppender extends LogAppender {
       responseHandler.addPending(request);
       snapshotRequestObserver.onCompleted();
     } catch (Exception e) {
-      LOG.warn("{} failed to notify follower {} to install snapshot. " +
-          "Exception: {}", this, follower, e);
+      GrpcUtil.warn(LOG, () -> this + ": Failed to notify follower to install snapshot.", e);
       if (snapshotRequestObserver != null) {
         snapshotRequestObserver.onError(e);
       }
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcServerProtocolService.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcServerProtocolService.java
index 5c5bd66..1e74502 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcServerProtocolService.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcServerProtocolService.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -21,6 +21,7 @@ import org.apache.ratis.grpc.GrpcUtil;
 import org.apache.ratis.protocol.RaftPeerId;
 import org.apache.ratis.server.RaftServer;
 import org.apache.ratis.server.impl.ServerProtoUtils;
+import org.apache.ratis.server.protocol.RaftServerProtocol;
 import org.apache.ratis.thirdparty.io.grpc.stub.StreamObserver;
 import org.apache.ratis.proto.RaftProtos.*;
 import org.apache.ratis.proto.grpc.RaftServerProtocolServiceGrpc.RaftServerProtocolServiceImplBase;
@@ -28,18 +29,104 @@ import org.apache.ratis.util.ProtoUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Supplier;
 
-public class GrpcServerProtocolService extends RaftServerProtocolServiceImplBase {
+class GrpcServerProtocolService extends RaftServerProtocolServiceImplBase {
   public static final Logger LOG = LoggerFactory.getLogger(GrpcServerProtocolService.class);
 
+  class PendingServerRequest<REQUEST> {
+    private final REQUEST request;
+    private final CompletableFuture<Void> future = new CompletableFuture<>();
+
+    PendingServerRequest(REQUEST request) {
+      this.request = request;
+    }
+
+    REQUEST getRequest() {
+      return request;
+    }
+
+    CompletableFuture<Void> getFuture() {
+      return future;
+    }
+  }
+
+  abstract class ServerRequestStreamObserver<REQUEST, REPLY> implements StreamObserver<REQUEST> {
+    private final RaftServer.Op op;
+    private final StreamObserver<REPLY> responseObserver;
+    private final AtomicReference<PendingServerRequest<REQUEST>> previousOnNext = new AtomicReference<>();
+    private final AtomicBoolean isClosed = new AtomicBoolean(false);
+
+    ServerRequestStreamObserver(RaftServer.Op op, StreamObserver<REPLY> responseObserver) {
+      this.op = op;
+      this.responseObserver = responseObserver;
+    }
+
+    private String getPreviousRequestString() {
+      return Optional.ofNullable(previousOnNext.get())
+          .map(PendingServerRequest::getRequest)
+          .map(this::requestToString)
+          .orElse(null);
+    }
+
+    abstract CompletableFuture<REPLY> process(REQUEST request) throws IOException;
+
+    abstract long getCallId(REQUEST request);
+
+    abstract String requestToString(REQUEST request);
+
+    abstract String replyToString(REPLY reply);
+
+    @Override
+    public void onNext(REQUEST request) {
+      final PendingServerRequest<REQUEST> current = new PendingServerRequest<>(request);
+      final PendingServerRequest<REQUEST> previous = previousOnNext.getAndSet(current);
+      final CompletableFuture<Void> previousFuture = Optional.ofNullable(previous)
+          .map(PendingServerRequest::getFuture)
+          .orElse(CompletableFuture.completedFuture(null));
+      try {
+        process(request).thenCombine(previousFuture, (reply, v) -> {
+          if (!isClosed.get()) {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("{}: reply {}", getId(), replyToString(reply));
+            }
+            responseObserver.onNext(reply);
+          }
+          current.getFuture().complete(null);
+          return null;
+        });
+      } catch (Throwable e) {
+        GrpcUtil.warn(LOG, () -> getId() + ": Failed " + op + " request " + requestToString(request), e);
+        responseObserver.onError(GrpcUtil.wrapException(e, getCallId(request)));
+        current.getFuture().completeExceptionally(e);
+      }
+    }
+
+    @Override
+    public void onCompleted() {
+      if (isClosed.compareAndSet(false, true)) {
+        LOG.info("{}: Completed {}, lastRequest: {}", getId(), op, getPreviousRequestString());
+        responseObserver.onCompleted();
+      }
+    }
+    @Override
+    public void onError(Throwable t) {
+      GrpcUtil.warn(LOG, () -> getId() + ": installSnapshot onError, lastRequest: " + getPreviousRequestString(), t);
+      if (isClosed.compareAndSet(false, true)) {
+        responseObserver.onCompleted();
+      }
+    }
+  }
+
   private final Supplier<RaftPeerId> idSupplier;
   private final RaftServer server;
 
-  public GrpcServerProtocolService(Supplier<RaftPeerId> idSupplier, RaftServer server) {
+  GrpcServerProtocolService(Supplier<RaftPeerId> idSupplier, RaftServer server) {
     this.idSupplier = idSupplier;
     this.server = server;
   }
@@ -64,46 +151,26 @@ public class GrpcServerProtocolService extends RaftServerProtocolServiceImplBase
   @Override
   public StreamObserver<AppendEntriesRequestProto> appendEntries(
       StreamObserver<AppendEntriesReplyProto> responseObserver) {
-    return new StreamObserver<AppendEntriesRequestProto>() {
-      private final AtomicReference<CompletableFuture<Void>> previousOnNext =
-          new AtomicReference<>(CompletableFuture.completedFuture(null));
-      private final AtomicBoolean isClosed = new AtomicBoolean(false);
+    return new ServerRequestStreamObserver<AppendEntriesRequestProto, AppendEntriesReplyProto>(
+        RaftServerProtocol.Op.APPEND_ENTRIES, responseObserver) {
+      @Override
+      CompletableFuture<AppendEntriesReplyProto> process(AppendEntriesRequestProto request) throws IOException {
+        return server.appendEntriesAsync(request);
+      }
 
       @Override
-      public void onNext(AppendEntriesRequestProto request) {
-        final CompletableFuture<Void> current = new CompletableFuture<>();
-        final CompletableFuture<Void> previous = previousOnNext.getAndSet(current);
-        try {
-          server.appendEntriesAsync(request).thenCombine(previous,
-              (reply, v) -> {
-            if (!isClosed.get()) {
-              if (LOG.isDebugEnabled()) {
-                LOG.debug(server.getId() + ": reply " + ServerProtoUtils.toString(reply));
-              }
-              responseObserver.onNext(reply);
-            }
-            current.complete(null);
-            return null;
-          });
-        } catch (Throwable e) {
-          GrpcUtil.warn(LOG, () -> getId() + ": Failed appendEntries " + ProtoUtils.toString(request.getServerRequest()), e);
-          responseObserver.onError(GrpcUtil.wrapException(e, request.getServerRequest().getCallId()));
-          current.completeExceptionally(e);
-        }
+      long getCallId(AppendEntriesRequestProto request) {
+        return request.getServerRequest().getCallId();
       }
 
       @Override
-      public void onError(Throwable t) {
-        // for now we just log a msg
-        GrpcUtil.warn(LOG, () -> getId() + ": appendEntries onError", t);
+      String requestToString(AppendEntriesRequestProto request) {
+        return ServerProtoUtils.toString(request);
       }
 
       @Override
-      public void onCompleted() {
-        if (isClosed.compareAndSet(false, true)) {
-          LOG.info("{}: appendEntries completed", getId());
-          responseObserver.onCompleted();
-        }
+      String replyToString(AppendEntriesReplyProto reply) {
+        return ServerProtoUtils.toString(reply);
       }
     };
   }
@@ -111,27 +178,26 @@ public class GrpcServerProtocolService extends RaftServerProtocolServiceImplBase
   @Override
   public StreamObserver<InstallSnapshotRequestProto> installSnapshot(
       StreamObserver<InstallSnapshotReplyProto> responseObserver) {
-    return new StreamObserver<InstallSnapshotRequestProto>() {
+    return new ServerRequestStreamObserver<InstallSnapshotRequestProto, InstallSnapshotReplyProto>(
+        RaftServerProtocol.Op.INSTALL_SNAPSHOT, responseObserver) {
       @Override
-      public void onNext(InstallSnapshotRequestProto request) {
-        try {
-          final InstallSnapshotReplyProto reply = server.installSnapshot(request);
-          responseObserver.onNext(reply);
-        } catch (Throwable e) {
-          GrpcUtil.warn(LOG, () -> getId() + ": Failed installSnapshot " + ProtoUtils.toString(request.getServerRequest()), e);
-          responseObserver.onError(GrpcUtil.wrapException(e));
-        }
+      CompletableFuture<InstallSnapshotReplyProto> process(InstallSnapshotRequestProto request) throws IOException {
+        return CompletableFuture.completedFuture(server.installSnapshot(request));
       }
 
       @Override
-      public void onError(Throwable t) {
-        GrpcUtil.warn(LOG, () -> getId() + ": installSnapshot onError", t);
+      long getCallId(InstallSnapshotRequestProto request) {
+        return request.getServerRequest().getCallId();
       }
 
       @Override
-      public void onCompleted() {
-        LOG.info("{}: installSnapshot completed", getId());
-        responseObserver.onCompleted();
+      String requestToString(InstallSnapshotRequestProto request) {
+        return ServerProtoUtils.toString(request);
+      }
+
+      @Override
+      String replyToString(InstallSnapshotReplyProto reply) {
+        return ServerProtoUtils.toString(reply);
       }
     };
   }
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/FollowerInfo.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/FollowerInfo.java
index 11cf87f..5c69c26 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/FollowerInfo.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/FollowerInfo.java
@@ -17,8 +17,9 @@
  */
 package org.apache.ratis.server.impl;
 
+import org.apache.ratis.protocol.RaftGroupMemberId;
 import org.apache.ratis.protocol.RaftPeer;
-import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.server.raftlog.RaftLog;
 import org.apache.ratis.server.raftlog.RaftLogIndex;
 import org.apache.ratis.util.Timestamp;
 import org.slf4j.Logger;
@@ -39,12 +40,12 @@ public class FollowerInfo {
   private final AtomicReference<Timestamp> lastRpcSendTime;
   private final RaftLogIndex nextIndex;
   private final RaftLogIndex matchIndex = new RaftLogIndex("matchIndex", 0L);
-  private final RaftLogIndex commitIndex = new RaftLogIndex("commitIndex", RaftServerConstants.INVALID_LOG_INDEX);
+  private final RaftLogIndex commitIndex = new RaftLogIndex("commitIndex", RaftLog.INVALID_LOG_INDEX);
   private volatile boolean attendVote;
   private final int rpcSlownessTimeoutMs;
 
 
-  FollowerInfo(RaftPeerId id, RaftPeer peer, Timestamp lastRpcTime, long nextIndex,
+  FollowerInfo(RaftGroupMemberId id, RaftPeer peer, Timestamp lastRpcTime, long nextIndex,
       boolean attendVote, int rpcSlownessTimeoutMs) {
     this.name = id + "->" + peer.getId();
     this.infoIndexChange = s -> LOG.info("{}: {}", name, s);
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/LogAppender.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/LogAppender.java
index bbc97a6..f7b94a4 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/LogAppender.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/LogAppender.java
@@ -40,7 +40,6 @@ import java.nio.file.Path;
 import java.util.*;
 
 import static org.apache.ratis.server.impl.RaftServerConstants.DEFAULT_CALLID;
-import static org.apache.ratis.server.impl.RaftServerConstants.INVALID_LOG_INDEX;
 import static org.apache.ratis.util.LifeCycle.State.CLOSED;
 import static org.apache.ratis.util.LifeCycle.State.CLOSING;
 import static org.apache.ratis.util.LifeCycle.State.EXCEPTION;
@@ -55,13 +54,10 @@ public class LogAppender {
   public static final Logger LOG = LoggerFactory.getLogger(LogAppender.class);
 
   class AppenderDaemon {
-    private final LifeCycle lifeCycle;
+    private final String name = LogAppender.this + "-" + getClass().getSimpleName();
+    private final LifeCycle lifeCycle = new LifeCycle(name);
     private final Daemon daemon = new Daemon(this::run);
 
-    AppenderDaemon(Object name) {
-      this.lifeCycle = new LifeCycle(name);
-    }
-
     void start() {
       // The life cycle state could be already closed due to server shutdown.
       if (lifeCycle.compareAndTransition(NEW, STARTING)) {
@@ -115,8 +111,14 @@ public class LogAppender {
       }
       daemon.interrupt();
     }
+
+    @Override
+    public String toString() {
+      return name;
+    }
   }
 
+  private final String name;
   protected final RaftServerImpl server;
   private final LeaderState leaderState;
   protected final RaftLog raftLog;
@@ -130,6 +132,7 @@ public class LogAppender {
 
   public LogAppender(RaftServerImpl server, LeaderState leaderState, FollowerInfo f) {
     this.follower = f;
+    this.name = follower.getName() + "-" + getClass().getSimpleName();
     this.server = server;
     this.leaderState = leaderState;
     this.raftLog = server.getState().getLog();
@@ -141,12 +144,12 @@ public class LogAppender {
     final SizeInBytes bufferByteLimit = RaftServerConfigKeys.Log.Appender.bufferByteLimit(properties);
     final int bufferElementLimit = RaftServerConfigKeys.Log.Appender.bufferElementLimit(properties);
     this.buffer = new DataQueue<>(this, bufferByteLimit, bufferElementLimit, EntryWithData::getSerializedSize);
-    this.daemon = new AppenderDaemon(this);
+    this.daemon = new AppenderDaemon();
   }
 
   @Override
   public String toString() {
-    return getClass().getSimpleName() + "(" + follower.getName() + ")";
+    return name;
   }
 
   void startAppender() {
@@ -250,11 +253,10 @@ public class LogAppender {
         }
 
         if (request == null) {
-          LOG.trace("{} need not send AppendEntries now." +
-              " Wait for more entries.", server.getId());
+          LOG.trace("{} no entries to send now, wait ...", this);
           return null;
         } else if (!isAppenderRunning()) {
-          LOG.debug("LogAppender {} has been stopped. Skip the request.", this);
+          LOG.info("{} is stopped. Skip appendEntries.", this);
           return null;
         }
 
@@ -371,7 +373,7 @@ public class LogAppender {
               } catch (IOException ignored) {
               }
             }
-            LOG.warn("Got exception when preparing InstallSnapshot request", e);
+            LOG.warn("{}: Failed to prepare installSnapshot request", LogAppender.this, e);
             throw new RuntimeException(e);
           }
         }
@@ -424,8 +426,7 @@ public class LogAppender {
 
     if (reply != null) {
       follower.setSnapshotIndex(snapshot.getTermIndex().getIndex());
-      LOG.info("{}:{} install snapshot-{} successfully on follower {}",
-          server.getId(), server.getGroupId(), snapshot.getTermIndex().getIndex(), follower.getPeer());
+      LOG.info("{}: installSnapshot {} successfully", this, snapshot);
     }
     return reply;
   }
@@ -438,7 +439,7 @@ public class LogAppender {
     if (follower.getNextIndex() < raftLog.getNextIndex()) {
       SnapshotInfo snapshot = server.getState().getLatestSnapshot();
       if (follower.getNextIndex() < logStartIndex ||
-          (logStartIndex == INVALID_LOG_INDEX && snapshot != null)) {
+          (logStartIndex == RaftLog.INVALID_LOG_INDEX && snapshot != null)) {
         return snapshot;
       }
     }
@@ -451,8 +452,8 @@ public class LogAppender {
       if (shouldSendRequest()) {
         SnapshotInfo snapshot = shouldInstallSnapshot();
         if (snapshot != null) {
-          LOG.info("{}:{} follower's next index is {}, log's start index is {}, will install snapshot",
-              follower.getName(), server.getGroupId(), follower.getNextIndex(), raftLog.getStartIndex());
+          LOG.info("{}: followerNextIndex = {} but logStartIndex = {}, send snapshot {} to follower",
+              this, follower.getNextIndex(), raftLog.getStartIndex(), snapshot);
 
           final InstallSnapshotReplyProto r = installSnapshot(snapshot);
           if (r != null && r.getResult() == InstallSnapshotResult.NOT_LEADER) {
@@ -503,8 +504,7 @@ public class LogAppender {
           follower.decreaseNextIndex(reply.getNextIndex());
           break;
         case UNRECOGNIZED:
-          LOG.warn("{} received UNRECOGNIZED AppendResult from {}",
-              server.getId(), follower.getPeer().getId());
+          LOG.warn("{}: received {}", this, reply.getResult());
           break;
       }
     }
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
index d93bcec..9845b85 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
@@ -119,7 +119,8 @@ public class RaftServerImpl implements RaftServerProtocol, RaftServerAsynchronou
   LogAppender newLogAppender(
       LeaderState state, RaftPeer peer, Timestamp lastRpcTime, long nextIndex,
       boolean attendVote) {
-    final FollowerInfo f = new FollowerInfo(getId(), peer, lastRpcTime, nextIndex, attendVote, rpcSlownessTimeoutMs);
+    final FollowerInfo f = new FollowerInfo(getMemberId(), peer, lastRpcTime, nextIndex, attendVote,
+        rpcSlownessTimeoutMs);
     return getProxy().getFactory().newLogAppender(this, state, f);
   }
 
@@ -1063,7 +1064,7 @@ public class RaftServerImpl implements RaftServerProtocol, RaftServerAsynchronou
       if (!recognized) {
         final InstallSnapshotReplyProto reply = ServerProtoUtils.toInstallSnapshotReplyProto(leaderId, getMemberId(),
             currentTerm, snapshotChunkRequest.getRequestIndex(), InstallSnapshotResult.NOT_LEADER);
-        LOG.warn("{}: Failed to recognize leader for installSnapshot chunk. Reply: {}", getMemberId(), reply);
+        LOG.warn("{}: Failed to recognize leader for installSnapshot chunk.", getMemberId());
         return reply;
       }
       changeToFollowerAndPersistMetadata(leaderTerm, "installSnapshot");
@@ -1112,7 +1113,7 @@ public class RaftServerImpl implements RaftServerProtocol, RaftServerAsynchronou
       if (!recognized) {
         final InstallSnapshotReplyProto reply = ServerProtoUtils.toInstallSnapshotReplyProto(leaderId, getMemberId(),
             currentTerm, InstallSnapshotResult.NOT_LEADER, -1);
-        LOG.warn("{}: Failed to recognize leader for installSnapshot notification. Reply: {}", getMemberId(), reply);
+        LOG.warn("{}: Failed to recognize leader for installSnapshot notification.", getMemberId());
         return reply;
       }
       changeToFollowerAndPersistMetadata(leaderTerm, "installSnapshot");
@@ -1132,8 +1133,7 @@ public class RaftServerImpl implements RaftServerProtocol, RaftServerAsynchronou
           inProgressInstallSnapshotRequest.compareAndSet(firstAvailableLogTermIndex, null);
           final InstallSnapshotReplyProto reply = ServerProtoUtils.toInstallSnapshotReplyProto(
               leaderId, getMemberId(), currentTerm, InstallSnapshotResult.ALREADY_INSTALLED, snapshotIndex);
-          LOG.info("{}: StateMachine snapshotIndex: {}. Reply: {}", getMemberId(), snapshotIndex, reply);
-
+          LOG.info("{}: StateMachine snapshotIndex is {}", getMemberId(), snapshotIndex);
           return reply;
         }
 
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerProtoUtils.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerProtoUtils.java
index e688446..37d2dfb 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerProtoUtils.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerProtoUtils.java
@@ -146,15 +146,15 @@ public interface ServerProtoUtils {
     final String s;
     switch (proto.getInstallSnapshotReplyBodyCase()) {
       case REQUESTINDEX:
-        s = "requestIndex=" + proto.getRequestIndex();
+        s = ",requestIndex=" + proto.getRequestIndex();
         break;
       case SNAPSHOTINDEX:
-        s = "snapshotIndex=" + proto.getSnapshotIndex();
+        s = ",snapshotIndex=" + proto.getSnapshotIndex();
         break;
       default:
-        throw new IllegalStateException("Unexpected body case in " + proto);
+        s = ""; // result is not SUCCESS
     }
-    return ProtoUtils.toString(proto.getServerReply()) + "-t" + proto.getTerm() + "," + s;
+    return ProtoUtils.toString(proto.getServerReply()) + "-t" + proto.getTerm() + "," + proto.getResult() + s;
   }
 
   static RaftConfigurationProto.Builder toRaftConfigurationProto(RaftConfiguration conf) {
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/protocol/RaftServerProtocol.java b/ratis-server/src/main/java/org/apache/ratis/server/protocol/RaftServerProtocol.java
index d1ef4f3..c8cfad6 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/protocol/RaftServerProtocol.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/protocol/RaftServerProtocol.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -27,6 +27,7 @@ import org.apache.ratis.proto.RaftProtos.RequestVoteReplyProto;
 import org.apache.ratis.proto.RaftProtos.RequestVoteRequestProto;
 
 public interface RaftServerProtocol {
+  enum Op {REQUEST_VOTE, APPEND_ENTRIES, INSTALL_SNAPSHOT}
 
   RequestVoteReplyProto requestVote(RequestVoteRequestProto request) throws IOException;
 
diff --git a/ratis-test/src/test/java/org/apache/ratis/grpc/TestInstallSnapshotWithGrpc.java b/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotNotificationTests.java
similarity index 58%
rename from ratis-test/src/test/java/org/apache/ratis/grpc/TestInstallSnapshotWithGrpc.java
rename to ratis-server/src/test/java/org/apache/ratis/InstallSnapshotNotificationTests.java
index 6e469b6..894bf6a 100644
--- a/ratis-test/src/test/java/org/apache/ratis/grpc/TestInstallSnapshotWithGrpc.java
+++ b/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotNotificationTests.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -15,16 +15,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ratis.grpc;
+package org.apache.ratis;
 
 import org.apache.log4j.Level;
-import org.apache.ratis.MiniRaftCluster;
-import org.apache.ratis.RaftTestUtil;
 import org.apache.ratis.client.RaftClient;
 import org.apache.ratis.conf.RaftProperties;
 import org.apache.ratis.proto.RaftProtos;
 import org.apache.ratis.protocol.RaftClientReply;
-import org.apache.ratis.protocol.RaftGroup;
 import org.apache.ratis.protocol.RaftPeerId;
 import org.apache.ratis.server.RaftServerConfigKeys;
 import org.apache.ratis.server.impl.RaftServerImpl;
@@ -34,14 +31,14 @@ import org.apache.ratis.server.raftlog.RaftLog;
 import org.apache.ratis.server.storage.RaftStorageDirectory;
 import org.apache.ratis.statemachine.RaftSnapshotBaseTest;
 import org.apache.ratis.statemachine.SimpleStateMachine4Testing;
+import org.apache.ratis.statemachine.SnapshotInfo;
 import org.apache.ratis.statemachine.StateMachine;
 import org.apache.ratis.statemachine.impl.SingleFileSnapshotInfo;
 import org.apache.ratis.util.FileUtils;
 import org.apache.ratis.util.JavaUtils;
 import org.apache.ratis.util.LogUtils;
-import org.junit.After;
+import org.apache.ratis.util.SizeInBytes;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,59 +49,53 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
 
-import static org.apache.ratis.BaseTest.ONE_SECOND;
+public abstract class InstallSnapshotNotificationTests<CLUSTER extends MiniRaftCluster>
+    extends BaseTest
+    implements MiniRaftCluster.Factory.Get<CLUSTER> {
+  static final Logger LOG = LoggerFactory.getLogger(InstallSnapshotNotificationTests.class);
 
-public class TestInstallSnapshotWithGrpc {
-  static {
-    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
+  {
     LogUtils.setLogLevel(RaftLog.LOG, Level.DEBUG);
-    LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
   }
 
-  static final Logger LOG = LoggerFactory.getLogger(TestInstallSnapshotWithGrpc.class);
-  private static final int SNAPSHOT_TRIGGER_THRESHOLD = 10;
-  private static SingleFileSnapshotInfo leaderSnapshotInfo;
-
-  private MiniRaftCluster cluster;
-
-  private MiniRaftCluster.Factory<?> getFactory() {
-    return MiniRaftClusterWithGrpc.FACTORY;
-  }
-
-  @Before
-  public void setup() throws IOException {
-    final RaftProperties prop = new RaftProperties();
+  {
+    final RaftProperties prop = getProperties();
     prop.setClass(MiniRaftCluster.STATEMACHINE_CLASS_KEY,
-        TestInstallSnapshotWithGrpc.StateMachineForGRpcTest.class, StateMachine.class);
+        StateMachine4InstallSnapshotNotificationTests.class, StateMachine.class);
     RaftServerConfigKeys.Log.Appender.setInstallSnapshotEnabled(prop, false);
     RaftServerConfigKeys.Snapshot.setAutoTriggerThreshold(
         prop, SNAPSHOT_TRIGGER_THRESHOLD);
     RaftServerConfigKeys.Snapshot.setAutoTriggerEnabled(prop, true);
-    this.cluster = getFactory().newCluster(1, prop);
-    cluster.start();
-  }
 
-  @After
-  public void tearDown() {
-    if (cluster != null) {
-      cluster.shutdown();
-    }
+    RaftServerConfigKeys.Log.setPurgeGap(prop, PURGE_GAP);
+    RaftServerConfigKeys.Log.setSegmentSizeMax(prop, SizeInBytes.valueOf(1024)); // 1k segment
   }
 
-  private static class StateMachineForGRpcTest extends
-      SimpleStateMachine4Testing {
+  private static final int SNAPSHOT_TRIGGER_THRESHOLD = 64;
+  private static final int PURGE_GAP = 8;
+  private static final AtomicReference<SnapshotInfo> leaderSnapshotInfoRef = new AtomicReference<>();
+
+  private static class StateMachine4InstallSnapshotNotificationTests extends SimpleStateMachine4Testing {
     @Override
     public CompletableFuture<TermIndex> notifyInstallSnapshotFromLeader(
         RaftProtos.RoleInfoProto roleInfoProto,
         TermIndex termIndex) {
+      final SingleFileSnapshotInfo leaderSnapshotInfo = (SingleFileSnapshotInfo) leaderSnapshotInfoRef.get();
+      LOG.info("{}: leaderSnapshotInfo = {}", getId(), leaderSnapshotInfo);
+      if (leaderSnapshotInfo == null) {
+        return super.notifyInstallSnapshotFromLeader(roleInfoProto, termIndex);
+      }
+
       try {
         Path leaderSnapshotFile = leaderSnapshotInfo.getFile().getPath();
         File followerSnapshotFilePath = new File(getSMdir(),
             leaderSnapshotFile.getFileName().toString());
         Files.copy(leaderSnapshotFile, followerSnapshotFilePath.toPath());
       } catch (IOException e) {
-        e.printStackTrace();
+        LOG.error("Failed notifyInstallSnapshotFromLeader", e);
+        return JavaUtils.completeExceptionally(e);
       }
       return CompletableFuture.completedFuture(leaderSnapshotInfo.getTermIndex());
     }
@@ -117,7 +108,12 @@ public class TestInstallSnapshotWithGrpc {
    * The new follower nodes should get a install snapshot notification.
    */
   @Test
-  public void testInstallSnapshotNotification() throws Exception {
+  public void testAddNewFollowers() throws Exception {
+    runWithNewCluster(1, this::testAddNewFollowers);
+  }
+
+  private void testAddNewFollowers(CLUSTER cluster) throws Exception {
+    leaderSnapshotInfoRef.set(null);
     final List<RaftStorageDirectory.LogPathAndIndex> logs;
     int i = 0;
     try {
@@ -148,8 +144,9 @@ public class TestInstallSnapshotWithGrpc {
     }
 
     // delete the log segments from the leader
+    LOG.info("Delete logs {}", logs);
     for (RaftStorageDirectory.LogPathAndIndex path : logs) {
-      FileUtils.delete(path.getPath());
+      FileUtils.deleteFully(path.getPath()); // the log may be already puged
     }
 
     // restart the peer
@@ -163,11 +160,12 @@ public class TestInstallSnapshotWithGrpc {
         Assert.assertTrue(client.send(new RaftTestUtil.SimpleMessage("m" + i)).isSuccess());
       }
 
-      leaderSnapshotInfo = (SingleFileSnapshotInfo) cluster.getLeader().getStateMachine().getLatestSnapshot();
+      final SnapshotInfo leaderSnapshotInfo = cluster.getLeader().getStateMachine().getLatestSnapshot();
+      final boolean set = leaderSnapshotInfoRef.compareAndSet(null, leaderSnapshotInfo);
+      Assert.assertTrue(set);
 
       // add two more peers
-      MiniRaftCluster.PeerChanges change = cluster.addNewPeers(
-          new String[]{"s3", "s4"}, true);
+      final MiniRaftCluster.PeerChanges change = cluster.addNewPeers(2, true);
       // trigger setConfiguration
       cluster.setConfiguration(change.allPeersInNewConf);
 
@@ -189,4 +187,52 @@ public class TestInstallSnapshotWithGrpc {
       cluster.shutdown();
     }
   }
+
+  @Test
+  public void testRestartFollower() throws Exception {
+    runWithNewCluster(3, this::testRestartFollower);
+  }
+
+  private void testRestartFollower(CLUSTER cluster) throws Exception {
+    leaderSnapshotInfoRef.set(null);
+    int i = 0;
+    final RaftServerImpl leader = RaftTestUtil.waitForLeader(cluster);
+    final RaftPeerId leaderId = leader.getId();
+
+    try (final RaftClient client = cluster.createClient(leaderId)) {
+      for (; i < SNAPSHOT_TRIGGER_THRESHOLD * 2 - 1; i++) {
+        final RaftClientReply reply = client.send(new RaftTestUtil.SimpleMessage("m" + i));
+        Assert.assertTrue(reply.isSuccess());
+      }
+    }
+
+    // wait for the snapshot to be done
+    final long oldLeaderNextIndex = leader.getState().getLog().getNextIndex();
+    {
+      LOG.info("{}: oldLeaderNextIndex = {}", leaderId, oldLeaderNextIndex);
+      final List<File> snapshotFiles = RaftSnapshotBaseTest.getSnapshotFiles(cluster,
+          oldLeaderNextIndex - SNAPSHOT_TRIGGER_THRESHOLD, oldLeaderNextIndex);
+      JavaUtils.attempt(() -> snapshotFiles.stream().anyMatch(RaftSnapshotBaseTest::exists),
+          10, ONE_SECOND, "snapshotFile.exist", LOG);
+    }
+
+    final RaftPeerId followerId = cluster.getFollowers().get(0).getId();
+    cluster.killServer(followerId);
+
+    // generate some more traffic
+    try (final RaftClient client = cluster.createClient(leader.getId())) {
+      Assert.assertTrue(client.send(new RaftTestUtil.SimpleMessage("m" + i)).isSuccess());
+    }
+
+    FIVE_SECONDS.sleep();
+    cluster.restartServer(followerId, false);
+    final RaftServerImpl follower = cluster.getRaftServerImpl(followerId);
+    JavaUtils.attempt(() -> {
+      final long newLeaderNextIndex = leader.getState().getLog().getNextIndex();
+      LOG.info("{}: newLeaderNextIndex = {}", leaderId, newLeaderNextIndex);
+      Assert.assertTrue(newLeaderNextIndex > oldLeaderNextIndex);
+      Assert.assertEquals(newLeaderNextIndex, follower.getState().getLog().getNextIndex());
+    }, 10, ONE_SECOND, "followerNextIndex", LOG);
+
+  }
 }
diff --git a/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java b/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
index 22888ec..c3d7868 100644
--- a/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
+++ b/ratis-server/src/test/java/org/apache/ratis/MiniRaftCluster.java
@@ -300,15 +300,15 @@ public abstract class MiniRaftCluster implements Closeable {
   /**
    * start a stopped server again.
    */
-  public RaftServerImpl restartServer(RaftPeerId newId, boolean format) throws IOException {
-    return restartServer(newId, group, format);
+  public RaftServerImpl restartServer(RaftPeerId serverId, boolean format) throws IOException {
+    return restartServer(serverId, group, format);
   }
 
-  public RaftServerImpl restartServer(RaftPeerId newId, RaftGroup group, boolean format) throws IOException {
-    killServer(newId);
-    servers.remove(newId);
+  public RaftServerImpl restartServer(RaftPeerId serverId, RaftGroup group, boolean format) throws IOException {
+    killServer(serverId);
+    servers.remove(serverId);
 
-    final RaftServerProxy proxy = putNewServer(newId, group, format);
+    final RaftServerProxy proxy = putNewServer(serverId, group, format);
     proxy.start();
     return group == null? null: proxy.getImpl(group.getGroupId());
   }
diff --git a/ratis-server/src/test/java/org/apache/ratis/server/impl/RaftStateMachineExceptionTests.java b/ratis-server/src/test/java/org/apache/ratis/server/impl/RaftStateMachineExceptionTests.java
index ed3929f..7e6b376 100644
--- a/ratis-server/src/test/java/org/apache/ratis/server/impl/RaftStateMachineExceptionTests.java
+++ b/ratis-server/src/test/java/org/apache/ratis/server/impl/RaftStateMachineExceptionTests.java
@@ -48,7 +48,7 @@ public abstract class RaftStateMachineExceptionTests<CLUSTER extends MiniRaftClu
     LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
   }
 
-  protected static boolean failPreAppend = false;
+  private static volatile boolean failPreAppend = false;
 
   protected static class StateMachineWithException extends
       SimpleStateMachine4Testing {
diff --git a/ratis-server/src/test/java/org/apache/ratis/statemachine/RaftSnapshotBaseTest.java b/ratis-server/src/test/java/org/apache/ratis/statemachine/RaftSnapshotBaseTest.java
index c54191f..55c6ce9 100644
--- a/ratis-server/src/test/java/org/apache/ratis/statemachine/RaftSnapshotBaseTest.java
+++ b/ratis-server/src/test/java/org/apache/ratis/statemachine/RaftSnapshotBaseTest.java
@@ -51,7 +51,7 @@ import java.util.stream.Collectors;
 import java.util.stream.LongStream;
 
 public abstract class RaftSnapshotBaseTest extends BaseTest {
-  static {
+  {
     LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
     LogUtils.setLogLevel(RaftLog.LOG, Level.DEBUG);
     LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
diff --git a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java b/ratis-test/src/test/java/org/apache/ratis/grpc/TestInstallSnapshotNotificationWithGrpc.java
similarity index 69%
copy from ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
copy to ratis-test/src/test/java/org/apache/ratis/grpc/TestInstallSnapshotNotificationWithGrpc.java
index 105eb11..87ce6f0 100644
--- a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/TestInstallSnapshotNotificationWithGrpc.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -17,15 +17,9 @@
  */
 package org.apache.ratis.grpc;
 
-import org.apache.log4j.Level;
-import org.apache.ratis.grpc.server.GrpcServerProtocolService;
-import org.apache.ratis.server.impl.RaftReconfigurationBaseTest;
-import org.apache.ratis.util.LogUtils;
+import org.apache.ratis.InstallSnapshotNotificationTests;
 
-public class TestRaftReconfigurationWithGrpc
-    extends RaftReconfigurationBaseTest<MiniRaftClusterWithGrpc>
+public class TestInstallSnapshotNotificationWithGrpc
+    extends InstallSnapshotNotificationTests<MiniRaftClusterWithGrpc>
     implements MiniRaftClusterWithGrpc.FactoryGet {
-  static {
-    LogUtils.setLogLevel(GrpcServerProtocolService.LOG, Level.DEBUG);
-  }
 }
diff --git a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
index 105eb11..58349a5 100644
--- a/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/TestRaftReconfigurationWithGrpc.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -17,15 +17,9 @@
  */
 package org.apache.ratis.grpc;
 
-import org.apache.log4j.Level;
-import org.apache.ratis.grpc.server.GrpcServerProtocolService;
 import org.apache.ratis.server.impl.RaftReconfigurationBaseTest;
-import org.apache.ratis.util.LogUtils;
 
 public class TestRaftReconfigurationWithGrpc
     extends RaftReconfigurationBaseTest<MiniRaftClusterWithGrpc>
     implements MiniRaftClusterWithGrpc.FactoryGet {
-  static {
-    LogUtils.setLogLevel(GrpcServerProtocolService.LOG, Level.DEBUG);
-  }
 }