Details
Description
I have a program that runs several threads at the same time, all of them executing an invoke() on the same cache key to update it. I found that the EntryProcessor is not always executed, and in this case invoke() returns null. This happens when the cache is configured with CLOCK CacheAtomicWriteOrderMode, but not when using PRIMARY.
After some debugging I found out that it happens when the cache gets the invocations out of order, and dismisses the older ones, returning the result from GridCacheMapEntry line 1855.
As I understand it, this mechanism is in place to protect against out-of-order updates. Invokes, however, are a special case as they execute client code with unknown side effects that should not be discarded. If you believe the current behavior is correct, it should at least be documented.
Here is a sample program to illustrate the problem. Notice that "IN INVOKE" is printed less than 10 times (usually):
IgniteConfiguration igniteConfiguration = new IgniteConfiguration() .setFailoverSpi(new AlwaysFailoverSpi()) .setGridLogger(new Slf4jLogger()) .setPeerClassLoadingEnabled(false) .setDeploymentMode(DeploymentMode.CONTINUOUS); Ignite ignite = Ignition.start(igniteConfiguration); CacheConfiguration<String, Long> cacheConfiguration = new CacheConfiguration<String, Long>() .setName("test") .setCacheMode(CacheMode.PARTITIONED) .setAtomicityMode(CacheAtomicityMode.ATOMIC) .setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); IgniteCache<String, Long> cache = ignite.getOrCreateCache(cacheConfiguration); Thread[] threads = new Thread[10]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread() { @Override public void run() { System.out.println(String.format("Thread %s running", getId())); Long result = cache.invoke("key", (entry, arguments) -> { Long value = entry.getValue(); System.out.println(String.format("IN INVOKE: thread ID:%s exists:%s value:%s", getId(), entry.exists(), value)); if (!entry.exists()) { entry.setValue(getId()); } return value; }); System.out.println(String.format("Thread %s got invoke result %s", getId(), result)); } }; } for (Thread thread : threads) { thread.start(); } Thread.sleep(2_000); for (Thread thread : threads) { thread.interrupt(); } for (Thread thread : threads) { thread.join(); } System.out.println("Done");
Output sample:
[13:51:41] __________ ________________ [13:51:41] / _/ ___/ |/ / _/_ __/ __/ [13:51:41] _/ // (7 7 // / / / / _/ [13:51:41] /___/\___/_/|_/___/ /_/ /___/ [13:51:41] [13:51:41] ver. 1.4.0#20150924-sha1:c2def5f6 [13:51:41] 2015 Copyright(C) Apache Software Foundation [13:51:41] [13:51:41] Ignite documentation: http://ignite.apache.org [13:51:41] [13:51:41] Quiet mode. [13:51:41] ^-- To see **FULL** console log here add -DIGNITE_QUIET=false or "-v" to ignite.{sh|bat} [13:51:41] [13:51:41] Initial heap size is 256MB (should be no less than 512MB, use -Xms512m -Xmx512m). [13:51:43] Configured plugins: [13:51:43] ^-- None [13:51:43] [13:52:04] Security status [authentication=off, communication encryption=off] [13:52:10] To start Console Management & Monitoring run ignitevisorcmd.{sh|bat} [13:52:10] [13:52:10] Ignite node started OK (id=0ceced67) [13:52:10] Topology snapshot [ver=1, servers=1, clients=0, CPUs=8, heap=3.5GB] Thread 105 running Thread 107 running Thread 106 running Thread 108 running Thread 109 running Thread 110 running Thread 111 running Thread 112 running Thread 114 running Thread 113 running IN INVOKE: thread ID:107 exists:false value:null IN INVOKE: thread ID:113 exists:true value:107 Thread 107 got invoke result null IN INVOKE: thread ID:105 exists:true value:107 Thread 111 got invoke result null Thread 113 got invoke result 107 Thread 105 got invoke result 107 IN INVOKE: thread ID:110 exists:true value:107 Thread 110 got invoke result 107 Thread 106 got invoke result null IN INVOKE: thread ID:112 exists:true value:107 Thread 112 got invoke result 107 IN INVOKE: thread ID:108 exists:true value:107 Thread 108 got invoke result 107 Thread 109 got invoke result null Thread 114 got invoke result null Done
Attachments
Issue Links
- is duplicated by
-
IGNITE-3955 IgniteCache.invokeAll returns empty result set in ATOMIC cache
- Resolved