-
Bug
-
Resolution: Done
-
Major
-
13.0.0.Final
-
None
Data container entries are mutable, and writers modify them in-place. CacheEntry.clone() is used instead to copy the entry when it is returned to the user, so the user can't modify the data in the cache (see ISPN-4424 and PR #2674).
This means all users must synchronize on the entry, both writers and readers.
The only writer (InternalEntryFactoryImpl.update()) does synchronize, as do most readers (InternalEntryFactoryImpl.copy(), various ExpirationManagerImpl and ClusterExpirationManager methods).
One big omission is EntryFactoryImpl.createWrappedEntry(), which copies the data from the data container entry to a ReadCommittedEntry or a RepeatableReadEntry to store in the invocation context. Because there is no synchronization, it can read a value before another thread updates the entry, then the updated version after the update completes.
The missing synchronization is the cause of random failures in TransactionalLocalWriteSkewTest.testSharedCounter.
19:40:28,791 TRACE (ForkThread-2,Test:[]) [EntryFactoryImpl] Retrieved from container ImmortalCacheEntry{key=counter, value=420, internalMetadata=PrivateMetadata{iracMetadata=null, entryVersion=NumericVersion{version=421}}} 19:40:28,795 TRACE (ForkThread-1,Test:[]) [AbstractInternalDataContainer] Creating new ICE for writing. Existing=ImmortalCacheEntry{key=counter, value=421, internalMetadata=PrivateMetadata{iracMetadata=null, entryVersion=NumericVersion{version=422}}}, metadata=EmbeddedExpirableMetadata{version=null, lifespan=-1, maxIdle=-1}, new value=422 19:40:28,795 TRACE (ForkThread-1,Test:[]) [AbstractInternalDataContainer] Store counter=ImmortalCacheEntry{key=counter, value=422, internalMetadata=PrivateMetadata{iracMetadata=null, entryVersion=NumericVersion{version=423}}} in container 19:40:28,795 TRACE (ForkThread-2,Test:[]) [EntryFactoryImpl] Creating new entry for key counter 19:40:28,795 TRACE (ForkThread-2,Test:[]) [EntryFactoryImpl] Wrap counter for read. Entry=VersionedRepeatableReadEntry(737c1fa8){key=counter, value=420, oldValue=420, isCreated=false, isChanged=false, isRemoved=false, isExpired=false, isCommited=false, skipLookup=false, metadata=EmbeddedMetadata{version=null}, oldMetadata=EmbeddedMetadata{version=null}, internalMetadata=PrivateMetadata{iracMetadata=null, entryVersion=NumericVersion{version=423}}} 19:40:28,801 ERROR (testng-Test:[]) [TestSuiteProgress] Test failed: org.infinispan.container.versioning.TransactionalLocalWriteSkewTest.testSharedCounter java.util.concurrent.ExecutionException: java.lang.AssertionError: Duplicate value found (value=422) at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[?:?] at java.util.concurrent.FutureTask.get(FutureTask.java:191) ~[?:?] at org.infinispan.container.versioning.TransactionalLocalWriteSkewTest.testSharedCounter(TransactionalLocalWriteSkewTest.java:65) ~[test-classes/:?] Caused by: java.lang.AssertionError: Duplicate value found (value=422) at org.testng.AssertJUnit.fail(AssertJUnit.java:59) ~[testng-6.14.3.jar:?] at org.testng.AssertJUnit.assertTrue(AssertJUnit.java:24) ~[testng-6.14.3.jar:?] at org.infinispan.container.versioning.Test$IncrementCounterTask.run(TransactionalLocalWriteSkewTest.java:111) ~[test-classes/:?] at org.infinispan.test.AbstractInfinispanTest.lambda$fork$6(AbstractInfinispanTest.java:273) ~[test-classes/:?] at org.infinispan.test.AbstractInfinispanTest$CallableWrapper.call(AbstractInfinispanTest.java:538) ~[test-classes/:?]
- relates to
-
ISPN-4424 getCacheEntry is not safe
- Closed