You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by il...@apache.org on 2020/02/11 13:36:42 UTC
[ignite] branch master updated: IGNITE-6804 Dev warning if
unordered collection passed to bulk cache op - Fixes #6976.
This is an automated email from the ASF dual-hosted git repository.
ilyak 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 6e3e23a IGNITE-6804 Dev warning if unordered collection passed to bulk cache op - Fixes #6976.
6e3e23a is described below
commit 6e3e23a78ff62a3acf6a72e1ea0b4afe46a0b94a
Author: Ilya Kasnacheev <il...@gmail.com>
AuthorDate: Tue Feb 11 16:35:59 2020 +0300
IGNITE-6804 Dev warning if unordered collection passed to bulk cache op - Fixes #6976.
Signed-off-by: Ilya Kasnacheev <il...@gmail.com>
---
.../main/java/org/apache/ignite/IgniteCache.java | 40 ++-
.../processors/cache/GridCacheAdapter.java | 101 ++++++
.../distributed/dht/atomic/GridDhtAtomicCache.java | 26 +-
.../dht/colocated/GridDhtColocatedCache.java | 2 +
.../distributed/near/GridNearAtomicCache.java | 2 +
.../near/GridNearTransactionalCache.java | 2 +
.../cache/local/atomic/GridLocalAtomicCache.java | 13 +-
.../GridCacheHashMapPutAllWarningsTest.java | 384 +++++++++++++++++++++
.../cache/GridCacheAbstractLocalStoreSelfTest.java | 15 +-
.../cache/GridCacheAbstractMetricsSelfTest.java | 7 +-
...acheAtomicEntryProcessorDeploymentSelfTest.java | 4 +-
.../GridCacheInterceptorAbstractSelfTest.java | 6 +-
.../processors/cache/IgniteCacheGroupsTest.java | 2 +-
.../cache/IgniteClientCacheStartFailoverTest.java | 4 +-
.../GridCacheTransformEventSelfTest.java | 4 +-
.../atomic/AtomicPutAllChangingTopologyTest.java | 4 +-
.../near/GridNearCacheStoreUpdateTest.java | 10 +-
.../IgniteCacheStoreSessionAbstractTest.java | 12 +-
.../cache/transactions/TxRollbackAsyncTest.java | 4 +-
.../transactions/TxRollbackOnTimeoutTest.java | 4 +-
.../junits/common/GridCommonAbstractTest.java | 3 +-
.../ignite/testsuites/IgniteCacheTestSuite5.java | 3 +
.../processors/cache/BigEntryQueryTest.java | 7 +-
.../cpp/core/include/ignite/cache/cache.h | 6 +
.../dotnet/Apache.Ignite.Core/Cache/ICache.cs | 24 ++
25 files changed, 637 insertions(+), 52 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteCache.java b/modules/core/src/main/java/org/apache/ignite/IgniteCache.java
index 0e59a78..49c8908 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteCache.java
@@ -643,6 +643,10 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
* <p>
* Please refer to documentation for {@link CacheAtomicityMode#ATOMIC} for information on
* system behavior in crash scenarios for atomic caches.
+ * <p>
+ * Keys are locked in the order in which they appear in map. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeMap}. Using unordered map,
+ * such as {@link java.util.HashMap}, while calling this method in parallel <b>will lead to deadlock</b>.
*
* @param map Map containing keys and entry processors to be applied to values.
* @param args Additional arguments to pass to the {@link EntryProcessor}.
@@ -657,7 +661,7 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
Object... args) throws TransactionException;
/**
- * Asynchronously version of the {@link #invokeAll(Map, Object...)} method.
+ * Asynchronous version of the {@link #invokeAll(Map, Object...)} method.
*
* @param map Map containing keys and entry processors to be applied to values.
* @param args Additional arguments to pass to the {@link EntryProcessor}.
@@ -902,6 +906,11 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
/**
* {@inheritDoc}
+ * <p>
+ * Keys are locked in the order in which they appear in map. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeMap}. Using unordered map,
+ * such as {@link java.util.HashMap}, while calling this method in parallel <b>will lead to deadlock</b>.
+ *
* @throws TransactionException If operation within transaction is failed.
*/
@IgniteAsyncSupported
@@ -923,6 +932,10 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
* <p>
* In Default Consistency mode, individual puts occur atomically but not
* the entire putAll. Listeners may observe individual updates.
+ * <p>
+ * Keys are locked in the order in which they appear in map. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeMap}. Using unordered map,
+ * such as {@link java.util.HashMap}, while calling this method in parallel <b>will lead to deadlock</b>.
*
* @param map Map containing keys and values to put into the cache.
* @return a Future representing pending completion of the operation.
@@ -1103,6 +1116,11 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
/**
* {@inheritDoc}
+ * <p>
+ * Keys are locked in the order in which they appear in key set. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeSet}. Using unordered map,
+ * such as {@link java.util.HashSet}, while calling this method in parallel <b>will lead to deadlock</b>.
+ *
* @throws TransactionException If operation within transaction is failed.
*/
@IgniteAsyncSupported
@@ -1119,6 +1137,10 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
* <li>if the cache is a write-through cache, the {@link CacheWriter}</li>
* </ul>
* If the key set is empty, the {@link CacheWriter} is not called.
+ * <p>
+ * Keys are locked in the order in which they appear in key set. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeSet}. Using unordered map,
+ * such as {@link java.util.HashSet}, while calling this method in parallel <b>will lead to deadlock</b>.
*
* @param keys Keys set.
* @return a Future representing pending completion of the operation.
@@ -1360,6 +1382,10 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
* <p>
* Please refer to documentation for {@link CacheAtomicityMode#ATOMIC} for information on
* system behavior in crash scenarios for atomic caches.
+ * <p>
+ * Keys are locked in the order in which they appear in key set. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeSet}. Using unordered map,
+ * such as {@link java.util.HashSet}, while calling this method in parallel <b>will lead to deadlock</b>.
*
* @throws TransactionException If operation within transaction is failed.
*/
@@ -1389,6 +1415,10 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
* <p>
* Please refer to documentation for {@link CacheAtomicityMode#ATOMIC} for information on
* system behavior in crash scenarios for atomic caches.
+ * <p>
+ * Keys are locked in the order in which they appear in key set. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeSet}. Using unordered map,
+ * such as {@link java.util.HashSet}, while calling this method in parallel <b>will lead to deadlock</b>.
*
* @param keys The set of keys.
* @param entryProcessor The {@link EntryProcessor} to invoke.
@@ -1422,6 +1452,10 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
* An instance of entry processor must be stateless as it may be invoked multiple times on primary and
* backup nodes in the cache. It is guaranteed that the value passed to the entry processor will be always
* the same.
+ * <p>
+ * Keys are locked in the order in which they appear in key set. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeSet}. Using unordered map,
+ * such as {@link java.util.HashSet}, while calling this method in parallel <b>will lead to deadlock</b>.
*
* @param keys The set of keys for entries to process.
* @param entryProcessor The {@link CacheEntryProcessor} to invoke.
@@ -1466,6 +1500,10 @@ public interface IgniteCache<K, V> extends javax.cache.Cache<K, V>, IgniteAsyncS
* An instance of entry processor must be stateless as it may be invoked multiple times on primary and
* backup nodes in the cache. It is guaranteed that the value passed to the entry processor will be always
* the same.
+ * <p>
+ * Keys are locked in the order in which they appear in key set. It is caller's responsibility to
+ * make sure keys always follow same order, such as by using {@link java.util.TreeSet}. Using unordered map,
+ * such as {@link java.util.HashSet}, while calling this method in parallel <b>will lead to deadlock</b>.
*
* @param keys The set of keys for entries to process.
* @param entryProcessor The {@link CacheEntryProcessor} to invoke.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index 00eb65e..265448b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -34,6 +34,8 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@@ -114,6 +116,7 @@ import org.apache.ignite.internal.transactions.IgniteTxHeuristicCheckedException
import org.apache.ignite.internal.transactions.IgniteTxRollbackCheckedException;
import org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException;
import org.apache.ignite.internal.transactions.TransactionCheckedException;
+import org.apache.ignite.internal.util.GridSerializableMap;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
import org.apache.ignite.internal.util.future.GridEmbeddedFuture;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
@@ -134,6 +137,7 @@ import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.GPC;
+import org.apache.ignite.internal.util.typedef.internal.LT;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiPredicate;
@@ -1979,6 +1983,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.GET);
+
return getAllAsync0(ctx.cacheKeysView(keys),
readerArgs,
readThrough,
@@ -2726,6 +2732,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -2815,6 +2823,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -2865,6 +2875,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(map.keySet());
+ warnIfUnordered(map, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -2910,6 +2922,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(map.keySet());
+ warnIfUnordered(map, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -3055,6 +3069,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(m.keySet());
+ warnIfUnordered(m, BulkOperation.PUT);
+
putAll0(m);
if (statsEnabled)
@@ -3086,6 +3102,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(m.keySet());
+ warnIfUnordered(m, BulkOperation.PUT);
+
return putAllAsync0(m);
}
@@ -3243,6 +3261,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.REMOVE);
+
removeAll0(keys);
if (statsEnabled)
@@ -3283,6 +3303,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.REMOVE);
+
IgniteInternalFuture<Object> fut = removeAllAsync0(keys);
if (statsEnabled)
@@ -5180,6 +5202,85 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
}
/**
+ * Checks that given map is sorted or otherwise constant order, or processed inside deadlock-detecting transaction.
+ *
+ * Issues developer warning otherwise.
+ *
+ * @param m Map to examine.
+ */
+ protected void warnIfUnordered(Map<?, ?> m, BulkOperation op) {
+ if (m == null || m.size() <= 1)
+ return;
+
+ if (m instanceof SortedMap || m instanceof GridSerializableMap)
+ return;
+
+ Transaction tx = ctx.kernalContext().cache().transactions().tx();
+
+ if (tx != null && !op.canBlockTx(tx.concurrency(), tx.isolation()))
+ return;
+
+ LT.warn(log, "Unordered map " + m.getClass().getName() +
+ " is used for " + op.title() + " operation on cache " + name() + ". " +
+ "This can lead to a distributed deadlock. Switch to a sorted map like TreeMap instead.");
+ }
+
+ /**
+ * Checks that given collection is sorted set, or processed inside deadlock-detecting transaction.
+ *
+ * Issues developer warning otherwise.
+ *
+ * @param coll Collection to examine.
+ */
+ protected void warnIfUnordered(Collection<?> coll, BulkOperation op) {
+ if (coll == null || coll.size() <= 1)
+ return;
+
+ if (coll instanceof SortedSet || coll instanceof GridCacheAdapter.KeySet)
+ return;
+
+ // To avoid false positives, once removeAll() is called, cache will never issue Remove All warnings.
+ if (ctx.lastRemoveAllJobFut().get() != null && op == BulkOperation.REMOVE)
+ return;
+
+ Transaction tx = ctx.kernalContext().cache().transactions().tx();
+
+ if (op == BulkOperation.GET && tx == null)
+ return;
+
+ if (tx != null && !op.canBlockTx(tx.concurrency(), tx.isolation()))
+ return;
+
+ LT.warn(log, "Unordered collection " + coll.getClass().getName() +
+ " is used for " + op.title() + " operation on cache " + name() + ". " +
+ "This can lead to a distributed deadlock. Switch to a sorted set like TreeSet instead.");
+ }
+
+ /** */
+ protected enum BulkOperation {
+ GET,
+ PUT,
+ INVOKE,
+ REMOVE;
+
+ /** */
+ public String title() {
+ return name().toLowerCase() + "All";
+ }
+
+ /** */
+ public boolean canBlockTx(TransactionConcurrency concurrency, TransactionIsolation isolation) {
+ if (concurrency == OPTIMISTIC && isolation == SERIALIZABLE)
+ return false;
+
+ if (this == GET && concurrency == PESSIMISTIC && isolation == READ_COMMITTED)
+ return false;
+
+ return true;
+ }
+ }
+
+ /**
* @param it Internal entry iterator.
* @param deserializeBinary Deserialize binary flag.
* @return Public API iterator.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
index 9f8271a..a416416 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
@@ -565,6 +565,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.GET);
+
CacheOperationContext opCtx = ctx.operationContextPerCall();
subjId = ctx.subjectIdPerCall(subjId, opCtx);
@@ -696,6 +698,10 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
/** {@inheritDoc} */
@Override public IgniteInternalFuture<?> putAllConflictAsync(Map<KeyCacheObject, GridCacheDrInfo> conflictMap) {
ctx.dr().onReceiveCacheEntriesReceived(conflictMap.size());
+ if (map != null && keyCheck)
+ validateCacheKeys(conflictMap.keySet());
+
+ warnIfUnordered(conflictMap, BulkOperation.PUT);
return updateAll0(null,
null,
@@ -807,6 +813,11 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
@Override public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys,
EntryProcessor<K, V, T> entryProcessor,
Object... args) throws IgniteCheckedException {
+ if (map != null && keyCheck)
+ validateCacheKeys(keys);
+
+ warnIfUnordered(keys, BulkOperation.INVOKE);
+
return invokeAll0(false, keys, entryProcessor, args).get();
}
@@ -885,6 +896,11 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
@Override public <T> IgniteInternalFuture<Map<K, EntryProcessorResult<T>>> invokeAllAsync(Set<? extends K> keys,
final EntryProcessor<K, V, T> entryProcessor,
Object... args) {
+ if (map != null && keyCheck)
+ validateCacheKeys(keys);
+
+ warnIfUnordered(keys, BulkOperation.INVOKE);
+
return invokeAll0(true, keys, entryProcessor, args);
}
@@ -902,9 +918,6 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
Object... args) {
A.notNull(keys, "keys", entryProcessor, "entryProcessor");
- if (keyCheck)
- validateCacheKeys(keys);
-
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -953,6 +966,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
if (keyCheck)
validateCacheKeys(map.keySet());
+ warnIfUnordered(map, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -983,6 +998,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
if (keyCheck)
validateCacheKeys(map.keySet());
+ warnIfUnordered(map, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -1030,9 +1047,6 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
) {
assert ctx.updatesAllowed();
- if (map != null && keyCheck)
- validateCacheKeys(map.keySet());
-
ctx.checkSecurity(SecurityPermission.CACHE_PUT);
final CacheOperationContext opCtx = ctx.operationContextPerCall();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
index 63a0582..57214da 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
@@ -331,6 +331,8 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.GET);
+
GridNearTxLocal tx = checkCurrentTx();
final CacheOperationContext opCtx = ctx.operationContextPerCall();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
index a9b34a4..4212b87 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
@@ -420,6 +420,8 @@ public class GridNearAtomicCache<K, V> extends GridNearCacheAdapter<K, V> {
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.GET);
+
CacheOperationContext opCtx = ctx.operationContextPerCall();
subjId = ctx.subjectIdPerCall(subjId, opCtx);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java
index 151aeb5..a9eec8e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java
@@ -133,6 +133,8 @@ public class GridNearTransactionalCache<K, V> extends GridNearCacheAdapter<K, V>
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.GET);
+
GridNearTxLocal tx = ctx.tm().threadLocalTx(ctx);
CacheOperationContext opCtx = ctx.operationContextPerCall();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
index c62b1df..fb40d0c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
@@ -387,6 +387,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.GET);
+
final IgniteCacheExpiryPolicy expiry = expiryPolicy(opCtx != null ? opCtx.expiry() : null);
boolean success = true;
@@ -563,6 +565,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -650,6 +654,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
if (keyCheck)
validateCacheKeys(keys);
+ warnIfUnordered(keys, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -682,6 +688,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
if (keyCheck)
validateCacheKeys(map.keySet());
+ warnIfUnordered(map, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -717,6 +725,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
if (keyCheck)
validateCacheKeys(map.keySet());
+ warnIfUnordered(map, BulkOperation.INVOKE);
+
final boolean statsEnabled = ctx.statisticsEnabled();
final long start = statsEnabled ? System.nanoTime() : 0L;
@@ -858,9 +868,6 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
boolean readThrough,
boolean keepBinary
) throws IgniteCheckedException {
- if (keyCheck)
- validateCacheKeys(keys);
-
if (op == DELETE)
ctx.checkSecurity(SecurityPermission.CACHE_REMOVE);
else
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridCacheHashMapPutAllWarningsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridCacheHashMapPutAllWarningsTest.java
new file mode 100644
index 0000000..1e49d90
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/GridCacheHashMapPutAllWarningsTest.java
@@ -0,0 +1,384 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.EntryProcessorResult;
+import javax.cache.processor.MutableEntry;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.testframework.ListeningTestLogger;
+import org.apache.ignite.testframework.MvccFeatureChecker;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+import org.apache.ignite.transactions.TransactionConcurrency;
+import org.apache.ignite.transactions.TransactionIsolation;
+import org.junit.Test;
+
+/**
+ * Test exchange manager warnings.
+ */
+public class GridCacheHashMapPutAllWarningsTest extends GridCommonAbstractTest {
+ /** */
+ private ListeningTestLogger testLog;
+
+ /** */
+ @Override protected IgniteConfiguration getConfiguration(String instanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(instanceName);
+
+ cfg.setGridLogger(testLog);
+
+ return cfg;
+ }
+
+ /** */
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+
+ stopAllGrids();
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testHashMapPutAllExactMessage() throws Exception {
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<>("exact"));
+
+ HashMap<Integer, String> m = new HashMap<>();
+
+ m.put(1, "foo");
+ m.put(2, "bar");
+
+ c.putAll(m);
+
+ assertEquals(2, c.size());
+
+ int found = 0;
+
+ for (String message : messages) {
+ if (message.contains("Unordered map java.util.HashMap is used for putAll operation on cache exact. " +
+ "This can lead to a distributed deadlock. Switch to a sorted map like TreeMap instead."))
+ found++;
+ }
+
+ assertEquals(1, found);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testHashMapPutAllExplicitOptimistic() throws Exception {
+ if (MvccFeatureChecker.forcedMvcc())
+ return;
+
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<Integer, String>("explicitTx")
+ .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL));
+
+ ignite.transactions().txStart(TransactionConcurrency.OPTIMISTIC, TransactionIsolation.SERIALIZABLE);
+
+ HashMap<Integer, String> m = new HashMap<>();
+
+ m.put(1, "foo");
+ m.put(2, "bar");
+
+ c.putAllAsync(m);
+
+ ignite.transactions().tx().commit();
+
+ assertEquals(2, c.size());
+
+ for (String message : messages) {
+ assertFalse(message.contains("Unordered map"));
+ assertFalse(message.contains("operation on cache"));
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testHashMapInvokeAllLocal() throws Exception {
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<Integer, String>("invoke")
+ .setCacheMode(CacheMode.LOCAL));
+
+ c.put(1, "foo");
+ c.put(2, "bar");
+
+ Map<Integer, EntryProcessorResult<String>> result = c.invokeAll(new HashSet<>(Arrays.asList(1, 2)),
+ new EntryProcessor<Integer, String, String>() {
+ @Override public String process(MutableEntry entry, Object... arguments) throws EntryProcessorException {
+ String newVal = entry.getValue() + "2";
+
+ entry.setValue(newVal);
+
+ return newVal;
+ }
+ });
+
+ assertEquals(2, result.size());
+ assertEquals("bar2", c.get(2));
+
+ int found = 0;
+
+ for (String message : messages) {
+ if (message.contains("Unordered collection java.util.HashSet is used for invokeAll operation on cache invoke. "))
+ found++;
+ }
+
+ assertEquals(1, found);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testTreeMapRemoveAll() throws Exception {
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<Integer, String>("remove")
+ .setCacheMode(CacheMode.PARTITIONED));
+
+ c.put(1, "foo");
+ c.put(2, "bar");
+
+ c.removeAll(new TreeSet<>(Arrays.asList(1, 3)));
+
+ assertEquals(1, c.size());
+
+ int found = 0;
+
+ for (String message : messages) {
+ if (message.contains("Unordered collection "))
+ found++;
+
+ if (message.contains("operation on cache"))
+ found++;
+ }
+
+ assertEquals(0, found);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testTreeMapRemoveAllEntries() throws Exception {
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+ startGrid(1);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<Integer, String>("entries")
+ .setCacheMode(CacheMode.REPLICATED)
+ .setAtomicityMode(CacheAtomicityMode.ATOMIC)
+ .setBackups(1));
+
+ for (int i = 0; i < 1000; i++) {
+ c.put(i, "foo");
+ c.put(i * 2, "bar");
+ }
+
+ c.removeAll();
+
+ assertEquals(0, c.size());
+
+ for (String message : messages) {
+ assertFalse(message.contains("Unordered collection "));
+
+ assertFalse(message.contains("operation on cache"));
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testTreeMapClearEntries() throws Exception {
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+ startGrid(1);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<Integer, String>("entries")
+ .setCacheMode(CacheMode.PARTITIONED)
+ .setAtomicityMode(CacheAtomicityMode.ATOMIC)
+ .setBackups(1));
+
+ for (int i = 0; i < 1000; i++) {
+ c.put(i, "foo");
+ c.put(i * 2, "bar");
+ }
+
+ c.clear();
+
+ assertEquals(0, c.size());
+
+ for (String message : messages) {
+ assertFalse(message.contains("Unordered "));
+
+ assertFalse(message.contains("operation on cache"));
+ }
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testHashSetGetAllReplicated() throws Exception {
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<Integer, String>("get")
+ .setCacheMode(CacheMode.REPLICATED));
+
+ c.put(1, "foo");
+ c.put(2, "bar");
+
+ assertEquals(1, c.getAll(new HashSet<>(Arrays.asList(1, 3))).size());
+
+ int found = 0;
+
+ for (String message : messages) {
+ if (message.contains("Unordered collection "))
+ found++;
+
+ if (message.contains("operation on cache"))
+ found++;
+ }
+
+ assertEquals(0, found);
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testHashSetGetAllTx() throws Exception {
+ List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+ testLog = new ListeningTestLogger(false, log());
+
+ testLog.registerListener((s) -> {
+ if (s.contains("deadlock"))
+ messages.add(s);
+ });
+
+ Ignite ignite = startGrid(0);
+
+ IgniteCache<Integer, String> c = ignite.getOrCreateCache(new CacheConfiguration<Integer, String>("getTx")
+ .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
+ .setCacheMode(CacheMode.PARTITIONED));
+
+ c.put(1, "foo");
+ c.put(2, "bar");
+
+ try (Transaction tx = ignite.transactions().txStart(TransactionConcurrency.PESSIMISTIC, TransactionIsolation.REPEATABLE_READ)) {
+ assertEquals(1, c.getAll(new HashSet<>(Arrays.asList(1, 3))).size());
+
+ tx.commit();
+ }
+
+ int found = 0;
+
+ for (String message : messages) {
+ if (message.contains("Unordered collection java.util.HashSet is used for getAll operation on cache getTx."))
+ found++;
+ }
+
+ assertEquals(1, found);
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java
index 6c050d2..14963e6 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractLocalStoreSelfTest.java
@@ -20,10 +20,10 @@ package org.apache.ignite.internal.processors.cache;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -341,7 +341,7 @@ public abstract class GridCacheAbstractLocalStoreSelfTest extends GridCommonAbst
cache.put(key1, key1);
cache.put(key2, key2);
- Map<Integer, Integer> m = new HashMap<>();
+ Map<Integer, Integer> m = new TreeMap<>();
for (int i = KEYS; i < KEYS + 100; i++)
m.put(i, i);
@@ -453,7 +453,7 @@ public abstract class GridCacheAbstractLocalStoreSelfTest extends GridCommonAbst
for (int j = 0; j < 5; j++)
cache.get(rn.nextInt(KEYS));
- Map<Integer, Integer> m = new HashMap<>(5);
+ Map<Integer, Integer> m = new TreeMap<>();
for (int j = 0; j < 5; j++) {
Integer key = rn.nextInt(KEYS);
@@ -565,7 +565,7 @@ public abstract class GridCacheAbstractLocalStoreSelfTest extends GridCommonAbst
assertTrue(kP != kB && kB != kN && kN != kP);
- Map<Integer, Integer> m = new HashMap<>(3);
+ Map<Integer, Integer> m = new TreeMap<>();
m.put(kP, kP);
m.put(kB, kB);
@@ -595,11 +595,10 @@ public abstract class GridCacheAbstractLocalStoreSelfTest extends GridCommonAbst
.withSkipStore().withAllowAtomicOpsInTx();
try (Transaction tx = grid(i).transactions().txStart()) {
- Map<Integer, Integer> m = new HashMap<>(3);
+ Map<Integer, Integer> m = new TreeMap<>();
- for (int j = 0; j < 50; j++) {
+ for (int j = 0; j < 50; j++)
m.put(rn.nextInt(1000), 1000);
- }
cache.putAll(m);
@@ -816,7 +815,7 @@ public abstract class GridCacheAbstractLocalStoreSelfTest extends GridCommonAbst
/** {@inheritDoc} */
@Override public Map<K, IgniteBiTuple<V, ?>> loadAll(Iterable<? extends K> keys) throws CacheLoaderException {
- Map<K, IgniteBiTuple<V, ?>> res = new HashMap<>();
+ Map<K, IgniteBiTuple<V, ?>> res = new TreeMap<>();
for (K key : keys) {
IgniteBiTuple<V, ?> val = map.get(key);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractMetricsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractMetricsSelfTest.java
index 57ee010..b6808ef 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractMetricsSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAbstractMetricsSelfTest.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.processors.cache;
import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -368,7 +367,7 @@ public abstract class GridCacheAbstractMetricsSelfTest extends GridCacheAbstract
assertEquals(0.0, cache.localMetrics().getAverageRemoveTime(), 0.0);
- Set<Integer> keys = new HashSet<>(4, 1);
+ Set<Integer> keys = new TreeSet<>();
keys.add(1);
keys.add(2);
keys.add(3);
@@ -1377,7 +1376,7 @@ public abstract class GridCacheAbstractMetricsSelfTest extends GridCacheAbstract
public void testInvokeAllMultipleKeysAvgTime() throws IgniteCheckedException {
IgniteCache<Integer, Integer> cache = grid(0).cache(DEFAULT_CACHE_NAME);
- Set<Integer> keys = new HashSet<>();
+ Set<Integer> keys = new TreeSet<>();
keys.add(1);
keys.add(2);
@@ -1397,7 +1396,7 @@ public abstract class GridCacheAbstractMetricsSelfTest extends GridCacheAbstract
public void testInvokeAllAsyncMultipleKeysAvgTime() throws IgniteCheckedException {
IgniteCache<Integer, Integer> cache = grid(0).cache(DEFAULT_CACHE_NAME);
- Set<Integer> keys = new HashSet<>();
+ Set<Integer> keys = new TreeSet<>();
keys.add(1);
keys.add(2);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAtomicEntryProcessorDeploymentSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAtomicEntryProcessorDeploymentSelfTest.java
index b286596..2cef945 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAtomicEntryProcessorDeploymentSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheAtomicEntryProcessorDeploymentSelfTest.java
@@ -17,8 +17,8 @@
package org.apache.ignite.internal.processors.cache;
-import java.util.HashSet;
import java.util.Map;
+import java.util.TreeSet;
import javax.cache.processor.EntryProcessorResult;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.CacheAtomicityMode;
@@ -183,7 +183,7 @@ public class GridCacheAtomicEntryProcessorDeploymentSelfTest extends GridCommonA
IgniteCache cache = getCache();
- HashSet keys = new HashSet();
+ TreeSet keys = new TreeSet();
for (int i = 0; i < 3; i++) {
String key = "key" + i;
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheInterceptorAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheInterceptorAbstractSelfTest.java
index 8cfbab3..b7bd7034 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheInterceptorAbstractSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheInterceptorAbstractSelfTest.java
@@ -19,11 +19,11 @@ package org.apache.ignite.internal.processors.cache;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.cache.Cache;
import javax.cache.processor.EntryProcessor;
@@ -941,7 +941,7 @@ public abstract class GridCacheInterceptorAbstractSelfTest extends GridCacheAbst
// Interceptor returns incremented new value.
interceptor.retInterceptor = new PutIncrementInterceptor();
- Map<String, Integer> map = new HashMap<>();
+ Map<String, Integer> map = new TreeMap<>();
final String key1;
String key2;
@@ -1030,7 +1030,7 @@ public abstract class GridCacheInterceptorAbstractSelfTest extends GridCacheAbst
*/
@SuppressWarnings("unchecked")
private void testBatchRemove(Operation op) throws Exception {
- Map<String, Integer> map = new HashMap<>();
+ Map<String, Integer> map = new TreeMap<>();
final String key1;
String key2;
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java
index 316ed48..dff20b3 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java
@@ -1190,7 +1190,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
private Map<Integer, Integer> generateDataMap(int startKey, int cnt) {
Random rnd = ThreadLocalRandom.current();
- Map<Integer, Integer> data = U.newHashMap(cnt);
+ Map<Integer, Integer> data = new TreeMap<>();
for (int i = 0; i < cnt; i++)
data.put(startKey++, rnd.nextInt());
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java
index 72f2155..0376913 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheStartFailoverTest.java
@@ -18,11 +18,11 @@
package org.apache.ignite.internal.processors.cache;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
@@ -518,7 +518,7 @@ public class IgniteClientCacheStartFailoverTest extends GridCommonAbstractTest {
private List<String> startCaches(Ignite node, int keys) {
List<String> cacheNames = new ArrayList<>();
- final Map<Integer, Integer> map = new HashMap<>();
+ final Map<Integer, Integer> map = new TreeMap<>();
for (int i = 0; i < keys; i++)
map.put(i, i);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTransformEventSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTransformEventSelfTest.java
index f018285..6146085 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTransformEventSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTransformEventSelfTest.java
@@ -20,9 +20,9 @@ package org.apache.ignite.internal.processors.cache.distributed;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.TreeSet;
import java.util.UUID;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.MutableEntry;
@@ -221,7 +221,7 @@ public class GridCacheTransformEventSelfTest extends GridCommonAbstractTest {
key++;
}
- keys = new HashSet<>();
+ keys = new TreeSet<>();
keys.add(key1);
keys.add(key2);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/AtomicPutAllChangingTopologyTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/AtomicPutAllChangingTopologyTest.java
index d654e47..a062905 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/AtomicPutAllChangingTopologyTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/AtomicPutAllChangingTopologyTest.java
@@ -17,10 +17,10 @@
package org.apache.ignite.internal.processors.cache.distributed.dht.atomic;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import org.apache.ignite.Ignite;
@@ -124,7 +124,7 @@ public class AtomicPutAllChangingTopologyTest extends GridCommonAbstractTest {
log.info("Created cache.");
- Map<Integer, Integer> data = new HashMap<>(CACHE_SIZE);
+ Map<Integer, Integer> data = new TreeMap<>();
for (int i = 0; i < CACHE_SIZE; i++)
data.put(i, i);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheStoreUpdateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheStoreUpdateTest.java
index 51a48c9..0bdb4cb 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheStoreUpdateTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheStoreUpdateTest.java
@@ -18,9 +18,9 @@
package org.apache.ignite.internal.processors.cache.distributed.near;
import java.io.Serializable;
-import java.util.HashMap;
import java.util.Map;
import java.util.Random;
+import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
@@ -267,8 +267,8 @@ public class GridNearCacheStoreUpdateTest extends GridCommonAbstractTest {
* @throws Exception If fail.
*/
private void checkNearBatch(TransactionConcurrency txConc, TransactionIsolation txIsolation) throws Exception {
- final Map<String, String> data1 = new HashMap<>();
- final Map<String, String> data2 = new HashMap<>();
+ final Map<String, String> data1 = new TreeMap<>();
+ final Map<String, String> data2 = new TreeMap<>();
for (int i = 0; i < 10; i++) {
data1.put(String.valueOf(i), String.valueOf(i));
@@ -326,8 +326,8 @@ public class GridNearCacheStoreUpdateTest extends GridCommonAbstractTest {
*/
private void checkNearBatchConcurrent(TransactionConcurrency txConc, TransactionIsolation txIsolation)
throws Exception {
- final Map<String, String> data1 = new HashMap<>();
- final Map<String, String> data2 = new HashMap<>();
+ final Map<String, String> data1 = new TreeMap<>();
+ final Map<String, String> data2 = new TreeMap<>();
for (int j = 0; j < 10; j++) {
data1.clear();
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheStoreSessionAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheStoreSessionAbstractTest.java
index cfc5f75..c240d53 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheStoreSessionAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheStoreSessionAbstractTest.java
@@ -20,11 +20,11 @@ package org.apache.ignite.internal.processors.cache.integration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import javax.cache.Cache;
import javax.cache.integration.CacheLoaderException;
import javax.cache.integration.CacheWriterException;
@@ -136,13 +136,13 @@ public abstract class IgniteCacheStoreSessionAbstractTest extends IgniteCacheAbs
boolean tx = atomicityMode() == TRANSACTIONAL;
- expData.add(new ExpectedData(false, "load", new HashMap<>(), cache.getName()));
+ expData.add(new ExpectedData(false, "load", new TreeMap<>(), cache.getName()));
assertEquals(key, cache.get(key));
assertTrue(expData.isEmpty());
- expData.add(new ExpectedData(false, "loadAll", new HashMap<>(), cache.getName()));
+ expData.add(new ExpectedData(false, "loadAll", new TreeMap<>(), cache.getName()));
assertEquals(3, cache.getAll(keys).size());
@@ -172,7 +172,7 @@ public abstract class IgniteCacheStoreSessionAbstractTest extends IgniteCacheAbs
assertTrue(expData.isEmpty());
- Map<Object, Object> vals = new HashMap<>();
+ Map<Object, Object> vals = new TreeMap<>();
for (Object key0 : keys)
vals.put(key0, key0);
@@ -202,7 +202,7 @@ public abstract class IgniteCacheStoreSessionAbstractTest extends IgniteCacheAbs
* @param expCacheName Expected cache name.
*/
private void expectedData(boolean tx, String expMtd, String expCacheName) {
- expData.add(new ExpectedData(tx, expMtd, new HashMap<>(), expCacheName));
+ expData.add(new ExpectedData(tx, expMtd, new TreeMap<>(), expCacheName));
if (tx)
expData.add(new ExpectedData(true, "sessionEnd", F.<Object, Object>asMap(0, expMtd), expCacheName));
@@ -290,7 +290,7 @@ public abstract class IgniteCacheStoreSessionAbstractTest extends IgniteCacheAbs
checkSession("loadAll");
- Map<Object, Object> loaded = new HashMap<>();
+ Map<Object, Object> loaded = new TreeMap<>();
for (Object key : keys)
loaded.put(key, key);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackAsyncTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackAsyncTest.java
index 9b4a319..23213b8 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackAsyncTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackAsyncTest.java
@@ -18,12 +18,12 @@
package org.apache.ignite.internal.processors.cache.transactions;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
@@ -498,7 +498,7 @@ public class TxRollbackAsyncTest extends GridCommonAbstractTest {
TransactionConcurrency conc) throws Exception {
final Ignite client = startClient();
- Map<Integer, Integer> entries = new HashMap<>();
+ Map<Integer, Integer> entries = new TreeMap<>();
for (int i = 0; i < 1000000; i++)
entries.put(i, i);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java
index 0cc072a..87f5790 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxRollbackOnTimeoutTest.java
@@ -18,10 +18,10 @@
package org.apache.ignite.internal.processors.cache.transactions;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -808,7 +808,7 @@ public class TxRollbackOnTimeoutTest extends GridCommonAbstractTest {
private void testEnlistMany(boolean write) throws Exception {
final Ignite client = startClient();
- Map<Integer, Integer> entries = new HashMap<>();
+ Map<Integer, Integer> entries = new TreeMap<>();
for (int i = 0; i < 1000000; i++)
entries.put(i, i);
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
index 05b4941..593d1c3 100755
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
@@ -28,6 +28,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
@@ -2180,7 +2181,7 @@ public abstract class GridCommonAbstractTest extends GridAbstractTest {
int mode = putType != null && putType.length > 0 ? putType[0] : 0;
- Map<Integer, Integer> map = keys.stream().collect(Collectors.toMap(k -> k, k -> k));
+ Map<Integer, Integer> map = keys.stream().collect(Collectors.toMap(k -> k, k -> k, (a, b) -> a, TreeMap::new));
switch (mode) {
case 0:
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java
index b2403b7..2301168 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite5.java
@@ -26,6 +26,7 @@ import org.apache.ignite.cache.affinity.AffinityClientNodeSelfTest;
import org.apache.ignite.cache.affinity.AffinityDistributionLoggingTest;
import org.apache.ignite.cache.affinity.AffinityHistoryCleanupTest;
import org.apache.ignite.cache.affinity.local.LocalAffinityFunctionTest;
+import org.apache.ignite.internal.GridCacheHashMapPutAllWarningsTest;
import org.apache.ignite.internal.GridCachePartitionExchangeManagerHistSizeTest;
import org.apache.ignite.internal.GridCachePartitionExchangeManagerWarningsTest;
import org.apache.ignite.internal.processors.cache.CacheKeepBinaryTransactionTest;
@@ -130,6 +131,8 @@ public class IgniteCacheTestSuite5 {
GridTestUtils.addTestIfNeeded(suite, GridCachePartitionExchangeManagerWarningsTest.class, ignoredTests);
+ GridTestUtils.addTestIfNeeded(suite, GridCacheHashMapPutAllWarningsTest.class, ignoredTests);
+
GridTestUtils.addTestIfNeeded(suite, NotMappedPartitionInTxTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, ConcurrentCacheStartTest.class, ignoredTests);
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/BigEntryQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/BigEntryQueryTest.java
index 7cf42b4..52b533a 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/BigEntryQueryTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/BigEntryQueryTest.java
@@ -22,8 +22,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
@@ -79,9 +81,10 @@ public class BigEntryQueryTest extends GridCommonAbstractTest {
.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC)
.setIndexedTypes(Long.class, Value.class));
- cache.putAll(LongStream.range(610026643276160000L, 610026643276170000L).boxed()
+ cache.putAll((Map) LongStream.range(610026643276160000L, 610026643276170000L).boxed()
.collect(Collectors.toMap(Function.identity(),
- t -> Value.of(new byte[(random.nextInt(16)) * 1000]))));
+ t -> Value.of(new byte[(random.nextInt(16)) * 1000]),
+ (a, b) -> a, TreeMap::new)));
for (int i = 0; i < 10; i++) {
long start = 610026643276160000L;
diff --git a/modules/platforms/cpp/core/include/ignite/cache/cache.h b/modules/platforms/cpp/core/include/ignite/cache/cache.h
index e18654d..c0dd4a9 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/cache.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/cache.h
@@ -469,6 +469,9 @@ namespace ignite
*
* This method should only be used on the valid instance.
*
+ * Keys are locked in the order of iteration. It is caller's responsibility to make sure keys always follow
+ * same order. If that is not observed, calling this method in parallel <b>will lead to deadlock</b>.
+ *
* @param begin Iterator pointing to the beggining of the key-value pair sequence.
* @param end Iterator pointing to the end of the key-value pair sequence.
*/
@@ -1214,6 +1217,9 @@ namespace ignite
*
* This method should only be used on the valid instance.
*
+ * Keys are locked in the order of iteration. It is caller's responsibility to make sure keys always follow
+ * same order. If that is not observed, calling this method in parallel <b>will lead to deadlock</b>.
+ *
* @param begin Iterator pointing to the beggining of the key sequence.
* @param end Iterator pointing to the end of the key sequence.
*/
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/ICache.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/ICache.cs
index a25fb97..49715f7 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Cache/ICache.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Cache/ICache.cs
@@ -527,6 +527,10 @@ namespace Apache.Ignite.Core.Cache
/// Stores given key-value pairs in cache.
/// If write-through is enabled, the stored values will be persisted to store.
/// This method is transactional and will enlist the entry into ongoing transaction if there is one.
+ ///
+ /// Keys are locked in the order in which they are enumerated. It is caller's responsibility to
+ /// make sure keys always follow same order, such as by using <see cref="SortedDictionary{K, V}"/>. Using unordered
+ /// dictionary, such as <see cref="Dictionary{K, V}"/>, while calling this method in parallel <b>will lead to deadlock</b>.
/// </summary>
/// <param name="vals">Key-value pairs to store in cache.</param>
void PutAll(IEnumerable<KeyValuePair<TK, TV>> vals);
@@ -535,6 +539,10 @@ namespace Apache.Ignite.Core.Cache
/// Stores given key-value pairs in cache.
/// If write-through is enabled, the stored values will be persisted to store.
/// This method is transactional and will enlist the entry into ongoing transaction if there is one.
+ ///
+ /// Keys are locked in the order in which they are enumerated. It is caller's responsibility to
+ /// make sure keys always follow same order, such as by using <see cref="SortedDictionary{K, V}"/>. Using unordered
+ /// dictionary, such as <see cref="Dictionary{K, V}"/>, while calling this method in parallel <b>will lead to deadlock</b>.
/// </summary>
/// <param name="vals">Key-value pairs to store in cache.</param>
Task PutAllAsync(IEnumerable<KeyValuePair<TK, TV>> vals);
@@ -656,6 +664,10 @@ namespace Apache.Ignite.Core.Cache
/// Removes given key mappings from cache.
/// If write-through is enabled, the value will be removed from store.
/// This method is transactional and will enlist the entry into ongoing transaction if there is one.
+ ///
+ /// Keys are locked in the order in which they are enumerated. It is caller's responsibility to
+ /// make sure keys always follow same order, such as by using <see cref="SortedSet{K}"/>. Using unordered
+ /// collection, such as <see cref="HashSet{K}"/>, while calling this method in parallel <b>will lead to deadlock</b>.
/// </summary>
/// <param name="keys">Keys whose mappings are to be removed from cache.</param>
void RemoveAll(IEnumerable<TK> keys);
@@ -664,6 +676,10 @@ namespace Apache.Ignite.Core.Cache
/// Removes given key mappings from cache.
/// If write-through is enabled, the value will be removed from store.
/// This method is transactional and will enlist the entry into ongoing transaction if there is one.
+ ///
+ /// Keys are locked in the order in which they are enumerated. It is caller's responsibility to
+ /// make sure keys always follow same order, such as by using <see cref="SortedSet{K}"/>. Using unordered
+ /// collection, such as <see cref="HashSet{K}"/>, while calling this method in parallel <b>will lead to deadlock</b>.
/// </summary>
/// <param name="keys">Keys whose mappings are to be removed from cache.</param>
Task RemoveAllAsync(IEnumerable<TK> keys);
@@ -848,6 +864,10 @@ namespace Apache.Ignite.Core.Cache
/// Implementations may choose to process the entries in any order, including concurrently.
/// Furthermore there is no guarantee implementations will use the same processor instance
/// to process each entry, as the case may be in a non-local cache topology.
+ ///
+ /// Keys are locked in the order in which they are enumerated. It is caller's responsibility to
+ /// make sure keys always follow same order, such as by using <see cref="SortedSet{K}"/>. Using unordered
+ /// collection, such as <see cref="HashSet{K}"/>, while calling this method in parallel <b>will lead to deadlock</b>.
/// </summary>
/// <typeparam name="TArg">The type of the argument.</typeparam>
/// <typeparam name="TRes">The type of the result.</typeparam>
@@ -872,6 +892,10 @@ namespace Apache.Ignite.Core.Cache
/// Implementations may choose to process the entries in any order, including concurrently.
/// Furthermore there is no guarantee implementations will use the same processor instance
/// to process each entry, as the case may be in a non-local cache topology.
+ ///
+ /// Keys are locked in the order in which they are enumerated. It is caller's responsibility to
+ /// make sure keys always follow same order, such as by using <see cref="SortedSet{K}"/>. Using unordered
+ /// collection, such as <see cref="HashSet{K}"/>, while calling this method in parallel <b>will lead to deadlock</b>.
/// </summary>
/// <typeparam name="TArg">The type of the argument.</typeparam>
/// <typeparam name="TRes">The type of the result.</typeparam>