RATIS-2036. Avoid trigger snapshot when removing raftGroup (#1055)

diff --git a/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java b/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
index 565e881..7419ca0 100644
--- a/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
+++ b/ratis-server-api/src/main/java/org/apache/ratis/server/RaftServerConfigKeys.java
@@ -647,6 +647,18 @@
       setBoolean(properties::setBoolean, TRIGGER_WHEN_STOP_ENABLED_KEY, triggerWhenStopEnabled);
     }
 
+    /** whether trigger snapshot when remove raft server */
+    String TRIGGER_WHEN_REMOVE_ENABLED_KEY = PREFIX + ".trigger-when-remove.enabled";
+    /** by default let the state machine to trigger snapshot when remove */
+    boolean TRIGGER_WHEN_REMOVE_ENABLED_DEFAULT = true;
+    static boolean triggerWhenRemoveEnabled(RaftProperties properties) {
+      return getBoolean(properties::getBoolean,
+          TRIGGER_WHEN_REMOVE_ENABLED_KEY, TRIGGER_WHEN_REMOVE_ENABLED_DEFAULT, getDefaultLog());
+    }
+    static void setTriggerWhenRemoveEnabled(RaftProperties properties, boolean triggerWhenRemoveEnabled) {
+      setBoolean(properties::setBoolean, TRIGGER_WHEN_REMOVE_ENABLED_KEY, triggerWhenRemoveEnabled);
+    }
+
     /** The log index gap between to two snapshot creations. */
     String CREATION_GAP_KEY = PREFIX + ".creation.gap";
     long CREATION_GAP_DEFAULT = 1024;
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 23cab70..0ea3746 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
@@ -461,6 +461,7 @@
     final RaftStorageDirectory dir = state.getStorage().getStorageDir();
 
     /* Shutdown is triggered here inorder to avoid any locked files. */
+    state.getStateMachineUpdater().setRemoving();
     close();
     getStateMachine().event().notifyGroupRemove();
     if (deleteDirectory) {
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java
index b01270d..fbd7f01 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/StateMachineUpdater.java
@@ -74,6 +74,8 @@
 
   private final boolean triggerSnapshotWhenStopEnabled;
 
+  private final boolean triggerSnapshotWhenRemoveEnabled;
+
   private final Long autoSnapshotThreshold;
   private final boolean purgeUptoSnapshotIndex;
 
@@ -91,6 +93,8 @@
 
   private final Consumer<Long> appliedIndexConsumer;
 
+  private volatile boolean isRemoving;
+
   StateMachineUpdater(StateMachine stateMachine, RaftServerImpl server,
       ServerState serverState, long lastAppliedIndex, RaftProperties properties, Consumer<Long> appliedIndexConsumer) {
     this.name = serverState.getMemberId() + "-" + JavaUtils.getClassSimpleName(getClass());
@@ -106,6 +110,7 @@
     this.snapshotIndex = new RaftLogIndex("snapshotIndex", lastAppliedIndex);
 
     this.triggerSnapshotWhenStopEnabled = RaftServerConfigKeys.Snapshot.triggerWhenStopEnabled(properties);
+    this.triggerSnapshotWhenRemoveEnabled = RaftServerConfigKeys.Snapshot.triggerWhenRemoveEnabled(properties);
     final boolean autoSnapshot = RaftServerConfigKeys.Snapshot.autoTriggerEnabled(properties);
     this.autoSnapshotThreshold = autoSnapshot? RaftServerConfigKeys.Snapshot.autoTriggerThreshold(properties): null;
     final int numSnapshotFilesRetained = RaftServerConfigKeys.Snapshot.retentionFileNum(properties);
@@ -327,12 +332,33 @@
     if (autoSnapshotThreshold == null) {
       return false;
     } else if (shouldStop()) {
-      return triggerSnapshotWhenStopEnabled && getLastAppliedIndex() - snapshotIndex.get() > 0;
+      return shouldTakeSnapshotAtStop() && getLastAppliedIndex() - snapshotIndex.get() > 0;
     }
     return state == State.RUNNING &&
         getStateMachineLastAppliedIndex() - snapshotIndex.get() >= autoSnapshotThreshold;
   }
 
+  /**
+   * In view of the three variables triggerSnapshotWhenStopEnabled, triggerSnapshotWhenRemoveEnabled and isRemoving,
+   * we can draw the following 8 combination:
+   * true true true => true
+   * true true false => true
+   * true false true => false
+   * true false false => true
+   * false true true => true
+   * false true false =>  false
+   * false false true => false
+   * false false false => false
+   * @return result
+   */
+  private boolean shouldTakeSnapshotAtStop() {
+    return isRemoving ? triggerSnapshotWhenRemoveEnabled : triggerSnapshotWhenStopEnabled;
+  }
+
+  void setRemoving() {
+    this.isRemoving = true;
+  }
+
   private long getLastAppliedIndex() {
     return appliedIndex.get();
   }