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

sergeychugunov 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 730ec3c  IGNITE-16163 Additional test for B+Tree corruption cases - 
Fixes #9672.
730ec3c is described below

commit 730ec3c7e24d4ed1138e771f0127cf0a70b84b17
Author: Kirill Tkalenko <tkalkir...@yandex.ru>
AuthorDate: Tue Dec 21 10:17:12 2021 +0300

    IGNITE-16163 Additional test for B+Tree corruption cases - Fixes #9672.
    
    Signed-off-by: Sergey Chugunov <sergey.chugu...@gmail.com>
---
 .../database/BPlusTreeReplaceRemoveRaceTest.java   | 144 ++++++++++++++-------
 .../apache/ignite/testframework/GridTestUtils.java |  24 ++++
 2 files changed, 119 insertions(+), 49 deletions(-)

diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReplaceRemoveRaceTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReplaceRemoveRaceTest.java
index ecfc093..8a4c59c 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReplaceRemoveRaceTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReplaceRemoveRaceTest.java
@@ -18,12 +18,10 @@
 package org.apache.ignite.internal.processors.database;
 
 import java.io.Externalizable;
-import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.IgniteException;
 import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.failure.FailureContext;
 import org.apache.ignite.internal.IgniteInternalFuture;
@@ -41,15 +39,14 @@ import 
org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusInne
 import 
org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusLeafIO;
 import 
org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
-import 
org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
 import org.apache.ignite.internal.processors.failure.FailureProcessor;
 import org.apache.ignite.internal.util.typedef.T2;
-import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.GridTestKernalContext;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
 
 import static org.apache.ignite.internal.util.IgniteUtils.MB;
+import static org.apache.ignite.testframework.GridTestUtils.runAsync;
 
 /**
  * Test is based on {@link BPlusTreeSelfTest} and has a partial copy of its 
code.
@@ -147,16 +144,14 @@ public class BPlusTreeReplaceRemoveRaceTest extends 
GridCommonAbstractTest {
      */
     protected static class TestPairTree extends BPlusTree<Pair, Pair> {
         /**
-         * @param reuseList Reuse list.
-         * @param canGetRow Can get row from inner page.
+         * Constructor.
+         *
          * @param cacheId Cache ID.
          * @param pageMem Page memory.
          * @param metaPageId Meta page ID.
          * @throws IgniteCheckedException If failed.
          */
         public TestPairTree(
-            ReuseList reuseList,
-            boolean canGetRow,
             int cacheId,
             PageMemory pageMem,
             long metaPageId,
@@ -170,7 +165,7 @@ public class BPlusTreeReplaceRemoveRaceTest extends 
GridCommonAbstractTest {
                 null,
                 new AtomicLong(),
                 metaPageId,
-                reuseList,
+                null,
                 new IOVersions<>(new TestPairInnerIO()),
                 new IOVersions<>(new TestPairLeafIO()),
                 PageIdAllocator.FLAG_IDX,
@@ -347,60 +342,31 @@ public class BPlusTreeReplaceRemoveRaceTest extends 
GridCommonAbstractTest {
      *
      * Several iterations are required for this, given that there's no 
guaranteed way to force a tree to perform page
      * modifications in the desired order. Typically, less than {@code 10} 
attempts have been required to get a
-     * corrupted tree. Value {@code 50} is arbitrary and has been chosen to be 
big enough for test to fail in case of
+     * corrupted tree. Value {@code 100} is arbitrary and has been chosen to 
be big enough for test to fail in case of
      * regression, but not too big so that test won't run for too long.
      *
      * @throws Exception If failed.
      */
     @Test
     public void testConcurrentPutRemove() throws Exception {
-        for (int i = 0; i < 50; i++) {
-            TestPairTree tree = new TestPairTree(
-                null,
-                true,
-                CACHE_ID,
-                pageMem,
-                allocateMetaPage().pageId(),
-                lockTrackerManager
-            );
-
-            tree.putx(new Pair(1, 0));
-            tree.putx(new Pair(2, 0));
-            tree.putx(new Pair(4, 0));
-            tree.putx(new Pair(6, 0));
-            tree.putx(new Pair(7, 0));
-
-            // Split root.
-            tree.putx(new Pair(5, 0));
-
-            // Split its left subtree.
-            tree.putx(new Pair(3, 0));
+        for (int i = 0; i < 100; i++) {
+            TestPairTree tree = prepareBPlusTree();
 
             // Exact tree from the description is constructed at this point.
             CyclicBarrier barrier = new CyclicBarrier(2);
 
             // This is the replace operation.
-            IgniteInternalFuture<?> putFut = GridTestUtils.runAsync(() -> {
-                try {
-                    barrier.await();
-
-                    tree.putx(new Pair(4, 999));
-                }
-                catch (IgniteCheckedException | BrokenBarrierException | 
InterruptedException e) {
-                    throw new IgniteException(e);
-                }
+            IgniteInternalFuture<?> putFut = runAsync(() -> {
+                barrier.await();
+
+                tree.putx(new Pair(4, 999));
             });
 
-            // This is the remove opertation.
-            IgniteInternalFuture<?> remFut = GridTestUtils.runAsync(() -> {
-                try {
-                    barrier.await();
+            // This is the remove operation.
+            IgniteInternalFuture<?> remFut = runAsync(() -> {
+                barrier.await();
 
-                    tree.removex(new Pair(5, -1));
-                }
-                catch (IgniteCheckedException | BrokenBarrierException | 
InterruptedException e) {
-                    throw new IgniteException(e);
-                }
+                tree.removex(new Pair(5, -1));
             });
 
             // Wait for both operations.
@@ -421,4 +387,84 @@ public class BPlusTreeReplaceRemoveRaceTest extends 
GridCommonAbstractTest {
             assertEquals(999, pair.getValue().intValue());
         }
     }
+
+    /**
+     * Checks that there will be no corrupted B+tree during concurrent update 
and deletion
+     * of the same key that is contained in the inner and leaf nodes of the 
B+tree.
+     *
+     * NOTE: Test logic is the same as of {@link #testConcurrentPutRemove},
+     * the only difference is that it operates (puts and removes) on a single 
key.
+     * 
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testConcurrentPutRemoveSameRow() throws Exception {
+        for (int i = 0; i < 100; i++) {
+            TestPairTree tree = prepareBPlusTree();
+
+            // Exact tree from the description is constructed at this point.
+            CyclicBarrier barrier = new CyclicBarrier(2);
+
+            // This is the replace operation.
+            IgniteInternalFuture<?> putFut = runAsync(() -> {
+                barrier.await();
+
+                tree.putx(new Pair(5, 999));
+            });
+
+            // This is the remove operation.
+            IgniteInternalFuture<?> remFut = runAsync(() -> {
+                barrier.await();
+
+                tree.removex(new Pair(5, 0));
+            });
+
+            // Wait for both operations.
+            try {
+                putFut.get(1, TimeUnit.SECONDS);
+            }
+            finally {
+                remFut.get(1, TimeUnit.SECONDS);
+            }
+
+            // Just in case.
+            tree.validateTree();
+        }
+    }
+
+    /**
+     * Creates and fills a tree:
+     * <pre><code>
+     *                                    [ 5:0 ]
+     *                                /            \
+     *                 [ 2:0 | 4:0 ]                  [ 6:0 ]
+     *               /       |       \              /      |
+     * [ 1:0 | 2:0 ]->[ 3:0 | 4:0 ]->[ 5:0 ]->[ 6:0 ]->[ 7:0 ]
+     * </code></pre>
+     *
+     * @return New B+tree.
+     * @throws Exception If failed.
+     */
+    private TestPairTree prepareBPlusTree() throws Exception {
+        TestPairTree tree = new TestPairTree(
+            CACHE_ID,
+            pageMem,
+            allocateMetaPage().pageId(),
+            lockTrackerManager
+        );
+
+        tree.putx(new Pair(1, 0));
+        tree.putx(new Pair(2, 0));
+        tree.putx(new Pair(4, 0));
+        tree.putx(new Pair(6, 0));
+        tree.putx(new Pair(7, 0));
+
+        // Split root.
+        tree.putx(new Pair(5, 0));
+
+        // Split its left subtree.
+        tree.putx(new Pair(3, 0));
+
+        return tree;
+    }
 }
diff --git 
a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java 
b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
index 3bc9a0d..8fd45e7 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
@@ -1130,6 +1130,16 @@ public final class GridTestUtils {
      * @param task Runnable.
      * @return Future with task result.
      */
+    public static IgniteInternalFuture runAsync(final RunnableX task) {
+        return runAsync(task, "async-runnable-runner");
+    }
+
+    /**
+     * Runs runnable task asyncronously.
+     *
+     * @param task Runnable.
+     * @return Future with task result.
+     */
     public static IgniteInternalFuture runAsync(final Runnable task, String 
threadName) {
         return runAsync(() -> {
             task.run();
@@ -1139,6 +1149,20 @@ public final class GridTestUtils {
     }
 
     /**
+     * Runs runnable task asyncronously.
+     *
+     * @param task Runnable.
+     * @return Future with task result.
+     */
+    public static IgniteInternalFuture runAsync(final RunnableX task, String 
threadName) {
+        return runAsync(() -> {
+            task.run();
+
+            return null;
+        }, threadName);
+    }
+
+    /**
      * Runs callable task asyncronously.
      *
      * @param task Callable.

Reply via email to