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

sk0x50 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 63dc55d  IGNITE-14893 Fixed IndexOutOfBoundsException in 
GridCacheWriteBehindStore flushers lookup by key. Fixes #9177
63dc55d is described below

commit 63dc55d66997023f572c76fd94882019a200959e
Author: Ilya Korol <[email protected]>
AuthorDate: Mon Jul 5 12:18:59 2021 +0300

    IGNITE-14893 Fixed IndexOutOfBoundsException in GridCacheWriteBehindStore 
flushers lookup by key. Fixes #9177
    
    Signed-off-by: Slava Koptilin <[email protected]>
---
 .../cache/store/GridCacheWriteBehindStore.java     | 25 ++++++++-----
 .../apache/ignite/internal/util/IgniteUtils.java   | 12 +++++++
 .../store/GridCacheWriteBehindStoreSelfTest.java   | 42 ++++++++++++++++++++--
 .../ignite/internal/util/IgniteUtilsSelfTest.java  | 28 +++++++++++++++
 4 files changed, 97 insertions(+), 10 deletions(-)

diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStore.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStore.java
index d3da3a7..d9120ab 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStore.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStore.java
@@ -222,7 +222,7 @@ public class GridCacheWriteBehindStore<K, V> implements 
CacheStore<K, V>, Lifecy
      */
     public void setFlushThreadCount(int flushThreadCnt) {
         this.flushThreadCnt = flushThreadCnt;
-        this.flushThreadCntIsPowerOfTwo = (flushThreadCnt & (flushThreadCnt - 
1)) == 0;
+        this.flushThreadCntIsPowerOfTwo = U.isPow2(flushThreadCnt);
     }
 
     /**
@@ -660,20 +660,29 @@ public class GridCacheWriteBehindStore<K, V> implements 
CacheStore<K, V>, Lifecy
     }
 
     /**
-     * Return flusher by by key.
+     * Return flusher by key.
      *
      * @param key Key for search.
      * @return flusher.
      */
     private Flusher flusher(K key) {
-        int h, idx;
+        return flushThreads[resolveFlusherByKeyHash(key.hashCode())];
+    }
 
-        if (flushThreadCntIsPowerOfTwo)
-            idx = ((h = key.hashCode()) ^ (h >>> 16)) & (flushThreadCnt - 1);
-        else
-            idx = ((h = key.hashCode()) ^ (h >>> 16)) % flushThreadCnt;
+    /**
+     * Lookup flusher index by provided key hash using
+     * approach similar to {@link HashMap#hash(Object)}. In case
+     * <code>size</code> is not a power of 2 we fallback to modulo operation.
+     *
+     * @param hash Object hash.
+     * @return Calculated flucher index [0..flushThreadCnt).
+     */
+    int resolveFlusherByKeyHash(int hash) {
+        int h = (hash ^ (hash >>> 16));
 
-        return flushThreads[idx];
+        return flushThreadCntIsPowerOfTwo
+            ? h & (flushThreadCnt - 1)
+            : U.hashToIndex(h, flushThreadCnt);
     }
 
     /**
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java 
b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 069ca37..deb83061 100755
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -12233,4 +12233,16 @@ public abstract class IgniteUtils {
             return size;
         }
     }
+
+    /**
+     * Maps object hash to some index between 0 and specified size via modulo 
operation.
+     *
+     * @param hash Object hash.
+     * @param size Size greater than 0.
+     * @return Calculated index in range [0..size).
+     */
+    public static int hashToIndex(int hash, int size) {
+        return safeAbs(hash % size);
+    }
+
 }
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStoreSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStoreSelfTest.java
index 5a00327..77d91b6 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStoreSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/store/GridCacheWriteBehindStoreSelfTest.java
@@ -36,6 +36,21 @@ import org.junit.Test;
  * This class provides basic tests for {@link 
org.apache.ignite.internal.processors.cache.store.GridCacheWriteBehindStore}.
  */
 public class GridCacheWriteBehindStoreSelfTest extends 
GridCacheWriteBehindStoreAbstractSelfTest {
+    /** Sizes set for {@link #testResolveFlusherByKeyHash()}. */
+    private static final int[] DISTRIBUTION_TESTING_SIZES = new int[] {
+        1, 2, 4, 8, 16, 32, 64, 128, 256, 0x10000, 0x80000,
+
+        3, 5, 7, 9, 10, 12, 15, 17, 19, 23, 29, 31, 37, 66, 146, 100500
+    };
+
+    /** Hashes set for {@link #testResolveFlusherByKeyHash()}. */
+    private static final int[] DISTRIBUTION_TESTING_HASHES = new int[] {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 17, 19, 23, 29, 31, 37, 123, 
100500,
+        0xdeadbeef,
+        "abc".hashCode(),
+        "accb2e8ea33e4a89b4189463cacc3c4e".hashCode(),
+    };
+
     /**
      * Tests correct store (with write coalescing) shutdown when underlying 
store fails.
      *
@@ -308,9 +323,8 @@ public class GridCacheWriteBehindStoreSelfTest extends 
GridCacheWriteBehindStore
             log().info(">>> [putCnt = " + actualPutCnt.get() + ", 
delegatePutCnt=" + delegatePutCnt + "]");
 
             assertTrue("No puts were made to the underlying store", 
delegatePutCnt > 0);
-            if (store.getWriteCoalescing()) {
+            if (store.getWriteCoalescing())
                 assertTrue("Too many puts were made to the underlying store", 
delegatePutCnt < actualPutCnt.get() / 10);
-            }
             else {
                 assertTrue(
                     "Too few puts cnt=" + actualPutCnt.get() + " << 
storePutCnt=" + delegatePutCnt,
@@ -453,4 +467,28 @@ public class GridCacheWriteBehindStoreSelfTest extends 
GridCacheWriteBehindStore
 
         assertTrue("Store map key set: " + underlyingMap.keySet(), 
F.eqOrdered(underlyingMap.keySet(), intList));
     }
+
+    /**
+     * Test to verify the {@link 
GridCacheWriteBehindStore#resolveFlusherByKeyHash(int)}.
+     */
+    @Test
+    public void testResolveFlusherByKeyHash() {
+        store = new GridCacheWriteBehindStore<>(null, "", "", log, delegate);
+
+        Arrays.stream(DISTRIBUTION_TESTING_SIZES).forEach(size -> {
+            store.setFlushThreadCount(size);
+
+            Arrays.stream(DISTRIBUTION_TESTING_HASHES).forEach(hash -> {
+                hashToIndexAdvancedDistributionAssertion(hash, size);
+                hashToIndexAdvancedDistributionAssertion((-1) * hash, size);
+            });
+        });
+    }
+
+    private void hashToIndexAdvancedDistributionAssertion(int hash, int size) {
+        int idx = store.resolveFlusherByKeyHash(hash);
+
+        assertTrue("index=" + idx + " is negative, when hash=" + hash + ", 
size=" + size, idx >= 0);
+        assertTrue("index=" + idx + " is bigger than " + size + " bound, when 
hash=" + hash + ", size=" + size, idx < size);
+    }
 }
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
index 3f1cf07..2ab8f97 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
@@ -1527,4 +1527,32 @@ public class IgniteUtilsSelfTest extends 
GridCommonAbstractTest {
          */
         void accept(@Nullable T t) throws Exception;
     }
+
+    /**
+     * Test to verify the {@link U#hashToIndex(int, int)}.
+     */
+    @Test
+    public void testHashToIndexDistribution() {
+        assertEquals(0, U.hashToIndex(1, 1));
+        assertEquals(0, U.hashToIndex(2, 1));
+        assertEquals(1, U.hashToIndex(1, 2));
+        assertEquals(0, U.hashToIndex(2, 2));
+
+        assertEquals(1, U.hashToIndex(1, 4));
+        assertEquals(2, U.hashToIndex(2, 4));
+        assertEquals(3, U.hashToIndex(3, 4));
+        assertEquals(0, U.hashToIndex(4, 4));
+        assertEquals(1, U.hashToIndex(5, 4));
+        assertEquals(0, U.hashToIndex(8, 4));
+        assertEquals(3, U.hashToIndex(15, 4));
+
+        assertEquals(1, U.hashToIndex(-1, 4));
+        assertEquals(2, U.hashToIndex(-2, 4));
+        assertEquals(3, U.hashToIndex(-3, 4));
+        assertEquals(0, U.hashToIndex(-4, 4));
+        assertEquals(1, U.hashToIndex(-5, 4));
+        assertEquals(0, U.hashToIndex(-8, 4));
+        assertEquals(3, U.hashToIndex(-15, 4));
+    }
+
 }

Reply via email to