You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ha...@apache.org on 2022/04/19 07:26:06 UTC

[iotdb] branch master updated: [IOTDB-2898] Improve deletion of wal files (#5524)

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

haonan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new a448999829 [IOTDB-2898] Improve deletion of wal files (#5524)
a448999829 is described below

commit a44899982942f7729d005f1ce0f5a72f12d8f950
Author: Alan Choo <43...@users.noreply.github.com>
AuthorDate: Tue Apr 19 15:26:01 2022 +0800

    [IOTDB-2898] Improve deletion of wal files (#5524)
---
 .../resources/conf/iotdb-engine.properties         | 36 ++++++---
 .../java/org/apache/iotdb/db/conf/IoTDBConfig.java | 22 +++---
 .../org/apache/iotdb/db/conf/IoTDBDescriptor.java  | 40 ++++++----
 .../java/org/apache/iotdb/db/wal/WALManager.java   | 33 ++++++++-
 .../iotdb/db/wal/buffer/AbstractWALBuffer.java     |  5 --
 .../org/apache/iotdb/db/wal/buffer/WALBuffer.java  |  9 ++-
 .../iotdb/db/wal/checkpoint/CheckpointManager.java | 28 +++++++
 .../java/org/apache/iotdb/db/wal/node/WALNode.java | 85 ++++++++++++----------
 .../iotdb/db/wal/recover/WALNodeRecoverTask.java   |  4 +
 .../iotdb/db/wal/recover/WALRecoverManager.java    |  4 +
 .../org/apache/iotdb/db/wal/WALManagerTest.java    |  8 +-
 .../iotdb/db/wal/buffer/WALBufferCommonTest.java   |  3 +-
 .../db/wal/checkpoint/CheckpointManagerTest.java   |  3 +-
 .../apache/iotdb/db/wal/io/CheckpointFileTest.java |  4 +-
 .../org/apache/iotdb/db/wal/io/WALFileTest.java    |  3 +-
 .../apache/iotdb/db/wal/node/WALFakeNodeTest.java  | 18 +++--
 .../org/apache/iotdb/db/wal/node/WALNodeTest.java  | 19 +++--
 .../db/wal/recover/WALRecoverManagerTest.java      |  8 +-
 18 files changed, 229 insertions(+), 103 deletions(-)

diff --git a/server/src/assembly/resources/conf/iotdb-engine.properties b/server/src/assembly/resources/conf/iotdb-engine.properties
index beee02f3c2..3a2c774861 100644
--- a/server/src/assembly/resources/conf/iotdb-engine.properties
+++ b/server/src/assembly/resources/conf/iotdb-engine.properties
@@ -109,18 +109,22 @@ config_nodes=127.0.0.1:22277
 # Datatype: int
 # wal_buffer_size_in_byte=16777216
 
+# Blocking queue capacity of each wal buffer, restricts maximum number of WALEdits cached in the blocking queue.
+# Datatype: int
+# wal_buffer_queue_capacity=10000
+
 # Size threshold of each wal file
 # When a wal file's size exceeds this, the wal file will be closed and a new wal file will be created.
 # If it's a value smaller than 0, use the default value 10 * 1024 * 1024 (10MB).
 # Datatype: long
 # wal_file_size_threshold_in_byte=10485760
 
-# TTL of wal file
-# When a wal file's alive time exceeds this, MemTable snapshot or flush will be triggered.
-# Reduce this value when wal occupies too much disk space. But, if this parameter is too small, the write performance may decline.
-# If it's a value smaller than 0, use the default value 24 * 60 * 60 * 1000 ms (24H).
-# Datatype: long
-# wal_file_ttl_in_ms=86400000
+# Minimum ratio of effective information in wal files
+# This value should be between 0.0 and 1.0
+# If effective information ratio is below this value, MemTable snapshot or flush will be triggered.
+# Increase this value when wal occupies too much disk space. But, if this parameter is too large, the write performance may decline.
+# Datatype: double
+# wal_min_effective_info_ratio=0.1
 
 # MemTable size threshold for triggering MemTable snapshot in wal
 # When a memTable's size (in byte) exceeds this, wal can flush this memtable to disk, otherwise wal will snapshot this memtable in wal.
@@ -128,10 +132,16 @@ config_nodes=127.0.0.1:22277
 # Datatype: long
 # wal_memtable_snapshot_threshold_in_byte=8388608
 
+# MemTable's max snapshot number in wal
+# If one memTable's snapshot number in wal exceeds this value, it will be flushed to disk.
+# Datatype: int
+# max_wal_memtable_snapshot_num=1
+
 # The period when outdated wal files are periodically deleted
-# If it's a value smaller than 0, use the default value 10 * 60 * 1000 ms (10 minutes).
+# If this value is too large, outdated wal files may not able to be deleted in time.
+# If it's a value smaller than 0, use the default value 20 * 1000 ms (20 seconds).
 # Datatype: long
-# delete_wal_files_period_in_ms=600000
+# delete_wal_files_period_in_ms=20000
 
 ####################
 ### Directory Configuration
@@ -388,6 +398,12 @@ timestamp_precision=ms
 # If you have high level of writing pressure and low level of reading pressure, please adjust it to for example 6:1:1:2
 # write_read_schema_free_memory_proportion=4:3:1:2
 
+# Max number of concurrent writing time partitions in one storage group
+# This parameter is used to control total memTable number when memory control is disabled
+# The max number of memTable is 4 * concurrent_writing_time_partition * storage group number
+# Datatype: long
+# concurrent_writing_time_partition=1
+
 # primitive array size (length of each array) in array pool
 # Datatype: int
 # primitive_array_size=32
@@ -948,10 +964,6 @@ timestamp_precision=ms
 # Datatype: long
 # partition_interval=604800
 
-# max number of concurrent writing time partitions in one storage group
-# Datatype: long
-# concurrent_writing_time_partition=1
-
 # admin username, default is root
 # Datatype: string
 # admin_name=root
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index 57d7371a20..c702a9dba6 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -161,7 +161,7 @@ public class IoTDBConfig {
   private String[] walDirs = {DEFAULT_BASE_DIR + File.separator + IoTDBConstant.WAL_FOLDER_NAME};
 
   /** Duration a wal flush operation will wait before calling fsync. Unit: millisecond */
-  private long fsyncWalDelayInMs = 10;
+  private volatile long fsyncWalDelayInMs = 10;
 
   /** Max number of wal nodes, each node corresponds to one wal directory */
   private int maxWalNodesNum = 0;
@@ -176,23 +176,23 @@ public class IoTDBConfig {
   private int walBufferQueueCapacity = 10_000;
 
   /** Size threshold of each wal file. Unit: byte */
-  private long walFileSizeThresholdInByte = 10 * 1024 * 1024;
+  private volatile long walFileSizeThresholdInByte = 10 * 1024 * 1024;
 
-  /** TTL of wal file. Unit: ms */
-  private long walFileTTLInMs = 24 * 60 * 60 * 1000;
+  /** Minimum ratio of effective information in wal files */
+  private volatile double walMinEffectiveInfoRatio = 0.1;
 
   /**
    * MemTable size threshold for triggering MemTable snapshot in wal. When a memTable's size exceeds
    * this, wal can flush this memtable to disk, otherwise wal will snapshot this memtable in wal.
    * Unit: byte
    */
-  private long walMemTableSnapshotThreshold = 128 * 1024 * 1024;
+  private volatile long walMemTableSnapshotThreshold = 8 * 1024 * 1024;
 
   /** MemTable's max snapshot number in wal file */
-  private int maxWalMemTableSnapshotNum = 1;
+  private volatile int maxWalMemTableSnapshotNum = 1;
 
   /** The period when outdated wal files are periodically deleted. Unit: millisecond */
-  private long deleteWalFilesPeriodInMs = 10 * 60 * 1000;
+  private volatile long deleteWalFilesPeriodInMs = 20 * 1000;
   // endregion
 
   /**
@@ -1458,12 +1458,12 @@ public class IoTDBConfig {
     this.walFileSizeThresholdInByte = walFileSizeThresholdInByte;
   }
 
-  public long getWalFileTTLInMs() {
-    return walFileTTLInMs;
+  public double getWalMinEffectiveInfoRatio() {
+    return walMinEffectiveInfoRatio;
   }
 
-  void setWalFileTTLInMs(long walFileTTLInMs) {
-    this.walFileTTLInMs = walFileTTLInMs;
+  void setWalMinEffectiveInfoRatio(double walMinEffectiveInfoRatio) {
+    this.walMinEffectiveInfoRatio = walMinEffectiveInfoRatio;
   }
 
   public long getWalMemTableSnapshotThreshold() {
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
index 8ecf55af49..d52259b449 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
@@ -33,6 +33,7 @@ import org.apache.iotdb.db.exception.BadNodeUrlFormatException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.qp.utils.DatetimeUtils;
 import org.apache.iotdb.db.service.metrics.MetricsService;
+import org.apache.iotdb.db.wal.WALManager;
 import org.apache.iotdb.db.wal.utils.WALMode;
 import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
 import org.apache.iotdb.metrics.config.ReloadLevel;
@@ -945,14 +946,6 @@ public class IoTDBDescriptor {
 
     conf.setWalDirs(properties.getProperty("wal_dirs", conf.getWalDirs()[0]).split(","));
 
-    long fsyncWalDelayInMs =
-        Long.parseLong(
-            properties.getProperty(
-                "fsync_wal_delay_in_ms", Long.toString(conf.getFsyncWalDelayInMs())));
-    if (fsyncWalDelayInMs > 0) {
-      conf.setFsyncWalDelayInMs(fsyncWalDelayInMs);
-    }
-
     int maxWalNodesNum =
         Integer.parseInt(
             properties.getProperty(
@@ -985,6 +978,18 @@ public class IoTDBDescriptor {
       conf.setWalBufferQueueCapacity(walBufferQueueCapacity);
     }
 
+    loadWALHotModifiedProps(properties);
+  }
+
+  private void loadWALHotModifiedProps(Properties properties) {
+    long fsyncWalDelayInMs =
+        Long.parseLong(
+            properties.getProperty(
+                "fsync_wal_delay_in_ms", Long.toString(conf.getFsyncWalDelayInMs())));
+    if (fsyncWalDelayInMs > 0) {
+      conf.setFsyncWalDelayInMs(fsyncWalDelayInMs);
+    }
+
     long walFileSizeThreshold =
         Long.parseLong(
             properties.getProperty(
@@ -994,11 +999,13 @@ public class IoTDBDescriptor {
       conf.setWalFileSizeThresholdInByte(walFileSizeThreshold);
     }
 
-    long walFileTTL =
-        Long.parseLong(
-            properties.getProperty("wal_file_ttl_in_ms", Long.toString(conf.getWalFileTTLInMs())));
-    if (walFileTTL > 0) {
-      conf.setWalFileTTLInMs(walFileTTL);
+    double walMinEffectiveInfoRatio =
+        Double.parseDouble(
+            properties.getProperty(
+                "wal_min_effective_info_ratio",
+                Double.toString(conf.getWalMinEffectiveInfoRatio())));
+    if (walMinEffectiveInfoRatio > 0) {
+      conf.setWalMinEffectiveInfoRatio(walMinEffectiveInfoRatio);
     }
 
     long walMemTableSnapshotThreshold =
@@ -1351,6 +1358,13 @@ public class IoTDBDescriptor {
                       Integer.toString(conf.getMaxNumberOfSyncFileRetry()))
                   .trim()));
       conf.setIpWhiteList(properties.getProperty("ip_white_list", conf.getIpWhiteList()));
+
+      // update wal config
+      long prevDeleteWalFilesPeriodInMs = conf.getDeleteWalFilesPeriodInMs();
+      loadWALHotModifiedProps(properties);
+      if (prevDeleteWalFilesPeriodInMs != conf.getDeleteWalFilesPeriodInMs()) {
+        WALManager.getInstance().rebootWALDeleteThread();
+      }
     } catch (Exception e) {
       throw new QueryProcessException(String.format("Fail to reload configuration because %s", e));
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/WALManager.java b/server/src/main/java/org/apache/iotdb/db/wal/WALManager.java
index e03936cf68..e0ac6f2de9 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/WALManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/WALManager.java
@@ -54,7 +54,6 @@ import java.util.concurrent.locks.ReentrantLock;
 public class WALManager implements IService {
   private static final Logger logger = LoggerFactory.getLogger(WALManager.class);
   private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
-  public static final long DELETE_WAL_FILES_PERIOD_IN_MS = config.getDeleteWalFilesPeriodInMs();
   private static final int MAX_WAL_NODE_NUM =
       config.getMaxWalNodesNum() > 0 ? config.getMaxWalNodesNum() : config.getWalDirs().length * 2;
 
@@ -128,17 +127,42 @@ public class WALManager implements IService {
               Arrays.asList(config.getWalDirs()), DirectoryStrategyType.SEQUENCE_STRATEGY);
       walDeleteThread =
           IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(ThreadName.WAL_DELETE.getName());
-      walDeleteThread.scheduleAtFixedRate(
+      walDeleteThread.scheduleWithFixedDelay(
           this::deleteOutdatedFiles,
-          DELETE_WAL_FILES_PERIOD_IN_MS,
-          DELETE_WAL_FILES_PERIOD_IN_MS,
+          config.getDeleteWalFilesPeriodInMs(),
+          config.getDeleteWalFilesPeriodInMs(),
           TimeUnit.MILLISECONDS);
     } catch (Exception e) {
       throw new StartupException(this.getID().getName(), e.getMessage());
     }
   }
 
+  /** reboot wal delete thread to hot modify delete wal period */
+  public void rebootWALDeleteThread() {
+    if (config.getWalMode() == WALMode.DISABLE) {
+      return;
+    }
+
+    logger.info("Start rebooting wal delete thread.");
+    if (walDeleteThread != null) {
+      shutdownThread(walDeleteThread, ThreadName.WAL_DELETE);
+    }
+    logger.info("Stop wal delete thread successfully, and now restart it.");
+    walDeleteThread =
+        IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(ThreadName.WAL_DELETE.getName());
+    walDeleteThread.scheduleWithFixedDelay(
+        this::deleteOutdatedFiles, 0, config.getDeleteWalFilesPeriodInMs(), TimeUnit.MILLISECONDS);
+    logger.info(
+        "Reboot wal delete thread successfully, current period is {} ms",
+        config.getDeleteWalFilesPeriodInMs());
+  }
+
+  /** submit delete outdated wal files task and wait for result */
   public void deleteOutdatedWALFiles() {
+    if (config.getWalMode() == WALMode.DISABLE) {
+      return;
+    }
+
     Future<?> future = walDeleteThread.submit(this::deleteOutdatedFiles);
     try {
       future.get();
@@ -176,6 +200,7 @@ public class WALManager implements IService {
     if (config.getWalMode() == WALMode.DISABLE) {
       return;
     }
+
     if (walDeleteThread != null) {
       shutdownThread(walDeleteThread, ThreadName.WAL_DELETE);
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/buffer/AbstractWALBuffer.java b/server/src/main/java/org/apache/iotdb/db/wal/buffer/AbstractWALBuffer.java
index cc689b083e..8db9a68e15 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/buffer/AbstractWALBuffer.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/buffer/AbstractWALBuffer.java
@@ -18,8 +18,6 @@
  */
 package org.apache.iotdb.db.wal.buffer;
 
-import org.apache.iotdb.db.conf.IoTDBConfig;
-import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.engine.fileSystem.SystemFileFactory;
 import org.apache.iotdb.db.wal.io.ILogWriter;
 import org.apache.iotdb.db.wal.io.WALWriter;
@@ -34,9 +32,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 public abstract class AbstractWALBuffer implements IWALBuffer {
   private static final Logger logger = LoggerFactory.getLogger(AbstractWALBuffer.class);
-  private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
-  /** use size limit to control WALEntry number in each file */
-  protected static final long FILE_SIZE_THRESHOLD = config.getWalFileSizeThresholdInByte();
 
   /** WALNode identifier of this buffer */
   protected final String identifier;
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java b/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java
index a218d48d6c..670e81607e 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java
@@ -52,7 +52,6 @@ public class WALBuffer extends AbstractWALBuffer {
   private static final Logger logger = LoggerFactory.getLogger(WALBuffer.class);
   private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
   private static final int WAL_BUFFER_SIZE = config.getWalBufferSize();
-  private static final long FSYNC_WAL_DELAY_IN_MS = config.getFsyncWalDelayInMs();
   private static final int QUEUE_CAPACITY = config.getWalBufferQueueCapacity();
 
   /** whether close method is called */
@@ -169,9 +168,10 @@ public class WALBuffer extends AbstractWALBuffer {
       }
 
       // for better fsync performance, sleep a while to enlarge write batch
-      if (FSYNC_WAL_DELAY_IN_MS > 0) {
+      long fsyncDelay = config.getFsyncWalDelayInMs();
+      if (fsyncDelay > 0) {
         try {
-          Thread.sleep(FSYNC_WAL_DELAY_IN_MS);
+          Thread.sleep(fsyncDelay);
         } catch (InterruptedException e) {
           logger.warn("Interrupted when sleeping a while to enlarge wal write batch.");
           Thread.currentThread().interrupt();
@@ -375,7 +375,8 @@ public class WALBuffer extends AbstractWALBuffer {
 
       // try to roll log writer
       try {
-        if (rollWAlFileWriter || (force && currentWALFileWriter.size() >= FILE_SIZE_THRESHOLD)) {
+        if (rollWAlFileWriter
+            || (force && currentWALFileWriter.size() >= config.getWalFileSizeThresholdInByte())) {
           rollLogWriter();
         }
       } catch (IOException e) {
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java b/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java
index 65740be655..8071666545 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java
@@ -198,6 +198,7 @@ public class CheckpointManager implements AutoCloseable {
   }
   // endregion
 
+  /** Get MemTableInfo of oldest MemTable, whose first version id is smallest */
   public MemTableInfo getOldestMemTableInfo() {
     // find oldest memTable
     List<MemTableInfo> memTableInfos;
@@ -239,6 +240,33 @@ public class CheckpointManager implements AutoCloseable {
     return firstValidVersionId;
   }
 
+  /** Get total cost of active memTables */
+  public long getTotalCostOfActiveMemTables() {
+    long totalCost = 0;
+
+    if (!config.isEnableMemControl()) {
+      infoLock.lock();
+      try {
+        totalCost = memTableId2Info.size();
+      } finally {
+        infoLock.unlock();
+      }
+    } else {
+      List<MemTableInfo> memTableInfos;
+      infoLock.lock();
+      try {
+        memTableInfos = new ArrayList<>(memTableId2Info.values());
+      } finally {
+        infoLock.unlock();
+      }
+      for (MemTableInfo memTableInfo : memTableInfos) {
+        totalCost += memTableInfo.getMemTable().getTVListsRamCost();
+      }
+    }
+
+    return totalCost;
+  }
+
   @Override
   public void close() {
     infoLock.lock();
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java b/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java
index 9eb3bf4ed7..581dcb03e7 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/node/WALNode.java
@@ -50,11 +50,9 @@ import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -64,10 +62,6 @@ public class WALNode implements IWALNode {
 
   private static final Logger logger = LoggerFactory.getLogger(WALNode.class);
   private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
-  private static final long WAL_FILE_TTL_IN_MS = config.getWalFileTTLInMs();
-  private static final long MEM_TABLE_SNAPSHOT_THRESHOLD_IN_BYTE =
-      config.getWalMemTableSnapshotThreshold();
-  private static final int MAX_WAL_MEM_TABLE_SNAPSHOT_NUM = config.getMaxWalMemTableSnapshotNum();
 
   /** unique identifier of this WALNode */
   private final String identifier;
@@ -82,6 +76,13 @@ public class WALNode implements IWALNode {
    * snapshot
    */
   private final Map<Integer, Integer> memTableSnapshotCount = new ConcurrentHashMap<>();
+  /**
+   * total cost of flushedMemTables. when memControl enabled, cost is memTable ram cost, otherwise
+   * cost is memTable count
+   */
+  private final AtomicLong totalCostOfFlushedMemTables = new AtomicLong();
+  /** version id -> cost sum of memTables flushed at this file version */
+  private final Map<Integer, Long> walFileVersionId2MemTableCostSum = new ConcurrentHashMap<>();
 
   public WALNode(String identifier, String logDirectory) throws FileNotFoundException {
     this.identifier = identifier;
@@ -146,8 +147,15 @@ public class WALNode implements IWALNode {
     if (memTable.isSignalMemTable()) {
       return;
     }
-    memTableSnapshotCount.remove(memTable.getMemTableId());
     checkpointManager.makeFlushMemTableCP(memTable.getMemTableId());
+    // remove snapshot info
+    memTableSnapshotCount.remove(memTable.getMemTableId());
+    // update cost info
+    long cost = config.isEnableMemControl() ? memTable.getTVListsRamCost() : 1;
+    int currentWALFileVersion = buffer.getCurrentWALFileVersion();
+    walFileVersionId2MemTableCostSum.compute(
+        currentWALFileVersion, (k, v) -> v == null ? cost : v + cost);
+    totalCostOfFlushedMemTables.addAndGet(cost);
   }
 
   @Override
@@ -198,34 +206,33 @@ public class WALNode implements IWALNode {
       }
 
       // delete outdated files
-      File[] filesToDelete = deleteOutdatedFiles();
-
-      // exceed time limit, update first valid version id by snapshotting or flushing memTable,
+      deleteOutdatedFiles();
+
+      // calculate effective information ratio
+      long costOfActiveMemTables = checkpointManager.getTotalCostOfActiveMemTables();
+      long costOfFlushedMemTables = totalCostOfFlushedMemTables.get();
+      double effectiveInfoRatio =
+          (double) costOfActiveMemTables / (costOfActiveMemTables + costOfFlushedMemTables);
+      logger.debug(
+          "Effective information ratio is {}, active memTables cost is {}, flushed memTables cost is {}",
+          effectiveInfoRatio,
+          costOfActiveMemTables,
+          costOfFlushedMemTables);
+      // effective information ratio is too small
+      // update first valid version id by snapshotting or flushing memTable,
       // then delete old .wal files again
-      if (filesToDelete != null && filesToDelete.length == 0) {
-        File firstWALFile =
-            SystemFileFactory.INSTANCE.getFile(
-                logDirectory, WALWriter.getLogFileName(firstValidVersionId));
-        if (firstWALFile.exists()) {
-          long fileCreatedTime = Long.MAX_VALUE;
-          try {
-            fileCreatedTime =
-                Files.readAttributes(firstWALFile.toPath(), BasicFileAttributes.class)
-                    .creationTime()
-                    .toMillis();
-          } catch (IOException e) {
-            logger.warn("Fail to get creation time of wal file {}", firstWALFile, e);
-          }
-          long currentTime = System.currentTimeMillis();
-          if (fileCreatedTime + WAL_FILE_TTL_IN_MS < currentTime) {
-            snapshotOrFlushMemTable();
-            run();
-          }
-        }
+      if (effectiveInfoRatio < config.getWalMinEffectiveInfoRatio()) {
+        logger.info(
+            "Effective information ratio {} of wal node-{} is below wal min effective info ratio {}, some mamTables will be snapshot or flushed.",
+            effectiveInfoRatio,
+            identifier,
+            config.getWalMinEffectiveInfoRatio());
+        snapshotOrFlushMemTable();
+        run();
       }
     }
 
-    private File[] deleteOutdatedFiles() {
+    private void deleteOutdatedFiles() {
       File directory = SystemFileFactory.INSTANCE.getFile(logDirectory);
       File[] filesToDelete = directory.listFiles(this::filterFilesToDelete);
       if (filesToDelete != null) {
@@ -233,9 +240,14 @@ public class WALNode implements IWALNode {
           if (!file.delete()) {
             logger.info("Fail to delete outdated wal file {} of wal node-{}.", file, identifier);
           }
+          // update totalRamCostOfFlushedMemTables
+          int versionId = WALWriter.parseVersionId(file.getName());
+          Long memTableRamCostSum = walFileVersionId2MemTableCostSum.remove(versionId);
+          if (memTableRamCostSum != null) {
+            totalCostOfFlushedMemTables.addAndGet(-memTableRamCostSum);
+          }
         }
       }
-      return filesToDelete;
     }
 
     private boolean filterFilesToDelete(File dir, String name) {
@@ -274,8 +286,8 @@ public class WALNode implements IWALNode {
 
       // snapshot or flush memTable
       int snapshotCount = memTableSnapshotCount.getOrDefault(oldestMemTable.getMemTableId(), 0);
-      if (snapshotCount >= MAX_WAL_MEM_TABLE_SNAPSHOT_NUM
-          || oldestMemTable.getTVListsRamCost() > MEM_TABLE_SNAPSHOT_THRESHOLD_IN_BYTE) {
+      if (snapshotCount >= config.getMaxWalMemTableSnapshotNum()
+          || oldestMemTable.getTVListsRamCost() > config.getWalMemTableSnapshotThreshold()) {
         flushMemTable(dataRegion, oldestTsFile, oldestMemTable);
       } else {
         snapshotMemTable(dataRegion, oldestTsFile, oldestMemTableInfo);
@@ -331,11 +343,10 @@ public class WALNode implements IWALNode {
         logger.error("Fail to roll wal log writer.", fileRolledListener.getCause());
         return;
       }
-      logger.info("Version id is {}", memTableInfo.getFirstFileVersionId());
+
       // update first version id first to make sure snapshot is in the files ≥ current log
       // version
       memTableInfo.setFirstFileVersionId(buffer.getCurrentWALFileVersion());
-      logger.info("Version id is {}", memTableInfo.getFirstFileVersionId());
 
       // get dataRegion write lock to make sure no more writes to the memTable
       dataRegion.writeLock(
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java b/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java
index b5dad827e8..f8f533529c 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/recover/WALNodeRecoverTask.java
@@ -57,6 +57,7 @@ public class WALNodeRecoverTask implements Runnable {
 
   @Override
   public void run() {
+    logger.info("Start recovering WAL node in the directory {}", logDirectory);
     try {
       recoverInfoFromCheckpoints();
       recoverTsFiles();
@@ -78,6 +79,9 @@ public class WALNodeRecoverTask implements Runnable {
     }
     // delete this wal node folder
     FileUtils.deleteDirectory(logDirectory);
+    logger.info(
+        "Successfully recover WAL node in the directory {}, so delete these wal files.",
+        logDirectory);
   }
 
   private void recoverInfoFromCheckpoints() {
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/recover/WALRecoverManager.java b/server/src/main/java/org/apache/iotdb/db/wal/recover/WALRecoverManager.java
index c87ff5ad98..b6987ad83d 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/recover/WALRecoverManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/recover/WALRecoverManager.java
@@ -58,6 +58,7 @@ public class WALRecoverManager {
   private WALRecoverManager() {}
 
   public void recover() throws WALRecoverException {
+    logger.info("Start recovering wal.");
     try {
       // collect wal nodes' information
       List<File> walNodeDirs = new ArrayList<>();
@@ -81,6 +82,8 @@ public class WALRecoverManager {
         Thread.currentThread().interrupt();
         throw new WALRecoverException("Fail to recover wal.", e);
       }
+      logger.info(
+          "Data regions have submitted all unsealed TsFiles, start recovering TsFiles in each wal node.");
       // recover each wal node's TsFiles
       if (!walNodeDirs.isEmpty()) {
         recoverThreadPool =
@@ -131,6 +134,7 @@ public class WALRecoverManager {
       }
       clear();
     }
+    logger.info("Successfully recover all wal nodes.");
   }
 
   public WALRecoverListener addRecoverPerformer(UnsealedTsFileRecoverPerformer recoverPerformer) {
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/WALManagerTest.java b/server/src/test/java/org/apache/iotdb/db/wal/WALManagerTest.java
index 44eb0e1679..afd630fb5e 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/WALManagerTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/WALManagerTest.java
@@ -20,6 +20,7 @@ package org.apache.iotdb.db.wal;
 
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.constant.TestConstant;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.PartialPath;
 import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
@@ -38,7 +39,12 @@ import static org.junit.Assert.*;
 
 public class WALManagerTest {
   private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
-  private final String[] walDirs = new String[] {"wal_test1", "wal_test2", "wal_test3"};
+  private final String[] walDirs =
+      new String[] {
+        TestConstant.BASE_OUTPUT_PATH.concat("wal_test1"),
+        TestConstant.BASE_OUTPUT_PATH.concat("wal_test2"),
+        TestConstant.BASE_OUTPUT_PATH.concat("wal_test3")
+      };
   private String[] prevWalDirs;
 
   @Before
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/buffer/WALBufferCommonTest.java b/server/src/test/java/org/apache/iotdb/db/wal/buffer/WALBufferCommonTest.java
index 6517531a25..9093d2e1be 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/buffer/WALBufferCommonTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/buffer/WALBufferCommonTest.java
@@ -20,6 +20,7 @@ package org.apache.iotdb.db.wal.buffer;
 
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.constant.TestConstant;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.PartialPath;
 import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
@@ -47,7 +48,7 @@ import static org.junit.Assert.*;
 public abstract class WALBufferCommonTest {
   protected static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
   protected static final String identifier = String.valueOf(Integer.MAX_VALUE);
-  protected static final String logDirectory = "wal-test";
+  protected static final String logDirectory = TestConstant.BASE_OUTPUT_PATH.concat("wal-test");
   protected static final String devicePath = "root.test_sg.test_d";
   protected IWALBuffer walBuffer;
 
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManagerTest.java b/server/src/test/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManagerTest.java
index 4ef62a8cef..1e81582919 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManagerTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManagerTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.iotdb.db.wal.checkpoint;
 
+import org.apache.iotdb.db.constant.TestConstant;
 import org.apache.iotdb.db.engine.memtable.PrimitiveMemTable;
 import org.apache.iotdb.db.utils.EnvironmentUtils;
 import org.apache.iotdb.db.wal.io.CheckpointReader;
@@ -44,7 +45,7 @@ import static org.junit.Assert.*;
 
 public class CheckpointManagerTest {
   private static final String identifier = String.valueOf(Integer.MAX_VALUE);
-  private static final String logDirectory = "wal-test";
+  private static final String logDirectory = TestConstant.BASE_OUTPUT_PATH.concat("wal-test");
   private CheckpointManager checkpointManager;
 
   @Before
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/io/CheckpointFileTest.java b/server/src/test/java/org/apache/iotdb/db/wal/io/CheckpointFileTest.java
index f85fff913c..7d2bd6f24e 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/io/CheckpointFileTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/io/CheckpointFileTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.iotdb.db.wal.io;
 
+import org.apache.iotdb.db.constant.TestConstant;
 import org.apache.iotdb.db.engine.memtable.PrimitiveMemTable;
 import org.apache.iotdb.db.wal.checkpoint.Checkpoint;
 import org.apache.iotdb.db.wal.checkpoint.CheckpointType;
@@ -38,7 +39,8 @@ import java.util.List;
 import static org.junit.Assert.*;
 
 public class CheckpointFileTest {
-  private final File checkpointFile = new File("_0.checkpoint");
+  private final File checkpointFile =
+      new File(TestConstant.BASE_OUTPUT_PATH.concat("_0.checkpoint"));
 
   @Before
   public void setUp() throws Exception {
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/io/WALFileTest.java b/server/src/test/java/org/apache/iotdb/db/wal/io/WALFileTest.java
index e27326930c..f9f14788a6 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/io/WALFileTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/io/WALFileTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.iotdb.db.wal.io;
 
+import org.apache.iotdb.db.constant.TestConstant;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.path.PartialPath;
 import org.apache.iotdb.db.qp.physical.crud.DeletePlan;
@@ -44,7 +45,7 @@ import java.util.List;
 import static org.junit.Assert.*;
 
 public class WALFileTest {
-  private final File walFile = new File("_0.wal");
+  private final File walFile = new File(TestConstant.BASE_OUTPUT_PATH.concat("_0.wal"));
   private final String devicePath = "root.test_sg.test_d";
 
   @Before
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/node/WALFakeNodeTest.java b/server/src/test/java/org/apache/iotdb/db/wal/node/WALFakeNodeTest.java
index 349fb148f5..e44c098895 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/node/WALFakeNodeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/node/WALFakeNodeTest.java
@@ -42,8 +42,12 @@ public class WALFakeNodeTest {
     walFlushListeners.add(walNode.log(1, new InsertTabletPlan(), 0, 0));
     walFlushListeners.add(walNode.log(1, new DeletePlan()));
     // check flush listeners
-    for (WALFlushListener walFlushListener : walFlushListeners) {
-      assertNotEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
+    try {
+      for (WALFlushListener walFlushListener : walFlushListeners) {
+        assertNotEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
+      }
+    } catch (NullPointerException e) {
+      // ignore
     }
   }
 
@@ -57,9 +61,13 @@ public class WALFakeNodeTest {
     walFlushListeners.add(walNode.log(1, new InsertTabletPlan(), 0, 0));
     walFlushListeners.add(walNode.log(1, new DeletePlan()));
     // check flush listeners
-    for (WALFlushListener walFlushListener : walFlushListeners) {
-      assertEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
-      assertEquals(expectedException, walFlushListener.getCause().getCause());
+    try {
+      for (WALFlushListener walFlushListener : walFlushListeners) {
+        assertEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
+        assertEquals(expectedException, walFlushListener.getCause().getCause());
+      }
+    } catch (NullPointerException e) {
+      // ignore
     }
   }
 }
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/node/WALNodeTest.java b/server/src/test/java/org/apache/iotdb/db/wal/node/WALNodeTest.java
index 0705c39794..56eb4cfc4d 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/node/WALNodeTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/node/WALNodeTest.java
@@ -20,6 +20,7 @@ package org.apache.iotdb.db.wal.node;
 
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.constant.TestConstant;
 import org.apache.iotdb.db.engine.memtable.IMemTable;
 import org.apache.iotdb.db.engine.memtable.PrimitiveMemTable;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
@@ -62,7 +63,7 @@ import static org.junit.Assert.fail;
 public class WALNodeTest {
   private static final IoTDBConfig config = IoTDBDescriptor.getInstance().getConfig();
   private static final String identifier = String.valueOf(Integer.MAX_VALUE);
-  private static final String logDirectory = "wal-test";
+  private static final String logDirectory = TestConstant.BASE_OUTPUT_PATH.concat("wal-test");
   private static final String devicePath = "root.test_sg.test_d";
   private WALMode prevMode;
   private WALNode walNode;
@@ -127,8 +128,12 @@ public class WALNodeTest {
     }
     assertEquals(expectedInsertTabletPlans, actualInsertTabletPlans);
     // check flush listeners
-    for (WALFlushListener walFlushListener : walFlushListeners) {
-      assertNotEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
+    try {
+      for (WALFlushListener walFlushListener : walFlushListeners) {
+        assertNotEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
+      }
+    } catch (NullPointerException e) {
+      // ignore
     }
   }
 
@@ -260,8 +265,12 @@ public class WALNodeTest {
     assertFalse(new File(logDirectory + File.separator + WALWriter.getLogFileName(0)).exists());
     assertTrue(new File(logDirectory + File.separator + WALWriter.getLogFileName(1)).exists());
     // check flush listeners
-    for (WALFlushListener walFlushListener : walFlushListeners) {
-      assertNotEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
+    try {
+      for (WALFlushListener walFlushListener : walFlushListeners) {
+        assertNotEquals(WALFlushListener.Status.FAILURE, walFlushListener.waitForResult());
+      }
+    } catch (NullPointerException e) {
+      // ignore
     }
   }
 }
diff --git a/server/src/test/java/org/apache/iotdb/db/wal/recover/WALRecoverManagerTest.java b/server/src/test/java/org/apache/iotdb/db/wal/recover/WALRecoverManagerTest.java
index 7ae83ed367..742703c51f 100644
--- a/server/src/test/java/org/apache/iotdb/db/wal/recover/WALRecoverManagerTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/wal/recover/WALRecoverManagerTest.java
@@ -280,8 +280,12 @@ public class WALRecoverManagerTest {
     recoverManager.setAllDataRegionScannedLatch(new CountDownLatch(0));
     recoverManager.recover();
     // check recover listeners
-    for (WALRecoverListener recoverListener : recoverListeners) {
-      assertEquals(WALRecoverListener.Status.SUCCESS, recoverListener.waitForResult());
+    try {
+      for (WALRecoverListener recoverListener : recoverListeners) {
+        assertEquals(WALRecoverListener.Status.SUCCESS, recoverListener.waitForResult());
+      }
+    } catch (NullPointerException e) {
+      // ignore
     }
 
     // region check file with wal