You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dp...@apache.org on 2018/04/06 16:25:40 UTC

ignite git commit: IGNITE-8114 Add fail recovery mechanism to tracking pages - Fixes #3734.

Repository: ignite
Updated Branches:
  refs/heads/master e491f109c -> 0829397f8


IGNITE-8114 Add fail recovery mechanism to tracking pages - Fixes #3734.

Signed-off-by: dpavlov <dp...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/0829397f
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/0829397f
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/0829397f

Branch: refs/heads/master
Commit: 0829397f825493f778d4b62bf09069fbeefa34cf
Parents: e491f10
Author: Eduard Shangareev <es...@gridgain.com>
Authored: Fri Apr 6 19:22:07 2018 +0300
Committer: dpavlov <dp...@apache.org>
Committed: Fri Apr 6 19:22:07 2018 +0300

----------------------------------------------------------------------
 .../TrackingPageIsCorruptedException.java       |  60 +++++++
 .../cache/persistence/tree/io/PageMetaIO.java   |   6 +-
 .../persistence/tree/io/TrackingPageIO.java     | 156 +++++++++++++++----
 .../persistence/tree/io/TrackingPageIOTest.java | 116 +++++++++++---
 4 files changed, 289 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/0829397f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/TrackingPageIsCorruptedException.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/TrackingPageIsCorruptedException.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/TrackingPageIsCorruptedException.java
new file mode 100644
index 0000000..2b82bff
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/TrackingPageIsCorruptedException.java
@@ -0,0 +1,60 @@
+/*
+ * 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
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.ignite.internal.processors.cache.persistence.snapshot;
+
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ * Thrown when corrupted tracking page was queried.
+ */
+public class TrackingPageIsCorruptedException extends IgniteCheckedException {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Instance. */
+    public static final TrackingPageIsCorruptedException INSTANCE = new TrackingPageIsCorruptedException(-1, -1);
+
+    /** Last tag. */
+    private final long lastTag;
+
+    /** Passed tag. */
+    private final long passedTag;
+
+    /**
+     * @param lastTag Last tag.
+     * @param passedTag Passed tag.
+     */
+    public TrackingPageIsCorruptedException(long lastTag, long passedTag) {
+        this.lastTag = lastTag;
+        this.passedTag = passedTag;
+    }
+
+    /**
+     * @return Last tag.
+     */
+    public long lastTag() {
+        return lastTag;
+    }
+
+    /**
+     * @return Passed tag.
+     */
+    public long passedTag() {
+        return passedTag;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0829397f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageMetaIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageMetaIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageMetaIO.java
index d2921ee..d676cfd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageMetaIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageMetaIO.java
@@ -152,10 +152,10 @@ public class PageMetaIO extends PageIO {
 
     /**
      * @param pageAddr Page address.
-     * @param nextSnapshotId Next snapshot id.
+     * @param nextSnapshotTag Next snapshot tag.
      */
-    public void setNextSnapshotTag(long pageAddr, long nextSnapshotId) {
-        PageUtils.putLong(pageAddr, NEXT_SNAPSHOT_TAG_OFF, nextSnapshotId);
+    public void setNextSnapshotTag(long pageAddr, long nextSnapshotTag) {
+        PageUtils.putLong(pageAddr, NEXT_SNAPSHOT_TAG_OFF, nextSnapshotTag);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/0829397f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIO.java
index 1bd70f8..94885e4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIO.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.cache.persistence.tree.io;
 import java.nio.ByteBuffer;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.processors.cache.persistence.snapshot.TrackingPageIsCorruptedException;
 import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
 import org.apache.ignite.internal.util.GridStringBuilder;
 import org.apache.ignite.internal.util.GridUnsafe;
@@ -50,6 +51,12 @@ public class TrackingPageIO extends PageIO {
         new TrackingPageIO(1)
     );
 
+    /** Corrupt flag mask. */
+    public static final long CORRUPT_FLAG_MASK = 1L << 63;
+
+    /** Corrupt flag mask. */
+    public static final long CORRUPT_FLAG_FILTER_MASK = ~CORRUPT_FLAG_MASK;
+
     /** Last snapshot offset. */
     public static final int LAST_SNAPSHOT_TAG_OFFSET = COMMON_HEADER_END;
 
@@ -77,11 +84,11 @@ public class TrackingPageIO extends PageIO {
      *
      * @param buf Buffer.
      * @param pageId Page id.
-     * @param nextSnapshotTag tag of next snapshot.
+     * @param nextSnapshotTag Tag of next snapshot.
      * @param pageSize Page size.
      */
-    public boolean markChanged(ByteBuffer buf, long pageId, long nextSnapshotTag, long lastSuccessfulSnapshotTag, int pageSize) {
-        validateSnapshotId(buf, nextSnapshotTag, lastSuccessfulSnapshotTag, pageSize);
+    public void markChanged(ByteBuffer buf, long pageId, long nextSnapshotTag, long lastSuccessfulSnapshotTag, int pageSize) {
+        validateSnapshotTag(buf, nextSnapshotTag, lastSuccessfulSnapshotTag, pageSize);
 
         int cntOfPage = countOfPageToTrack(pageSize);
 
@@ -98,7 +105,7 @@ public class TrackingPageIO extends PageIO {
         byte newVal =  (byte) (byteToUpdate | updateTemplate);
 
         if (byteToUpdate == newVal)
-            return false;
+            return;
 
         buf.put(idx, newVal);
 
@@ -107,8 +114,6 @@ public class TrackingPageIO extends PageIO {
         buf.putShort(sizeOff, newSize);
 
         assert newSize == countOfChangedPage(buf, nextSnapshotTag, pageSize);
-
-        return true;
     }
 
     /**
@@ -116,22 +121,30 @@ public class TrackingPageIO extends PageIO {
      * @param nextSnapshotTag Next snapshot id.
      * @param lastSuccessfulSnapshotTag Last successful snapshot id.
      * @param pageSize Page size.
+     *
+     * @return <code>-1</code> if everything is ok, otherwise last saved tag.
      */
-    private void validateSnapshotId(ByteBuffer buf, long nextSnapshotTag, long lastSuccessfulSnapshotTag, int pageSize) {
+    private long validateSnapshotTag(ByteBuffer buf, long nextSnapshotTag, long lastSuccessfulSnapshotTag, int pageSize) {
         assert nextSnapshotTag != lastSuccessfulSnapshotTag : "nextSnapshotTag = " + nextSnapshotTag +
             ", lastSuccessfulSnapshotId = " + lastSuccessfulSnapshotTag;
 
         long last = getLastSnapshotTag(buf);
 
-        assert last <= nextSnapshotTag : "last = " + last + ", nextSnapshotTag = " + nextSnapshotTag;
+        if(last > nextSnapshotTag) { //we have lost snapshot tag therefore should mark this tracking as corrupted
+            PageHandler.zeroMemory(buf, LAST_SNAPSHOT_TAG_OFFSET, buf.capacity() - LAST_SNAPSHOT_TAG_OFFSET);
+
+            setLastSnasphotTag(buf, nextSnapshotTag | CORRUPT_FLAG_MASK);
+
+            return last;
+        }
 
         if (nextSnapshotTag == last) //everything is ok
-            return;
+            return -1;
 
         int cntOfPage = countOfPageToTrack(pageSize);
 
         if (last <= lastSuccessfulSnapshotTag) { //we can drop our data
-            buf.putLong(LAST_SNAPSHOT_TAG_OFFSET, nextSnapshotTag);
+            setLastSnasphotTag(buf, nextSnapshotTag);
 
             PageHandler.zeroMemory(buf, SIZE_FIELD_OFFSET, buf.capacity() - SIZE_FIELD_OFFSET);
         } else { //we can't drop data, it is still necessary for incremental snapshots
@@ -167,37 +180,119 @@ public class TrackingPageIO extends PageIO {
                 buf.putShort(sizeOff2, (short)newSize);
             }
 
-            buf.putLong(LAST_SNAPSHOT_TAG_OFFSET, nextSnapshotTag);
+            setLastSnasphotTag(buf, nextSnapshotTag);
 
             PageHandler.zeroMemory(buf, sizeOff, len + SIZE_FIELD_SIZE);
         }
+
+        return -1;
+    }
+
+    /**
+     * @param buf Buffer.
+     * @param nextSnapshotTag Next snapshot tag.
+     */
+    private void setLastSnasphotTag(ByteBuffer buf, long nextSnapshotTag) {
+        if (isCorrupted(buf))
+            nextSnapshotTag = nextSnapshotTag | CORRUPT_FLAG_MASK;
+
+        buf.putLong(LAST_SNAPSHOT_TAG_OFFSET, nextSnapshotTag);
     }
 
     /**
      * @param buf Buffer.
+     * @param nextSnapshotTag Next snapshot tag.
      */
-    long getLastSnapshotTag(ByteBuffer buf) {
-        return buf.getLong(LAST_SNAPSHOT_TAG_OFFSET);
+    private void setLastSnasphotTag0(ByteBuffer buf, long nextSnapshotTag) {
+        buf.putLong(LAST_SNAPSHOT_TAG_OFFSET, nextSnapshotTag);
     }
 
     /**
-     * @param addr Address.
+     * @param addr address.
+     * @param nextSnapshotTag Next snapshot tag.
      */
-    long getLastSnapshotTag(long addr) {
+    private void setLastSnasphotTag0(long addr, long nextSnapshotTag) {
+        GridUnsafe.putLong(addr + LAST_SNAPSHOT_TAG_OFFSET, nextSnapshotTag);
+    }
+
+    /**
+     * @param buf Buffer.
+     * @return Saved max seen snapshot tag.
+     */
+    private long getLastSnapshotTag(ByteBuffer buf) {
+        return getLastSnapshotTag0(buf) & CORRUPT_FLAG_FILTER_MASK;
+    }
+
+    /**
+     * @param buf Buffer.
+     * @return Saved value in {@link TrackingPageIO#LAST_SNAPSHOT_TAG_OFFSET}.
+     */
+    private long getLastSnapshotTag0(ByteBuffer buf) {
+        return buf.getLong(LAST_SNAPSHOT_TAG_OFFSET) ;
+    }
+
+    /**
+     * @param addr Address of buffer start.
+     * @return Saved max seen snapshot tag.
+     */
+    private long getLastSnapshotTag(long addr) {
+        return getLastSnapshotTag0(addr) & CORRUPT_FLAG_FILTER_MASK;
+    }
+
+    /**
+     * @param addr Address of buffer start.
+     * @return Saved value in {@link TrackingPageIO#LAST_SNAPSHOT_TAG_OFFSET}.
+     */
+    private long getLastSnapshotTag0(long addr) {
         return GridUnsafe.getLong(addr + LAST_SNAPSHOT_TAG_OFFSET);
     }
 
     /**
-     * Check that pageId was marked as changed between previous snapshot finish and current snapshot start.
+     * @param buf Buffer.
+     * @return Was tracking page marked as corrupted or not.
+     */
+    public boolean isCorrupted(ByteBuffer buf) {
+        return getLastSnapshotTag0(buf) < 0; //if true it means that first bit set to 1
+    }
+
+    /**
+     * Reset corrupted flag to false.
+     *
+     * @param buf Buffer.
+     */
+    public void resetCorruptFlag(ByteBuffer buf) {
+        setLastSnasphotTag0(buf, getLastSnapshotTag(buf));
+    }
+
+    /**
+     * Reset corrupted flag to false.
+     *
+     * @param addr Buffer.
+     */
+    public void resetCorruptFlag(long addr) {
+        setLastSnasphotTag0(addr, getLastSnapshotTag(addr));
+    }
+
+    /**
      *
      * @param buf Buffer.
      * @param pageId Page id.
      * @param curSnapshotTag Snapshot tag.
      * @param lastSuccessfulSnapshotTag Last successful snapshot id.
      * @param pageSize Page size.
+     *
+     * @return Was that pageId marked as changed between previous snapshot finish and current snapshot start or not.
+     * @throws TrackingPageIsCorruptedException if this tracking page was marked as corrupted.
      */
-    public boolean wasChanged(ByteBuffer buf, long pageId, long curSnapshotTag, long lastSuccessfulSnapshotTag, int pageSize) {
-        validateSnapshotId(buf, curSnapshotTag + 1, lastSuccessfulSnapshotTag, pageSize);
+    public boolean wasChanged(ByteBuffer buf, long pageId, long curSnapshotTag, long lastSuccessfulSnapshotTag, int pageSize)
+        throws TrackingPageIsCorruptedException {
+        if (isCorrupted(buf))
+            throw TrackingPageIsCorruptedException.INSTANCE;
+
+        long lastTag = validateSnapshotTag(buf, curSnapshotTag + 1, lastSuccessfulSnapshotTag, pageSize);
+
+        if (lastTag >= 0)
+            throw new TrackingPageIsCorruptedException(lastTag, curSnapshotTag);
 
         if (countOfChangedPage(buf, curSnapshotTag, pageSize) < 1)
             return false;
@@ -223,7 +318,7 @@ public class TrackingPageIO extends PageIO {
      * @param snapshotTag Snapshot tag.
      * @param pageSize Page size.
      *
-     * @return count of pages which were marked as change for given snapshotTag
+     * @return Count of pages which were marked as change for given snapshotTag.
      */
     public short countOfChangedPage(ByteBuffer buf, long snapshotTag, int pageSize) {
         long dif = getLastSnapshotTag(buf) - snapshotTag;
@@ -240,16 +335,16 @@ public class TrackingPageIO extends PageIO {
     /**
      * @param snapshotTag Snapshot id.
      *
-     * @return true if snapshotTag is odd, otherwise - false
+     * @return true if snapshotTag is odd, otherwise - false.
      */
-    boolean useLeftHalf(long snapshotTag) {
+    private boolean useLeftHalf(long snapshotTag) {
         return (snapshotTag & 0b1) == 0;
     }
 
     /**
      * @param pageId Page id.
      * @param pageSize Page size.
-     * @return pageId of tracking page which set pageId belongs to
+     * @return Page id of tracking page which set pageId belongs to.
      */
     public long trackingPageFor(long pageId, int pageSize) {
         assert PageIdUtils.pageIndex(pageId) > 0;
@@ -267,7 +362,7 @@ public class TrackingPageIO extends PageIO {
     /**
      * @param pageSize Page size.
      *
-     * @return how many page we can track with 1 page
+     * @return How many page we can track with 1 page.
      */
     public int countOfPageToTrack(int pageSize) {
         return ((pageSize - SIZE_FIELD_OFFSET) / 2 - SIZE_FIELD_SIZE)  << 3;
@@ -279,11 +374,18 @@ public class TrackingPageIO extends PageIO {
      * @param curSnapshotTag Snapshot id.
      * @param lastSuccessfulSnapshotTag  Last successful snapshot id.
      * @param pageSize Page size.
-     * @return set pageId if it was changed or next closest one, if there is no changed page {@code null} will be returned
+     * @return Passed pageId if it was changed or next closest one, if there is no changed page {@code null} will be returned.
+     * @throws TrackingPageIsCorruptedException if this tracking page was marked as corrupted.
      */
     @Nullable public Long findNextChangedPage(ByteBuffer buf, long start, long curSnapshotTag,
-        long lastSuccessfulSnapshotTag, int pageSize) {
-        validateSnapshotId(buf, curSnapshotTag + 1, lastSuccessfulSnapshotTag, pageSize);
+        long lastSuccessfulSnapshotTag, int pageSize) throws TrackingPageIsCorruptedException {
+        if (isCorrupted(buf))
+            throw TrackingPageIsCorruptedException.INSTANCE;
+
+        long lastTag = validateSnapshotTag(buf, curSnapshotTag + 1, lastSuccessfulSnapshotTag, pageSize);
+
+        if (lastTag >= 0)
+            throw new TrackingPageIsCorruptedException(lastTag, curSnapshotTag);
 
         int cntOfPage = countOfPageToTrack(pageSize);
 
@@ -332,6 +434,8 @@ public class TrackingPageIO extends PageIO {
     /**
      * @param byteToTest Byte to test.
      * @param firstBitToTest First bit to test.
+     *
+     * @return Index of bit which is set to 1, if there is no such index then <code>-1</code>.
      */
     private static int foundSetBit(byte byteToTest, int firstBitToTest) {
         assert firstBitToTest < 8;

http://git-wip-us.apache.org/repos/asf/ignite/blob/0829397f/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIOTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIOTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIOTest.java
index b50f026..cacea48 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIOTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/TrackingPageIOTest.java
@@ -26,14 +26,19 @@ import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.ThreadLocalRandom;
 import junit.framework.TestCase;
+import org.apache.ignite.internal.pagemem.PageIdAllocator;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.processors.cache.persistence.snapshot.TrackingPageIsCorruptedException;
 import org.apache.ignite.internal.util.GridUnsafe;
+import org.jetbrains.annotations.NotNull;
 
 /**
  *
  */
 public class TrackingPageIOTest extends TestCase {
     /** Page size. */
-    public static final int PAGE_SIZE = 2048;
+    public static final int PAGE_SIZE = 4096;
+
 
     /** */
     private final TrackingPageIO io = TrackingPageIO.VERSIONS.latest();
@@ -41,9 +46,8 @@ public class TrackingPageIOTest extends TestCase {
     /**
      *
      */
-    public void testBasics() {
-        ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE);
-        buf.order(ByteOrder.nativeOrder());
+    public void testBasics() throws Exception {
+        ByteBuffer buf = createBuffer();
 
         io.markChanged(buf, 2, 0, -1, PAGE_SIZE);
 
@@ -55,13 +59,21 @@ public class TrackingPageIOTest extends TestCase {
     }
 
     /**
-     *
+     * @return byte buffer with right order.
      */
-    public void testMarkingRandomly() {
+    @NotNull private ByteBuffer createBuffer() {
         ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE);
+
         buf.order(ByteOrder.nativeOrder());
 
-        int cntOfPageToTrack = io.countOfPageToTrack(PAGE_SIZE);
+        return buf;
+    }
+
+    /**
+     *
+     */
+    public void testMarkingRandomly() throws Exception {
+        ByteBuffer buf = createBuffer();
 
         for (int i = 0; i < 1001; i++)
             checkMarkingRandomly(buf, i, false);
@@ -70,9 +82,8 @@ public class TrackingPageIOTest extends TestCase {
     /**
      *
      */
-    public void testZeroingRandomly() {
-        ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE);
-        buf.order(ByteOrder.nativeOrder());
+    public void testZeroingRandomly() throws Exception {
+        ByteBuffer buf = createBuffer();
 
         for (int i = 0; i < 1001; i++)
             checkMarkingRandomly(buf, i, true);
@@ -82,7 +93,7 @@ public class TrackingPageIOTest extends TestCase {
      * @param buf Buffer.
      * @param backupId Backup id.
      */
-    private void checkMarkingRandomly(ByteBuffer buf, int backupId, boolean testZeroing) {
+    private void checkMarkingRandomly(ByteBuffer buf, int backupId, boolean testZeroing) throws Exception {
         ThreadLocalRandom rand = ThreadLocalRandom.current();
 
         int track = io.countOfPageToTrack(PAGE_SIZE);
@@ -132,8 +143,7 @@ public class TrackingPageIOTest extends TestCase {
      * @throws Exception If failed.
      */
     public void testFindNextChangedPage() throws Exception {
-        ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE);
-        buf.order(ByteOrder.nativeOrder());
+        ByteBuffer buf = createBuffer();
 
         for (int i = 0; i < 101; i++)
             checkFindingRandomly(buf, i);
@@ -143,7 +153,7 @@ public class TrackingPageIOTest extends TestCase {
      * @param buf Buffer.
      * @param backupId Backup id.
      */
-    private void checkFindingRandomly(ByteBuffer buf, int backupId) {
+    private void checkFindingRandomly(ByteBuffer buf, int backupId) throws Exception {
         ThreadLocalRandom rand = ThreadLocalRandom.current();
 
         int track = io.countOfPageToTrack(PAGE_SIZE);
@@ -187,9 +197,8 @@ public class TrackingPageIOTest extends TestCase {
     /**
      *
      */
-    public void testMerging() {
-        ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE);
-        buf.order(ByteOrder.nativeOrder());
+    public void testMerging() throws Exception {
+        ByteBuffer buf = createBuffer();
 
         ThreadLocalRandom rand = ThreadLocalRandom.current();
 
@@ -226,9 +235,8 @@ public class TrackingPageIOTest extends TestCase {
     /**
      *
      */
-    public void testMerging_MarksShouldBeDropForSuccessfulBackup() {
-        ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE);
-        buf.order(ByteOrder.nativeOrder());
+    public void testMerging_MarksShouldBeDropForSuccessfulBackup() throws Exception {
+        ByteBuffer buf = createBuffer();
 
         ThreadLocalRandom rand = ThreadLocalRandom.current();
 
@@ -260,6 +268,15 @@ public class TrackingPageIOTest extends TestCase {
             assertEquals("pageId = " + i, setIdx2.contains(i), io.wasChanged(buf, i, 5, 4, PAGE_SIZE));
     }
 
+    /**
+     * @param buf Buffer.
+     * @param track Track.
+     * @param basePageId Base page id.
+     * @param maxPageId Max page id.
+     * @param setIdx Set index.
+     * @param backupId Backup id.
+     * @param successfulBackupId Successful backup id.
+     */
     private void generateMarking(
         ByteBuffer buf,
         int track,
@@ -280,4 +297,63 @@ public class TrackingPageIOTest extends TestCase {
             }
         }
     }
+
+    /**
+     * We should handle case when we lost snapshot tag and now it's lower than saved.
+     *
+     * @throws Exception if failed.
+     */
+    public void testThatWeDontFailIfSnapshotTagWasLost() throws Exception {
+        ByteBuffer buf = createBuffer();
+
+        long basePageId = PageIdUtils.pageId(0, PageIdAllocator.FLAG_IDX, 1);
+
+        assert basePageId >= 0;
+
+        PageIO.setPageId(GridUnsafe.bufferAddress(buf), basePageId);
+
+        int oldTag = 10;
+
+        io.markChanged(buf, basePageId + 1, oldTag, oldTag - 1, PAGE_SIZE);
+
+        for (int i = 1; i < 100; i++)
+            io.markChanged(buf, basePageId + i, oldTag - 1, oldTag - 2, PAGE_SIZE);
+
+        assertTrue(io.isCorrupted(buf));
+
+        for (int i = 1; i < 100; i++) {
+            try {
+                long id = basePageId + i;
+
+                io.wasChanged(buf, id, oldTag - 1, oldTag - 2, PAGE_SIZE);
+
+                fail();
+            }
+            catch (TrackingPageIsCorruptedException e) {
+                //ignore
+            }
+        }
+
+        for (int i = 1; i < 100; i++) {
+            long id = basePageId + i + 1000;
+
+            io.markChanged(buf, id, oldTag, oldTag - 2, PAGE_SIZE);
+        }
+
+        io.resetCorruptFlag(buf);
+
+        assertFalse(io.isCorrupted(buf));
+
+        for (int i = 1; i < 100; i++) {
+            long id = basePageId + i + 1000;
+
+            assertTrue(io.wasChanged(buf, id, oldTag, oldTag - 1, PAGE_SIZE));
+        }
+
+        for (int i = 1; i < 100; i++) {
+            long id = basePageId + i;
+
+            assertFalse(io.wasChanged(buf, id, oldTag, oldTag - 1, PAGE_SIZE));
+        }
+    }
 }
\ No newline at end of file