You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ratis.apache.org by dr...@apache.org on 2022/08/22 12:12:13 UTC

[ratis] branch branch-2 updated (913ed630e -> 87d7d0144)

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

dragonyliu pushed a change to branch branch-2
in repository https://gitbox.apache.org/repos/asf/ratis.git


    from 913ed630e RATIS-1642. Specify the thread-safety requirement in StateMachine. (#709)
     new ef3b9b124 RATIS-1586. Set useCacheForAllThreads default to false. (#651)
     new d7fdfd982 RATIS-1680. TestLeaderInstallSnapshot creates untracked files (#719)
     new 87d7d0144 RATIS-1638. Separate first election timeout (#713)

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../main/java/org/apache/ratis/conf/ConfUtils.java | 14 ++++++-
 .../main/java/org/apache/ratis/util/JavaUtils.java | 47 +++++++++++++++++++++-
 .../java/org/apache/ratis/grpc/GrpcFactory.java    | 24 ++++++++---
 .../apache/ratis/server/RaftServerConfigKeys.java  | 39 ++++++++++++++++--
 .../apache/ratis/server/impl/RaftServerImpl.java   | 17 ++++++++
 .../org/apache/ratis/server/impl/ServerState.java  |  1 +
 .../ratis/InstallSnapshotFromLeaderTests.java      | 24 ++++++++---
 .../org/apache/ratis/grpc/TestGrpcFactory.java     | 24 ++++++-----
 8 files changed, 162 insertions(+), 28 deletions(-)
 copy ratis-metrics/src/main/java/org/apache/ratis/metrics/impl/MetricRegistryFactoryImpl.java => ratis-test/src/test/java/org/apache/ratis/grpc/TestGrpcFactory.java (63%)


[ratis] 03/03: RATIS-1638. Separate first election timeout (#713)

Posted by dr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dragonyliu pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/ratis.git

commit 87d7d0144ed0d059afa824d44d96d65e0b01075e
Author: William Song <48...@users.noreply.github.com>
AuthorDate: Fri Aug 19 23:18:29 2022 +0800

    RATIS-1638. Separate first election timeout (#713)
    
    
    (cherry picked from commit e49687aba20cad70c576900f1be59bb8fcd55d10)
---
 .../main/java/org/apache/ratis/conf/ConfUtils.java | 14 +++++++-
 .../apache/ratis/server/RaftServerConfigKeys.java  | 39 +++++++++++++++++++---
 .../apache/ratis/server/impl/RaftServerImpl.java   | 17 ++++++++++
 .../org/apache/ratis/server/impl/ServerState.java  |  1 +
 4 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
index 681c0dc67..629d5a389 100644
--- a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
@@ -214,6 +214,17 @@ public interface ConfUtils {
     return value;
   }
 
+  @SafeVarargs
+  static TimeDuration getTimeDuration(
+        BiFunction<String, TimeDuration, TimeDuration> getter,
+        String key, TimeDuration defaultValue, String fallbackKey, TimeDuration fallbackValue,
+        Consumer<String> logger, BiConsumer<String, TimeDuration>... assertions) {
+    final TimeDuration value = get(getter, key, defaultValue, fallbackKey, fallbackValue, logger, assertions);
+    requireNonNegativeTimeDuration().accept(key, value);
+    return value;
+  }
+
+
   static TlsConf getTlsConf(
       Function<String, TlsConf> tlsConfGetter,
       String key, Consumer<String> logger) {
@@ -236,10 +247,11 @@ public interface ConfUtils {
     T value = get(getter, key, defaultValue, null, assertions);
     if (value != defaultValue) {
       logGet(key, value, defaultValue, logger);
+      return value;
     } else {
       logFallback(key, fallbackKey, fallbackValue, logger);
+      return fallbackValue;
     }
-    return value;
   }
 
   static InetSocketAddress getInetSocketAddress(
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 6d0c5d41b..e24631378 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
@@ -619,9 +619,12 @@ public interface RaftServerConfigKeys {
 
     String TIMEOUT_MIN_KEY = PREFIX + ".timeout.min";
     TimeDuration TIMEOUT_MIN_DEFAULT = TimeDuration.valueOf(150, TimeUnit.MILLISECONDS);
-    static TimeDuration timeoutMin(RaftProperties properties) {
+    static TimeDuration timeoutMin(RaftProperties properties, Consumer<String> logger) {
       return getTimeDuration(properties.getTimeDuration(TIMEOUT_MIN_DEFAULT.getUnit()),
-          TIMEOUT_MIN_KEY, TIMEOUT_MIN_DEFAULT, getDefaultLog());
+          TIMEOUT_MIN_KEY, TIMEOUT_MIN_DEFAULT, logger);
+    }
+    static TimeDuration timeoutMin(RaftProperties properties) {
+      return timeoutMin(properties, getDefaultLog());
     }
     static void setTimeoutMin(RaftProperties properties, TimeDuration minDuration) {
       setTimeDuration(properties::setTimeDuration, TIMEOUT_MIN_KEY, minDuration);
@@ -629,14 +632,42 @@ public interface RaftServerConfigKeys {
 
     String TIMEOUT_MAX_KEY = PREFIX + ".timeout.max";
     TimeDuration TIMEOUT_MAX_DEFAULT = TimeDuration.valueOf(300, TimeUnit.MILLISECONDS);
-    static TimeDuration timeoutMax(RaftProperties properties) {
+    static TimeDuration timeoutMax(RaftProperties properties, Consumer<String> logger) {
       return getTimeDuration(properties.getTimeDuration(TIMEOUT_MAX_DEFAULT.getUnit()),
-          TIMEOUT_MAX_KEY, TIMEOUT_MAX_DEFAULT, getDefaultLog());
+          TIMEOUT_MAX_KEY, TIMEOUT_MAX_DEFAULT, logger);
+    }
+    static TimeDuration timeoutMax(RaftProperties properties) {
+      return timeoutMax(properties, getDefaultLog());
     }
     static void setTimeoutMax(RaftProperties properties, TimeDuration maxDuration) {
       setTimeDuration(properties::setTimeDuration, TIMEOUT_MAX_KEY, maxDuration);
     }
 
+    /** separate first timeout so that the startup unavailable time can be reduced */
+    String FIRST_ELECTION_TIMEOUT_MIN_KEY = PREFIX + ".first-election.timeout.min";
+    TimeDuration FIRST_ELECTION_TIMEOUT_MIN_DEFAULT = null;
+    static TimeDuration firstElectionTimeoutMin(RaftProperties properties) {
+      final TimeDuration fallbackFirstElectionTimeoutMin = Rpc.timeoutMin(properties, null);
+      return getTimeDuration(properties.getTimeDuration(fallbackFirstElectionTimeoutMin.getUnit()),
+          FIRST_ELECTION_TIMEOUT_MIN_KEY, FIRST_ELECTION_TIMEOUT_MIN_DEFAULT,
+          Rpc.TIMEOUT_MIN_KEY, fallbackFirstElectionTimeoutMin, getDefaultLog());
+    }
+    static void setFirstElectionTimeoutMin(RaftProperties properties, TimeDuration firstMinDuration) {
+      setTimeDuration(properties::setTimeDuration, FIRST_ELECTION_TIMEOUT_MIN_KEY, firstMinDuration);
+    }
+
+    String FIRST_ELECTION_TIMEOUT_MAX_KEY = PREFIX + ".first-election.timeout.max";
+    TimeDuration FIRST_ELECTION_TIMEOUT_MAX_DEFAULT = null;
+    static TimeDuration firstElectionTimeoutMax(RaftProperties properties) {
+      final TimeDuration fallbackFirstElectionTimeoutMax = Rpc.timeoutMax(properties, null);
+      return getTimeDuration(properties.getTimeDuration(fallbackFirstElectionTimeoutMax.getUnit()),
+          FIRST_ELECTION_TIMEOUT_MAX_KEY, FIRST_ELECTION_TIMEOUT_MAX_DEFAULT,
+          Rpc.TIMEOUT_MAX_KEY, fallbackFirstElectionTimeoutMax, getDefaultLog());
+    }
+    static void setFirstElectionTimeoutMax(RaftProperties properties, TimeDuration firstMaxDuration) {
+      setTimeDuration(properties::setTimeDuration, FIRST_ELECTION_TIMEOUT_MAX_KEY, firstMaxDuration);
+    }
+
     String REQUEST_TIMEOUT_KEY = PREFIX + ".request.timeout";
     TimeDuration REQUEST_TIMEOUT_DEFAULT = TimeDuration.valueOf(3000, TimeUnit.MILLISECONDS);
     static TimeDuration requestTimeout(RaftProperties properties) {
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 ee314dbe2..1994fa8de 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
@@ -187,6 +187,8 @@ class RaftServerImpl implements RaftServer.Division,
   private final ExecutorService serverExecutor;
   private final ExecutorService clientExecutor;
 
+  private final AtomicBoolean firstElectionSinceStartup = new AtomicBoolean(true);
+
   RaftServerImpl(RaftGroup group, StateMachine stateMachine, RaftServerProxy proxy) throws IOException {
     final RaftPeerId id = proxy.getId();
     LOG.info("{}: new RaftServerImpl for {} with {}", id, group, stateMachine);
@@ -245,11 +247,22 @@ class RaftServerImpl implements RaftServer.Division,
   }
 
   TimeDuration getRandomElectionTimeout() {
+    if (firstElectionSinceStartup.get()) {
+      return getFirstRandomElectionTimeout();
+    }
     final int min = properties().minRpcTimeoutMs();
     final int millis = min + ThreadLocalRandom.current().nextInt(properties().maxRpcTimeoutMs() - min + 1);
     return TimeDuration.valueOf(millis, TimeUnit.MILLISECONDS);
   }
 
+  private TimeDuration getFirstRandomElectionTimeout() {
+    final RaftProperties properties = proxy.getProperties();
+    final int min = RaftServerConfigKeys.Rpc.firstElectionTimeoutMin(properties).toIntExact(TimeUnit.MILLISECONDS);
+    final int max = RaftServerConfigKeys.Rpc.firstElectionTimeoutMax(properties).toIntExact(TimeUnit.MILLISECONDS);
+    final int mills = min + ThreadLocalRandom.current().nextInt(max - min + 1);
+    return TimeDuration.valueOf(mills, TimeUnit.MILLISECONDS);
+  }
+
   TimeDuration getLeaderStepDownWaitTime() {
     return leaderStepDownWaitTime;
   }
@@ -1725,4 +1738,8 @@ class RaftServerImpl implements RaftServer.Division,
           .collect(Collectors.toList());
     }
   }
+
+  void onGroupLeaderElected() {
+    this.firstElectionSinceStartup.set(false);
+  }
 }
diff --git a/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java b/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java
index 34e9cb92c..1ee2cab5e 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/impl/ServerState.java
@@ -315,6 +315,7 @@ class ServerState implements Closeable {
       leaderId = newLeaderId;
       if (leaderId != null) {
         server.finishTransferLeadership();
+        server.onGroupLeaderElected();
       }
     }
   }


[ratis] 01/03: RATIS-1586. Set useCacheForAllThreads default to false. (#651)

Posted by dr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dragonyliu pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/ratis.git

commit ef3b9b1248052bac2c58d780f04e3a39f0cbd835
Author: Tsz-Wo Nicholas Sze <sz...@apache.org>
AuthorDate: Thu Aug 18 08:45:02 2022 -0700

    RATIS-1586. Set useCacheForAllThreads default to false. (#651)
    
    
    (cherry picked from commit e5ee38ab0d38f5e785880a504d39c671c1b4d533)
---
 .../main/java/org/apache/ratis/util/JavaUtils.java | 47 +++++++++++++++++++++-
 .../java/org/apache/ratis/grpc/GrpcFactory.java    | 24 ++++++++---
 .../org/apache/ratis/grpc/TestGrpcFactory.java     | 33 +++++++++++++++
 3 files changed, 97 insertions(+), 7 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java b/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
index b3a8f7e99..80162e101 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
@@ -25,6 +25,8 @@ import org.slf4j.LoggerFactory;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
 import java.util.Date;
@@ -49,7 +51,7 @@ import java.util.function.Supplier;
 public interface JavaUtils {
   Logger LOG = LoggerFactory.getLogger(JavaUtils.class);
 
-  CompletableFuture[] EMPTY_COMPLETABLE_FUTURE_ARRAY = {};
+  CompletableFuture<?>[] EMPTY_COMPLETABLE_FUTURE_ARRAY = {};
 
   ConcurrentMap<Class<?>, String> CLASS_SIMPLE_NAMES = new ConcurrentHashMap<>();
   static String getClassSimpleName(Class<?> clazz) {
@@ -120,6 +122,49 @@ public interface JavaUtils {
     }
   }
 
+  static <T> T doPrivileged(Supplier<T> action, Function<SecurityException, T> exceptionHandler) {
+    try {
+      return System.getSecurityManager() == null? action.get()
+          : AccessController.doPrivileged((PrivilegedAction<T>) action::get);
+    } catch (SecurityException e) {
+      return exceptionHandler.apply(e);
+    }
+  }
+
+  static <T> T doPrivileged(Supplier<T> action, Supplier<String> name) {
+    return doPrivileged(action, e -> {
+      LOG.warn("Failed to " + name.get(), e);
+      return null;
+    });
+  }
+
+  /**
+   * Similar to {@link System#getProperty(String)}
+   * except that this method may invoke {@link AccessController#doPrivileged(PrivilegedAction)}
+   * if there is a {@link SecurityManager}.
+   *
+   * @return null if either the property is not set or there is a {@link SecurityException};
+   *         otherwise, return system property value.
+   */
+  static String getSystemProperty(final String key) {
+    Preconditions.assertNotNull(key, "key");
+    Preconditions.assertTrue(!key.isEmpty(), "key is empty.");
+    return doPrivileged(() -> System.getProperty(key), () -> "get system property " + key);
+  }
+
+  /**
+   * Similar to {@link System#setProperty(String, String)}
+   * except that this method may invoke {@link AccessController#doPrivileged(PrivilegedAction)}
+   * if there is a {@link SecurityManager}.
+   * When there is a {@link SecurityException}, this becomes a NOOP.
+   */
+  static void setSystemProperty(String key, String value) {
+    Preconditions.assertNotNull(key, "key");
+    Preconditions.assertTrue(!key.isEmpty(), "key is empty.");
+    Preconditions.assertNotNull(value, "value");
+    doPrivileged(() -> System.setProperty(key, value), () -> "set system property " + key + " to " + value);
+  }
+
   /**
    * Create a memoized supplier which gets a value by invoking the initializer once
    * and then keeps returning the same value as its supplied results.
diff --git a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java
index 5da5fc5c9..75eb34a2d 100644
--- a/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java
+++ b/ratis-grpc/src/main/java/org/apache/ratis/grpc/GrpcFactory.java
@@ -31,6 +31,7 @@ import org.apache.ratis.server.ServerFactory;
 import org.apache.ratis.server.leader.FollowerInfo;
 import org.apache.ratis.server.leader.LeaderState;
 import org.apache.ratis.thirdparty.io.netty.buffer.PooledByteBufAllocator;
+import org.apache.ratis.util.JavaUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,16 +41,27 @@ public class GrpcFactory implements ServerFactory, ClientFactory {
 
   public static final Logger LOG = LoggerFactory.getLogger(GrpcFactory.class);
 
-  private void checkPooledByteBufAllocatorUseCacheForAllThreads(Consumer<String> log) {
-    final String name = "useCacheForAllThreads";
-    final String key = "org.apache.ratis.thirdparty.io.netty.allocator." + name;
+  static final String USE_CACHE_FOR_ALL_THREADS_NAME = "useCacheForAllThreads";
+  static final String USE_CACHE_FOR_ALL_THREADS_KEY = "org.apache.ratis.thirdparty.io.netty.allocator."
+      + USE_CACHE_FOR_ALL_THREADS_NAME;
+  static {
+    // see org.apache.ratis.thirdparty.io.netty.buffer.PooledByteBufAllocator.DEFAULT_USE_CACHE_FOR_ALL_THREADS
+    final String value = JavaUtils.getSystemProperty(USE_CACHE_FOR_ALL_THREADS_KEY);
+    if (value == null) {
+      // Set the property to false, when it is not set.
+      JavaUtils.setSystemProperty(USE_CACHE_FOR_ALL_THREADS_KEY, Boolean.FALSE.toString());
+    }
+  }
+
+  static boolean checkPooledByteBufAllocatorUseCacheForAllThreads(Consumer<String> log) {
     final boolean value = PooledByteBufAllocator.defaultUseCacheForAllThreads();
     if (value) {
-      log.accept("PERFORMANCE WARNING: " + name + " is " + value
+      log.accept("PERFORMANCE WARNING: " + USE_CACHE_FOR_ALL_THREADS_NAME + " is " + true
           + " that may cause Netty to create a lot garbage objects and, as a result, trigger GC.\n"
-          + "\tIt is recommended to disable " + name + " by setting -D" + key
-          + "=" + !value + " in command line.");
+          + "\tIt is recommended to disable " + USE_CACHE_FOR_ALL_THREADS_NAME
+          + " by setting -D" + USE_CACHE_FOR_ALL_THREADS_KEY + "=" + false + " in command line.");
     }
+    return value;
   }
 
   private final GrpcTlsConfig tlsConfig;
diff --git a/ratis-test/src/test/java/org/apache/ratis/grpc/TestGrpcFactory.java b/ratis-test/src/test/java/org/apache/ratis/grpc/TestGrpcFactory.java
new file mode 100644
index 000000000..76fbcee5e
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/grpc/TestGrpcFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.grpc;
+
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.util.JavaUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestGrpcFactory extends BaseTest {
+  @Test
+  public void testUseCacheForAllThreads() {
+    // trigger GrpcFactory static initializer
+    final boolean value = GrpcFactory.checkPooledByteBufAllocatorUseCacheForAllThreads(LOG::info);
+    Assert.assertFalse(value);
+    LOG.info("value is {}", value);
+  }
+}
\ No newline at end of file


[ratis] 02/03: RATIS-1680. TestLeaderInstallSnapshot creates untracked files (#719)

Posted by dr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dragonyliu pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/ratis.git

commit d7fdfd9827a0f6ad57c8e790a25f02a84bd6d9cf
Author: Doroszlai, Attila <64...@users.noreply.github.com>
AuthorDate: Thu Aug 18 18:49:17 2022 +0200

    RATIS-1680. TestLeaderInstallSnapshot creates untracked files (#719)
    
    
    (cherry picked from commit 971e547acb6e5c623bb0e60cc243a77eb75c64c6)
---
 .../ratis/InstallSnapshotFromLeaderTests.java      | 24 ++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java b/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java
index 98be1e64c..bc4de2ff4 100644
--- a/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java
+++ b/ratis-server/src/test/java/org/apache/ratis/InstallSnapshotFromLeaderTests.java
@@ -20,6 +20,7 @@ package org.apache.ratis;
 import org.apache.ratis.client.RaftClient;
 import org.apache.ratis.conf.RaftProperties;
 import org.apache.ratis.protocol.RaftClientReply;
+import org.apache.ratis.protocol.RaftGroupId;
 import org.apache.ratis.protocol.RaftPeerId;
 import org.apache.ratis.server.RaftServer;
 import org.apache.ratis.server.RaftServerConfigKeys;
@@ -28,6 +29,7 @@ import org.apache.ratis.server.impl.RaftServerTestUtil;
 import org.apache.ratis.server.protocol.TermIndex;
 import org.apache.ratis.server.raftlog.RaftLog;
 import org.apache.ratis.server.storage.FileInfo;
+import org.apache.ratis.server.storage.RaftStorage;
 import org.apache.ratis.statemachine.SimpleStateMachine4Testing;
 import org.apache.ratis.statemachine.SnapshotInfo;
 import org.apache.ratis.statemachine.StateMachine;
@@ -106,12 +108,22 @@ public abstract class InstallSnapshotFromLeaderTests<CLUSTER extends MiniRaftClu
     }
 
     private static class StateMachineWithMultiNestedSnapshotFile extends SimpleStateMachine4Testing {
-        // contains two snapshot files
-        // sm/snapshot/1.bin
-        // sm/snapshot/sub/2.bin
-        final File snapshotRoot = new File(getSMdir(), "snapshot");
-        final File file1 = new File(snapshotRoot, "1.bin");
-        final File file2 = new File(new File(snapshotRoot, "sub"), "2.bin");
+
+        File snapshotRoot;
+        File file1;
+        File file2;
+
+        @Override
+        public synchronized void initialize(RaftServer server, RaftGroupId groupId, RaftStorage raftStorage) throws IOException {
+            super.initialize(server, groupId, raftStorage);
+
+            // contains two snapshot files
+            // sm/snapshot/1.bin
+            // sm/snapshot/sub/2.bin
+            snapshotRoot = new File(getSMdir(), "snapshot");
+            file1 = new File(snapshotRoot, "1.bin");
+            file2 = new File(new File(snapshotRoot, "sub"), "2.bin");
+        }
 
         @Override
         public synchronized void pause() {