Uploaded image for project: 'Ignite'
  1. Ignite
  2. IGNITE-17742

Trying to insert a large entry in an atomic cache results in CorruptedTreeException

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Major
    • Resolution: Fixed
    • 2.13
    • 2.15
    • cache
    • None
    • Fixed an issue that could lead to data corruption of atomic cache when a new updated entry is greater than WAL buffer size.
    • Release Notes Required

    Description

      In case of using Ignite Native Persistence, the attempt to insert a new entry which size is greater than WAL buffer size leads to CorruptedTreeException:

       org.apache.ignite.internal.processors.cache.persistence.tree.CorruptedTreeException: B+Tree is corrupted [groupId=-1407396309, pageIds=[1125904201809926], msg=Runtime failure on search row: SearchRow [key=KeyCacheObjectImpl [part=1, val=1, hasValBytes=true], hash=1, cacheId=0]]
      	at org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.corruptedTreeException(BPlusTree.java:6487) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.invoke(BPlusTree.java:2202) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl.invoke0(IgniteCacheOffheapManagerImpl.java:1698) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl.invoke(IgniteCacheOffheapManagerImpl.java:1681) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.GridCacheOffheapManager$GridCacheDataStore.invoke(GridCacheOffheapManager.java:2762) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl.invoke(IgniteCacheOffheapManagerImpl.java:425) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GridCacheMapEntry.innerUpdate(GridCacheMapEntry.java:1975) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.updateSingle(GridDhtAtomicCache.java:2554) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.update(GridDhtAtomicCache.java:2014) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.updateAllAsyncInternal0(GridDhtAtomicCache.java:1833) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.updateAllAsyncInternal(GridDhtAtomicCache.java:1706) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridNearAtomicAbstractUpdateFuture.sendSingleRequest(GridNearAtomicAbstractUpdateFuture.java:300) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridNearAtomicSingleUpdateFuture.map(GridNearAtomicSingleUpdateFuture.java:481) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridNearAtomicSingleUpdateFuture.mapOnTopology(GridNearAtomicSingleUpdateFuture.java:441) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridNearAtomicAbstractUpdateFuture.map(GridNearAtomicAbstractUpdateFuture.java:249) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.update0(GridDhtAtomicCache.java:1151) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.put0(GridDhtAtomicCache.java:619) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GridCacheAdapter.put(GridCacheAdapter.java:2487) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GridCacheAdapter.put(GridCacheAdapter.java:2466) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl.put(IgniteCacheProxyImpl.java:1332) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GatewayProtectedCacheProxy.put(GatewayProtectedCacheProxy.java:867) ~[classes/:?]
      	at org.apache.ignite.internal.processors.database.IgniteDbPutGetAbstractTest.lambda$testPutLargeEntry$4bb74d57$1(IgniteDbPutGetAbstractTest.java:331) ~[test-classes/:?]
      	at org.apache.ignite.internal.util.lang.RunnableX.run(RunnableX.java:37) ~[classes/:?]
      	at org.apache.ignite.testframework.GridTestUtils.lambda$assertThrows$0(GridTestUtils.java:467) ~[test-classes/:?]
      	at org.apache.ignite.testframework.GridTestUtils.assertThrows(GridTestUtils.java:505) ~[test-classes/:?]
      	at org.apache.ignite.testframework.GridTestUtils.assertThrows(GridTestUtils.java:485) ~[test-classes/:?]
      	at org.apache.ignite.testframework.GridTestUtils.assertThrows(GridTestUtils.java:466) ~[test-classes/:?]
      	at org.apache.ignite.internal.processors.database.IgniteDbPutGetAbstractTest.testPutLargeEntry(IgniteDbPutGetAbstractTest.java:329) ~[test-classes/:?]
      	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_341]
      	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_341]
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_341]
      	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_341]
      	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) ~[junit-4.12.jar:4.12]
      	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) ~[junit-4.12.jar:4.12]
      	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) ~[junit-4.12.jar:4.12]
      	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) ~[junit-4.12.jar:4.12]
      	at org.apache.ignite.testframework.junits.GridAbstractTest$6.run(GridAbstractTest.java:2445) ~[test-classes/:?]
      	at java.lang.Thread.run(Thread.java:750) ~[?:1.8.0_341]
      Caused by: java.lang.IllegalArgumentException: Record is too long [capacity=16777216, size=33554524]
      	at org.apache.ignite.internal.processors.cache.persistence.wal.SegmentedRingByteBuffer.offer0(SegmentedRingByteBuffer.java:219) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.wal.SegmentedRingByteBuffer.offer(SegmentedRingByteBuffer.java:195) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.wal.filehandle.FileWriteHandleImpl.addRecord(FileWriteHandleImpl.java:234) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager.log(FileWriteAheadLogManager.java:935) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager.log(FileWriteAheadLogManager.java:888) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GridCacheMapEntry.logUpdate(GridCacheMapEntry.java:4006) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GridCacheMapEntry$AtomicCacheUpdateClosure.update(GridCacheMapEntry.java:6172) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GridCacheMapEntry$AtomicCacheUpdateClosure.call(GridCacheMapEntry.java:5918) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.GridCacheMapEntry$AtomicCacheUpdateClosure.call(GridCacheMapEntry.java:5603) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$Invoke.invokeClosure(BPlusTree.java:4307) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$Invoke.access$5700(BPlusTree.java:4201) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.invokeDown(BPlusTree.java:2279) ~[classes/:?]
      	at org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.invoke(BPlusTree.java:2169) ~[classes/:?]
      	... 36 more
      
      

      The root cause is that the new data record is greater than the WAL buffer size, and the IllegalArgumentException exception is not properly handled.

      The issue can be solved in the following way:

      • if the primary node cannot write a new WAL record (DataRecord actually), it should throw CacheException to a user. Data must be consistent, and neither the primary node, nor the backup node must apply the new update.
      • if the primary node successfully wrote a new record to the WAL, but the backup node is unable to write the corresponding record (due to a different WAL buffer size configuration, for example) than the backup node must trigger a configured failure handler to avoid data inconsistencies.

      Attachments

        Issue Links

          Activity

            People

              slava.koptilin Vyacheslav Koptilin
              slava.koptilin Vyacheslav Koptilin
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0h
                  0h
                  Logged:
                  Time Spent - 20m
                  20m