You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by tk...@apache.org on 2022/12/12 11:01:01 UTC

[ignite] branch master updated: IGNITE-18364 Forcibly released the reservation of the WAL segments required by the checkpoint (#10429)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 234916967ee IGNITE-18364 Forcibly released the reservation of the WAL segments required by the checkpoint (#10429)
234916967ee is described below

commit 234916967ee6cdd4866f1800664ce675593ad627
Author: Kirill Tkalenko <tk...@yandex.ru>
AuthorDate: Mon Dec 12 14:00:51 2022 +0300

    IGNITE-18364 Forcibly released the reservation of the WAL segments required by the checkpoint (#10429)
---
 .../wal/aware/SegmentArchiveSizeStorage.java       |  20 +++-
 .../cache/persistence/wal/aware/SegmentAware.java  |   1 +
 .../db/wal/WalDeletionArchiveAbstractTest.java     | 106 +++++++++++++++++++++
 .../persistence/wal/aware/SegmentAwareTest.java    |  25 +++++
 4 files changed, 150 insertions(+), 2 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentArchiveSizeStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentArchiveSizeStorage.java
index cee0451cc54..eb1138dad58 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentArchiveSizeStorage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentArchiveSizeStorage.java
@@ -54,11 +54,14 @@ class SegmentArchiveSizeStorage {
     /** Automatically release segments. Guarded by {@code this}. */
     private boolean autoRelease;
 
+    /** Segment of last completed checkpoint. Guarded by {@code this}. */
+    private long lastCpIdx = -1;
+
     /**
      * Segment sizes. Mapping: segment idx -> size in bytes. Guarded by {@code this}.
      * {@code null} if {@link #walArchiveUnlimited} == {@code true}.
      */
-    @Nullable private final Map<Long, Long> segmentSizes;
+    @Nullable private final TreeMap<Long, Long> segmentSizes;
 
     /**
      * Segment reservations storage.
@@ -206,7 +209,7 @@ class SegmentArchiveSizeStorage {
      */
     void startAutoReleaseSegments() {
         if (!walArchiveUnlimited) {
-            T2<Long, Integer> forceReleaseSegments = null;
+            T2<Long, Integer> forceReleaseSegments;
 
             synchronized (this) {
                 autoRelease = true;
@@ -232,6 +235,9 @@ class SegmentArchiveSizeStorage {
             long size = 0;
 
             for (Map.Entry<Long, Long> e : segmentSizes.entrySet()) {
+                if (e.getKey() > lastCpIdx)
+                    break;
+
                 releaseIdx = e.getKey();
                 releaseCnt++;
 
@@ -260,4 +266,14 @@ class SegmentArchiveSizeStorage {
 
         reservationStorage.forceRelease(absIdx);
     }
+
+    /**
+     * Update segment of last completed checkpoint.
+     * Required for binary recovery.
+     *
+     * @param absIdx Absolut segment index.
+     */
+    synchronized void lastCheckpointIdx(long absIdx) {
+        lastCpIdx = absIdx;
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAware.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAware.java
index 728bd7411e5..cb1e7ed9422 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAware.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAware.java
@@ -370,6 +370,7 @@ public class SegmentAware {
      */
     public void lastCheckpointIdx(long absIdx) {
         truncateStorage.lastCheckpointIdx(absIdx);
+        archiveSizeStorage.lastCheckpointIdx(absIdx);
     }
 
     /**
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java
index 61911f83c55..f64435e0934 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/wal/WalDeletionArchiveAbstractTest.java
@@ -38,6 +38,8 @@ import org.apache.ignite.internal.processors.cache.persistence.checkpoint.Checkp
 import org.apache.ignite.internal.processors.cache.persistence.checkpoint.Checkpointer;
 import org.apache.ignite.internal.processors.cache.persistence.wal.FileDescriptor;
 import org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.persistence.wal.WALPointer;
+import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.testframework.ListeningTestLogger;
 import org.apache.ignite.testframework.junits.WithSystemProperty;
@@ -51,6 +53,10 @@ import static org.apache.ignite.internal.util.IgniteUtils.KB;
 import static org.apache.ignite.internal.util.IgniteUtils.MB;
 import static org.apache.ignite.testframework.GridTestUtils.getFieldValueHierarchy;
 import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThan;
 
 /**
  *
@@ -309,6 +315,96 @@ public abstract class WalDeletionArchiveAbstractTest extends GridCommonAbstractT
         assertEquals(logStrs.toString(), 1, logStrs.size());
     }
 
+    /**
+     * Check that if the maximum archive size is equal to one WAL segment size, then when the archive is overflowing,
+     * segments that are needed for binary recovery will not be deleted from the archive until the checkpoint occurs
+     * and finishes.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testMaxWalArchiveSizeEqualsOneWalSegmentSize() throws Exception {
+        int walSegmentSize = (int)MB;
+
+        IgniteConfiguration cfg = getConfiguration(getTestIgniteInstanceName(0))
+            .setCacheConfiguration(cacheConfiguration())
+            .setDataStorageConfiguration(
+                new DataStorageConfiguration()
+                    .setCheckpointFrequency(Long.MAX_VALUE)
+                    .setMaxWalArchiveSize(walSegmentSize)
+                    .setWalSegmentSize(walSegmentSize)
+                    .setWalSegments(2)
+                    .setDefaultDataRegionConfiguration(
+                        new DataRegionConfiguration()
+                            .setPersistenceEnabled(true)
+                            .setMaxSize(GB)
+                            .setCheckpointPageBufferSize(GB)
+                    )
+            );
+
+        IgniteEx n = startGrid(cfg);
+
+        n.cluster().state(ClusterState.ACTIVE);
+
+        // Let's not let a checkpoint happen.
+        gridDatabase(n).checkpointReadLock();
+
+        FileWriteAheadLogManager wal = wal(n);
+
+        try {
+            long idxLastCp = lastCheckpointPtr(n).index();
+
+            // Let's reserve the very first segment that will definitely be needed for the checkpoint.
+            assertTrue(wal.reserve(new WALPointer(idxLastCp, 0, 0)));
+
+            for (int i = 0; wal.lastArchivedSegment() < 20L; i++)
+                n.cache(DEFAULT_CACHE_NAME).put(i, new byte[(int)(512 * KB)]);
+
+            // Make sure nothing has been deleted from the archive.
+            assertThat(walArchiveSize(n), greaterThanOrEqualTo(20L * walSegmentSize));
+            assertThat(wal.lastTruncatedSegment(), equalTo(-1L));
+
+            // Let's try to reserve all the segments and then immediately release them.
+            long lastWalSegmentIndex = wal.lastWritePointer().index();
+
+            for (int i = (int)(idxLastCp + 1); i < lastWalSegmentIndex; i++) {
+                WALPointer pointer = new WALPointer(i, 0, 0);
+
+                // Should be able to reserve segments because the checkpoint has not happened yet.
+                assertTrue(String.valueOf(i), wal.reserve(pointer));
+
+                wal.release(pointer);
+            }
+
+            assertTrue(
+                String.valueOf(lastWalSegmentIndex),
+                wal.reserve(new WALPointer(lastWalSegmentIndex, 0, 0))
+            );
+
+            wal.release(new WALPointer(lastWalSegmentIndex, 0, 0));
+
+            // Let's wait a bit, suddenly there will be a deletion from the archive?
+            assertFalse(waitForCondition(() -> wal.lastTruncatedSegment() >= 0, 1_000, 100));
+
+            // Make sure nothing has been deleted from the archive.
+            assertThat(walArchiveSize(n), greaterThanOrEqualTo(20L * walSegmentSize));
+            assertThat(wal.lastTruncatedSegment(), equalTo(-1L));
+        }
+        finally {
+            gridDatabase(n).checkpointReadUnlock();
+        }
+
+        // Now let's run a checkpoint and make sure that only one segment remains in the archive.
+        forceCheckpoint(n);
+
+        assertTrue(
+            IgniteUtils.humanReadableByteCount(walArchiveSize(n)),
+            waitForCondition(() -> walArchiveSize(n) <= walSegmentSize, 1_000, 100)
+        );
+
+        assertThat(wal.lastTruncatedSegment(), lessThan(lastCheckpointPtr(n).index()));
+    }
+
     /**
      * Extract GridCacheDatabaseSharedManager.
      */
@@ -332,4 +428,14 @@ public abstract class WalDeletionArchiveAbstractTest extends GridCommonAbstractT
     private long walArchiveSize(Ignite n) {
         return Arrays.stream(wal(n).walArchiveFiles()).mapToLong(fd -> fd.file().length()).sum();
     }
+
+    /**
+     * Returns the value of field {@code FileWriteAheadLogManager#lastCheckpointPtr}.
+     *
+     * @param n Node.
+     * @return Field value {@code FileWriteAheadLogManager#lastCheckpointPtr}.
+     */
+    private WALPointer lastCheckpointPtr(Ignite n) {
+        return getFieldValueHierarchy(wal(n), "lastCheckpointPtr");
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAwareTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAwareTest.java
index d95fd88a0e0..e81b8a4a233 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAwareTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/aware/SegmentAwareTest.java
@@ -887,12 +887,21 @@ public class SegmentAwareTest {
         assertTrue(aware.reserve(1));
         assertTrue(aware.reserve(8));
 
+        aware.lastCheckpointIdx(1);
         aware.addSize(9, 10);
 
         assertFalse(aware.reserved(0));
         assertFalse(aware.reserved(1));
         assertTrue(aware.reserved(8));
 
+        assertEquals(1, reservationStorage.minReserveIdx());
+
+        aware.lastCheckpointIdx(5);
+        aware.startAutoReleaseSegments();
+
+        assertFalse(aware.reserved(0));
+        assertFalse(aware.reserved(1));
+        assertTrue(aware.reserved(8));
         assertEquals(5, reservationStorage.minReserveIdx());
 
         for (int i = 0; i <= 5; i++) {
@@ -958,6 +967,22 @@ public class SegmentAwareTest {
 
         aware.startAutoReleaseSegments();
 
+        assertTrue(aware.reserved(0));
+        assertTrue(aware.reserved(1));
+        assertTrue(aware.reserved(8));
+        assertEquals(-1, reservationStorage.minReserveIdx());
+
+        aware.lastCheckpointIdx(0);
+        aware.startAutoReleaseSegments();
+
+        assertFalse(aware.reserved(0));
+        assertTrue(aware.reserved(1));
+        assertTrue(aware.reserved(8));
+        assertEquals(0, reservationStorage.minReserveIdx());
+
+        aware.lastCheckpointIdx(100);
+        aware.startAutoReleaseSegments();
+
         assertFalse(aware.reserved(0));
         assertFalse(aware.reserved(1));
         assertTrue(aware.reserved(8));