You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2021/03/23 14:39:16 UTC

[ignite] branch ignite-cdc updated: IGNITE-13582 WAL force rollover timeout introduced (#8902)

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

nizhikov pushed a commit to branch ignite-cdc
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/ignite-cdc by this push:
     new 32ea9a9  IGNITE-13582 WAL force rollover timeout introduced (#8902)
32ea9a9 is described below

commit 32ea9a9e2219965a1e718838d71c3424bbd4435c
Author: Nikolay <ni...@apache.org>
AuthorDate: Tue Mar 23 17:38:57 2021 +0300

    IGNITE-13582 WAL force rollover timeout introduced (#8902)
---
 .../configuration/DataStorageConfiguration.java    | 26 +++++++
 .../persistence/wal/FileWriteAheadLogManager.java  | 90 +++++++++++++++-------
 .../platform/utils/PlatformConfigurationUtils.java |  4 +-
 .../visor/node/VisorDataStorageConfiguration.java  | 15 +++-
 .../db/wal/reader/IgniteWalReaderTest.java         | 44 +++++++++++
 .../Config/full-config.xml                         |  1 +
 .../IgniteConfigurationSerializerTest.cs           |  2 +
 .../IgniteConfigurationTest.cs                     |  3 +
 .../Configuration/DataStorageConfiguration.cs      |  9 +++
 .../IgniteConfigurationSection.xsd                 |  5 ++
 10 files changed, 170 insertions(+), 29 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
index 6f5b338..4574feb 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
@@ -27,6 +27,7 @@ import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteExperimental;
 import org.apache.ignite.mxbean.MetricsMxBean;
 import org.jetbrains.annotations.Nullable;
 
@@ -288,6 +289,10 @@ public class DataStorageConfiguration implements Serializable {
      */
     private long walAutoArchiveAfterInactivity = -1;
 
+    /** Time interval (in milliseconds) for force archiving of incompletely WAL segment. */
+    @IgniteExperimental
+    private long walForceArchiveTimeout = -1;
+
     /**
      * If true, threads that generate dirty pages too fast during ongoing checkpoint will be throttled.
      */
@@ -1016,6 +1021,27 @@ public class DataStorageConfiguration implements Serializable {
     }
 
     /**
+     * @param walForceArchiveTimeout time in millis to run auto archiving segment (even if incomplete) after last
+     * record logging.<br> Positive value enables incomplete segment archiving after timeout (inactivity).<br> Zero or
+     * negative  value disables auto archiving.
+     * @return current configuration instance for chaining
+     */
+    @IgniteExperimental
+    public DataStorageConfiguration setWalForceArchiveTimeout(long walForceArchiveTimeout) {
+        this.walForceArchiveTimeout = walForceArchiveTimeout;
+
+        return this;
+    }
+
+    /**
+     * @return time in millis to run auto archiving WAL segment (even if incomplete) after last record log
+     */
+    @IgniteExperimental
+    public long getWalForceArchiveTimeout() {
+        return walForceArchiveTimeout;
+    }
+
+    /**
      * This property defines order of writing pages to disk storage during checkpoint.
      *
      * @return Checkpoint write order.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
index 9407404..3982cb2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
@@ -326,6 +326,9 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
      */
     private final long walAutoArchiveAfterInactivity;
 
+    /** Positive (non-0) value indicates WAL must be archived even if not complete. */
+    private final long walForceArchiveTimeout;
+
     /**
      * Container with last WAL record logged timestamp.<br> Zero value means there was no records logged to current
      * segment, skip possible archiving for this case<br> Value is filled only for case {@link
@@ -333,6 +336,9 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
      */
     private final AtomicLong lastRecordLoggedMs = new AtomicLong();
 
+    /** Last rollover time. */
+    private AtomicLong lastRolloverMs;
+
     /**
      * Cancellable task for {@link WALMode#BACKGROUND}, should be cancelled at shutdown.
      * Null for non background modes.
@@ -401,8 +407,12 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
         ioFactory = mode == WALMode.FSYNC ? dsCfg.getFileIOFactory() : new RandomAccessFileIOFactory();
         segmentFileInputFactory = new SimpleSegmentFileInputFactory();
         walAutoArchiveAfterInactivity = dsCfg.getWalAutoArchiveAfterInactivity();
+        walForceArchiveTimeout = dsCfg.getWalForceArchiveTimeout();
 
-        timeoutRolloverMux = walAutoArchiveAfterInactivity > 0 ? new Object() : null;
+        timeoutRolloverMux = (walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0) ? new Object() : null;
+
+        if (walForceArchiveTimeout > 0)
+            lastRolloverMs = new AtomicLong();
 
         double thresholdWalArchiveSizePercentage = IgniteSystemProperties.getDouble(
             IGNITE_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE, DFLT_THRESHOLD_WAL_ARCHIVE_SIZE_PERCENTAGE);
@@ -748,21 +758,30 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
         if (mode == WALMode.BACKGROUND)
             backgroundFlushSchedule = cctx.time().schedule(this::doFlush, flushFreq, flushFreq);
 
-        if (walAutoArchiveAfterInactivity > 0)
-            scheduleNextInactivityPeriodElapsedCheck();
+        if (walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0)
+            scheduleNextRolloverCheck();
     }
 
     /**
-     * Schedules next check of inactivity period expired. Based on current record update timestamp. At timeout method
-     * does check of inactivity period and schedules new launch.
+     * Schedules next rollover check.
+     * If {@link DataStorageConfiguration#getWalForceArchiveTimeout()} configured rollover happens forcefully.
+     * Else check based on current record update timestamp and at timeout method does check of inactivity period and schedules new launch.
      */
-    private void scheduleNextInactivityPeriodElapsedCheck() {
-        assert walAutoArchiveAfterInactivity > 0;
+    private void scheduleNextRolloverCheck() {
+        assert walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0;
         assert timeoutRolloverMux != null;
 
         synchronized (timeoutRolloverMux) {
-            long lastRecMs = lastRecordLoggedMs.get();
-            long nextEndTime = lastRecMs <= 0 ? U.currentTimeMillis() : lastRecMs + walAutoArchiveAfterInactivity;
+            long nextEndTime;
+
+            if (walForceArchiveTimeout > 0) {
+                long lastRollover = lastRolloverMs.get();
+                nextEndTime = lastRollover == 0 ? U.currentTimeMillis() : lastRollover + walForceArchiveTimeout;
+            }
+            else {
+                long lastRecMs = lastRecordLoggedMs.get();
+                nextEndTime = lastRecMs <= 0 ? U.currentTimeMillis() : lastRecMs + walAutoArchiveAfterInactivity;
+            }
 
             cctx.time().addTimeoutObject(timeoutRollover = new TimeoutRollover(nextEndTime));
         }
@@ -774,25 +793,38 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
     }
 
     /**
-     * Checks if there was elapsed significant period of inactivity. If WAL auto-archive is enabled using
-     * {@link #walAutoArchiveAfterInactivity} > 0 this method will activate roll over by timeout.<br>
+     * Checks if there was elapsed significant period of inactivity or force archive timeout.
+     * If WAL auto-archive is enabled using {@link #walAutoArchiveAfterInactivity} > 0 or {@link #walForceArchiveTimeout}
+     * this method will activate roll over by timeout.
      */
-    private void checkWalRolloverRequiredDuringInactivityPeriod() {
-        if (walAutoArchiveAfterInactivity <= 0)
-            return; // feature not configured, nothing to do
+    private void checkWalRolloverRequired() {
+        if (walAutoArchiveAfterInactivity <= 0 && walForceArchiveTimeout <= 0)
+            return; // feature not configured, nothing to do.
 
         final long lastRecMs = lastRecordLoggedMs.get();
 
         if (lastRecMs == 0)
-            return; //no records were logged to current segment, does not consider inactivity
+            return; //no records were logged to current segment, does not consider inactivity.
 
-        final long elapsedMs = U.currentTimeMillis() - lastRecMs;
+        if (walForceArchiveTimeout > 0) {
+            final long lastRollover = lastRolloverMs.get();
+            final long elapsedMs = U.currentTimeMillis() - lastRollover;
 
-        if (elapsedMs <= walAutoArchiveAfterInactivity)
-            return; // not enough time elapsed since last write
+            if (elapsedMs < walForceArchiveTimeout)
+                return; // not enough time elapsed since last rollover.
 
-        if (!lastRecordLoggedMs.compareAndSet(lastRecMs, 0))
-            return; // record write occurred concurrently
+            if (!lastRolloverMs.compareAndSet(lastRollover, 0))
+                return; // record write occurred concurrently.
+        }
+        else {
+            final long elapsedMs = U.currentTimeMillis() - lastRecMs;
+
+            if (elapsedMs <= walAutoArchiveAfterInactivity)
+                return; // not enough time elapsed since last write.
+
+            if (!lastRecordLoggedMs.compareAndSet(lastRecMs, 0))
+                return; // record write occurred concurrently.
+        }
 
         final FileWriteHandle handle = currentHandle();
 
@@ -881,7 +913,7 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
             if (ptr != null) {
                 metrics.onWalRecordLogged(rec.size());
 
-                if (walAutoArchiveAfterInactivity > 0)
+                if (walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0)
                     lastRecordLoggedMs.set(U.currentTimeMillis());
 
                 return ptr;
@@ -1305,9 +1337,13 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
 
             assert updated : "Concurrent updates on rollover are not allowed";
 
-            if (walAutoArchiveAfterInactivity > 0)
+            if (walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0) {
                 lastRecordLoggedMs.set(0);
 
+                if (walForceArchiveTimeout > 0)
+                    lastRolloverMs.set(U.currentTimeMillis());
+            }
+
             // Let other threads to proceed with new segment.
             hnd.signalNextAvailable();
         }
@@ -3443,7 +3479,7 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
 
         /** {@inheritDoc} */
         @Override public void onTimeout() {
-            assert walAutoArchiveAfterInactivity > 0;
+            assert walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0;
             assert timeoutRolloverMux != null;
 
             synchronized (timeoutRolloverMux) {
@@ -3453,9 +3489,9 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
                             new Time(U.currentTimeMillis()).toString() + ")");
                     }
 
-                    checkWalRolloverRequiredDuringInactivityPeriod();
+                    checkWalRolloverRequired();
 
-                    scheduleNextInactivityPeriodElapsedCheck();
+                    scheduleNextRolloverCheck();
                 }
             }
         }
@@ -3464,7 +3500,7 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
          * Cancel auto rollover.
          */
         public void cancel() {
-            assert walAutoArchiveAfterInactivity > 0;
+            assert walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0;
             assert timeoutRolloverMux != null;
 
             synchronized (timeoutRolloverMux) {
@@ -3480,7 +3516,7 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
      * Stop auto rollover.
      */
     private void stopAutoRollover() {
-        if (walAutoArchiveAfterInactivity > 0) {
+        if (walAutoArchiveAfterInactivity > 0 || walForceArchiveTimeout > 0) {
             assert timeoutRolloverMux != null;
 
             synchronized (timeoutRolloverMux) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
index fbe3218..eec1607 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
@@ -2055,7 +2055,8 @@ public class PlatformConfigurationUtils {
                 .setSystemRegionMaxSize(in.readLong())
                 .setPageSize(in.readInt())
                 .setConcurrencyLevel(in.readInt())
-                .setWalAutoArchiveAfterInactivity(in.readLong());
+                .setWalAutoArchiveAfterInactivity(in.readLong())
+                .setWalForceArchiveTimeout(in.readLong());
 
         if (in.readBoolean())
             res.setCheckpointReadLockTimeout(in.readLong());
@@ -2191,6 +2192,7 @@ public class PlatformConfigurationUtils {
             w.writeInt(cfg.getPageSize());
             w.writeInt(cfg.getConcurrencyLevel());
             w.writeLong(cfg.getWalAutoArchiveAfterInactivity());
+            w.writeLong(cfg.getWalForceArchiveTimeout());
 
             if (cfg.getCheckpointReadLockTimeout() != null) {
                 w.writeBoolean(true);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorDataStorageConfiguration.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorDataStorageConfiguration.java
index d0e0f91..243127c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorDataStorageConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/node/VisorDataStorageConfiguration.java
@@ -116,9 +116,12 @@ public class VisorDataStorageConfiguration extends VisorDataTransferObject {
     /** Time interval (in milliseconds) for rate-based metrics. */
     private long metricsRateTimeInterval;
 
-    /** Time interval (in milliseconds) for running auto archiving for incompletely WAL segment */
+    /** Time interval (in milliseconds) for running auto archiving for incompletely WAL segment. */
     private long walAutoArchiveAfterInactivity;
 
+    /** Time interval (in milliseconds) for running auto archiving for incompletely WAL segment. */
+    private long walForceArchiveTimeout;
+
     /** If true, threads that generate dirty pages too fast during ongoing checkpoint will be throttled. */
     private boolean writeThrottlingEnabled;
 
@@ -177,6 +180,7 @@ public class VisorDataStorageConfiguration extends VisorDataTransferObject {
         metricsSubIntervalCount = cfg.getMetricsSubIntervalCount();
         metricsRateTimeInterval = cfg.getMetricsRateTimeInterval();
         walAutoArchiveAfterInactivity = cfg.getWalAutoArchiveAfterInactivity();
+        walForceArchiveTimeout = cfg.getWalForceArchiveTimeout();
         writeThrottlingEnabled = cfg.isWriteThrottlingEnabled();
         walCompactionEnabled = cfg.isWalCompactionEnabled();
     }
@@ -371,6 +375,13 @@ public class VisorDataStorageConfiguration extends VisorDataTransferObject {
     }
 
     /**
+     * @return Time in millis.
+     */
+    public long getWalForceArchiveTimeout() {
+        return walForceArchiveTimeout;
+    }
+
+    /**
      * @return Flag indicating whether write throttling is enabled.
      */
     public boolean isWriteThrottlingEnabled() {
@@ -425,6 +436,7 @@ public class VisorDataStorageConfiguration extends VisorDataTransferObject {
         out.writeInt(metricsSubIntervalCount);
         out.writeLong(metricsRateTimeInterval);
         out.writeLong(walAutoArchiveAfterInactivity);
+        out.writeLong(walForceArchiveTimeout);
         out.writeBoolean(writeThrottlingEnabled);
         out.writeInt(walBufSize);
         out.writeBoolean(walCompactionEnabled);
@@ -460,6 +472,7 @@ public class VisorDataStorageConfiguration extends VisorDataTransferObject {
         metricsSubIntervalCount = in.readInt();
         metricsRateTimeInterval = in.readLong();
         walAutoArchiveAfterInactivity = in.readLong();
+        walForceArchiveTimeout = in.readLong();
         writeThrottlingEnabled = in.readBoolean();
 
         if (protoVer > V1) {
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java
index 76188f5..005d491 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/reader/IgniteWalReaderTest.java
@@ -88,6 +88,7 @@ import org.junit.Assume;
 import org.junit.Test;
 
 import static java.util.Arrays.fill;
+import static org.apache.ignite.cluster.ClusterState.ACTIVE;
 import static org.apache.ignite.events.EventType.EVT_WAL_SEGMENT_ARCHIVED;
 import static org.apache.ignite.events.EventType.EVT_WAL_SEGMENT_COMPACTED;
 import static org.apache.ignite.internal.pagemem.wal.record.WALRecord.RecordType.DATA_RECORD;
@@ -119,6 +120,9 @@ public class IgniteWalReaderTest extends GridCommonAbstractTest {
      */
     private int archiveIncompleteSegmentAfterInactivityMs;
 
+    /** Force archive timeout in milliseconds. */
+    private int forceArchiveSegmentMs;
+
     /** Custom wal mode. */
     private WALMode customWalMode;
 
@@ -156,6 +160,9 @@ public class IgniteWalReaderTest extends GridCommonAbstractTest {
         if (archiveIncompleteSegmentAfterInactivityMs > 0)
             dsCfg.setWalAutoArchiveAfterInactivity(archiveIncompleteSegmentAfterInactivityMs);
 
+        if (forceArchiveSegmentMs > 0)
+            dsCfg.setWalForceArchiveTimeout(forceArchiveSegmentMs);
+
         String workDir = U.defaultWorkDirectory();
         File db = U.resolveWorkDirectory(workDir, DFLT_STORE_DIR, false);
         File wal = new File(db, "wal");
@@ -343,6 +350,43 @@ public class IgniteWalReaderTest extends GridCommonAbstractTest {
     }
 
     /**
+     * Tests force time out based WAL segment archiving.
+     *
+     * @throws Exception if failure occurs.
+     */
+    @Test
+    public void testForceArchiveSegment() throws Exception {
+        AtomicBoolean waitingForEvt = new AtomicBoolean();
+
+        CountDownLatch forceArchiveSegment = new CountDownLatch(1);
+
+        forceArchiveSegmentMs = 1000;
+
+        Ignite ignite = startGrid();
+
+        ignite.cluster().state(ACTIVE);
+
+        IgniteEvents evts = ignite.events();
+
+        evts.localListen(e -> {
+            if (waitingForEvt.get())
+                forceArchiveSegment.countDown();
+
+            return true;
+        }, EVT_WAL_SEGMENT_ARCHIVED);
+
+        putDummyRecords(ignite, 100);
+
+        waitingForEvt.set(true); // Flag for skipping regular log() and rollOver().
+
+        boolean recordedAfterSleep = forceArchiveSegment.await(forceArchiveSegmentMs + 1001, TimeUnit.MILLISECONDS);
+
+        stopGrid();
+
+        assertTrue(recordedAfterSleep);
+    }
+
+    /**
      * Tests time out based WAL segment archiving.
      *
      * @throws Exception if failure occurs.
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
index bcc8347..d4fcad6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
@@ -168,6 +168,7 @@
                               walArchivePath="abc" walFlushFrequency="00:00:12" walFsyncDelayNanos="13" walHistorySize="14"
                               walMode="Background" walRecordIteratorBufferSize="15" walSegments="16" walSegmentSize="17"
                               walPath="wal-store" writeThrottlingEnabled="true" walAutoArchiveAfterInactivity="00:00:18"
+                              walForceArchiveTimeout="00:00:19"
                               walPageCompression="Zstd">
         <dataRegionConfigurations>
             <dataRegionConfiguration emptyPagesPoolSize="1" evictionThreshold="2" initialSize="3" metricsEnabled="true"
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
index a98cad9..d0c2bda 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
@@ -344,6 +344,7 @@ namespace Apache.Ignite.Core.Tests
             Assert.AreEqual(17, ds.WalSegmentSize);
             Assert.AreEqual("wal-store", ds.WalPath);
             Assert.AreEqual(TimeSpan.FromSeconds(18), ds.WalAutoArchiveAfterInactivity);
+            Assert.AreEqual(TimeSpan.FromSeconds(19), ds.WalForceArchiveTimeout);
             Assert.IsTrue(ds.WriteThrottlingEnabled);
             Assert.AreEqual(DiskPageCompression.Zstd, ds.WalPageCompression);
 
@@ -1031,6 +1032,7 @@ namespace Apache.Ignite.Core.Tests
                     ConcurrencyLevel = 1,
                     PageSize = 5 * 1024,
                     WalAutoArchiveAfterInactivity = TimeSpan.FromSeconds(19),
+                    WalForceArchiveTimeout = TimeSpan.FromSeconds(20),
                     WalPageCompression = DiskPageCompression.Lz4,
                     WalPageCompressionLevel = 10,
                     DefaultDataRegionConfiguration = new DataRegionConfiguration
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
index e49b6c2..361f432 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
@@ -628,6 +628,8 @@ namespace Apache.Ignite.Core.Tests
             Assert.AreEqual(DataStorageConfiguration.DefaultConcurrencyLevel, cfg.ConcurrencyLevel);
             Assert.AreEqual(DataStorageConfiguration.DefaultWalAutoArchiveAfterInactivity,
                 cfg.WalAutoArchiveAfterInactivity);
+            Assert.AreEqual(DataStorageConfiguration.DefaultWalAutoArchiveAfterInactivity,
+                cfg.WalForceArchiveTimeout);
         }
 
         /// <summary>
@@ -887,6 +889,7 @@ namespace Apache.Ignite.Core.Tests
                     ConcurrencyLevel = 1,
                     PageSize = 8 * 1024,
                     WalAutoArchiveAfterInactivity = TimeSpan.FromMinutes(5),
+                    WalForceArchiveTimeout = TimeSpan.FromMinutes(6),
                     CheckpointReadLockTimeout = TimeSpan.FromSeconds(9.5),
                     DefaultDataRegionConfiguration = new DataRegionConfiguration
                     {
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs
index 2e2fc72..671fd29 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Configuration/DataStorageConfiguration.cs
@@ -200,6 +200,7 @@ namespace Apache.Ignite.Core.Configuration
             SystemRegionMaxSize = DefaultSystemRegionMaxSize;
             PageSize = DefaultPageSize;
             WalAutoArchiveAfterInactivity = DefaultWalAutoArchiveAfterInactivity;
+            WalForceArchiveTimeout = DefaultWalAutoArchiveAfterInactivity;
             MaxWalArchiveSize = DefaultMaxWalArchiveSize;
             WalPageCompression = DefaultWalPageCompression;
             ConcurrencyLevel = DefaultConcurrencyLevel;
@@ -241,6 +242,7 @@ namespace Apache.Ignite.Core.Configuration
             PageSize = reader.ReadInt();
             ConcurrencyLevel = reader.ReadInt();
             WalAutoArchiveAfterInactivity = reader.ReadLongAsTimespan();
+            WalForceArchiveTimeout = reader.ReadLongAsTimespan();
             CheckpointReadLockTimeout = reader.ReadTimeSpanNullable();
             WalPageCompression = (DiskPageCompression)reader.ReadInt();
             WalPageCompressionLevel = reader.ReadIntNullable();
@@ -296,6 +298,7 @@ namespace Apache.Ignite.Core.Configuration
             writer.WriteInt(PageSize);
             writer.WriteInt(ConcurrencyLevel);
             writer.WriteTimeSpanAsLong(WalAutoArchiveAfterInactivity);
+            writer.WriteTimeSpanAsLong(WalForceArchiveTimeout);
             writer.WriteTimeSpanAsLongNullable(CheckpointReadLockTimeout);
             writer.WriteInt((int)WalPageCompression);
             writer.WriteIntNullable(WalPageCompressionLevel);
@@ -501,6 +504,12 @@ namespace Apache.Ignite.Core.Configuration
         public TimeSpan WalAutoArchiveAfterInactivity { get; set; }
 
         /// <summary>
+        /// Gets or sets the time for running auto archiving for incompletely WAL segment.
+        /// </summary>
+        [DefaultValue(typeof(TimeSpan), "-00:00:00.001")]
+        public TimeSpan WalForceArchiveTimeout { get; set; }
+
+        /// <summary>
         /// Gets or sets the timeout for checkpoint read lock acquisition.
         /// </summary>
         public TimeSpan? CheckpointReadLockTimeout { get; set; }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
index 0b35ca4..7d80e4e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd
@@ -2053,6 +2053,11 @@
                                 <xs:documentation>Inactivity time after which to run WAL segment auto archiving.</xs:documentation>
                             </xs:annotation>
                         </xs:attribute>
+                        <xs:attribute name="walForceArchiveTimeout" type="xs:string">
+                            <xs:annotation>
+                                <xs:documentation>Time interval (in milliseconds) for force archiving of incompletely WAL segment.</xs:documentation>
+                            </xs:annotation>
+                        </xs:attribute>
                         <xs:attribute name="checkpointReadLockTimeout" type="xs:string">
                             <xs:annotation>
                                 <xs:documentation>