[ 
https://issues.apache.org/jira/browse/IGNITE-16137?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Alexey Kukushkin updated IGNITE-16137:
--------------------------------------
    Remaining Estimate: 64h  (was: 40h)
     Original Estimate: 64h  (was: 40h)

> ContainsKey operation fetches entry value into heap
> ---------------------------------------------------
>
>                 Key: IGNITE-16137
>                 URL: https://issues.apache.org/jira/browse/IGNITE-16137
>             Project: Ignite
>          Issue Type: Improvement
>          Components: cache
>    Affects Versions: 2.11
>            Reporter: Alexey Kukushkin
>            Priority: Major
>              Labels: cggg
>   Original Estimate: 64h
>  Remaining Estimate: 64h
>
> [See similar problem for the remove() 
> operation|https://issues.apache.org/jira/browse/IGNITE-15959]
> {{IgniteCache#containsKey(key)}} operation fetches full entry into heap 
> memory. This is inefficient when working with large objects: our application 
> running with limited heap memory fails with {{java.lang.OutOfMemoryError: 
> Java heap space}} when trying to check if a key exists.
> It seems wrong that Ignite needs to fetch the full entry on heap to check if 
> the key exists. Please enhance Ignite to not be doing that or explain why 
> Ignite must do that.
> h2. Reproducer
> h3. Steps
> Create a Gradle project with the below class and run it as
> {{./gradlew test --tests apache.ignite.issues.ContainsOperationHeapUsage}}
> {{build.gradle}}
> {code:groovy}
> test {
>     minHeapSize = "512m"
>     maxHeapSize = "512m"
> }
> {code}
> {{ContainsOperationHeapUsage.java}}
> {code:java}
> public class ContainsOperationHeapUsage {
>     /** Run with -Xmx512m -Xms512m */
>     @Test
>     public void containsOperationFetchesValueOnHeap() {
>         var igniteCfg = new IgniteConfiguration()
>             .setDiscoverySpi(
>                 new TcpDiscoverySpi()
>                     .setIpFinder(new 
> TcpDiscoveryVmIpFinder().setAddresses(Collections.singleton("127.0.0.1:47500")))
>             )
>             .setCacheConfiguration(new CacheConfiguration<>("blobs"));
>         try (var ignite = Ignition.start(igniteCfg)) {
>             Cache<Integer, byte[]> cache = ignite.cache("blobs");
>             // Put a BLOB having size of 35% of free memory to the cache
>             Runtime.getRuntime().gc();
>             var freeMemory = Runtime.getRuntime().freeMemory();
>             var blobSize = (int)(freeMemory * 0.35);
>             putBlob(cache, blobSize);
>             // Use 70% of the free heap
>             Runtime.getRuntime().gc();
>             var unused = new byte[2 * blobSize];
>             // Check if the blob exists in the cache.
>             // This throws "OutOfMemoryError: Java heap space" since Ignite 
> retrieves full entry to the heap.
>             // Why does Ignite retrieve entry value to check if the key 
> exists?
>             cache.containsKey(1);
>         }
>     }
>     private static void putBlob(Cache<Integer, byte[]> cache, int blobSize) {
>         var blob = new byte[blobSize];
>         cache.put(1, blob);
>     }
> }
> {code}
> h3. Expected
> The test passes
> h3. Actual
> The {{cache.containsKey}} operatoin fails with:
> {noformat}
> java.lang.OutOfMemoryError: Java heap space
>       at 
> org.apache.ignite.internal.processors.cache.IncompleteCacheObject.<init>(IncompleteCacheObject.java:44)
>       at 
> org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl.toCacheObject(CacheObjectBinaryProcessorImpl.java:1385)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.readIncompleteValue(CacheDataRowAdapter.java:680)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.readFragment(CacheDataRowAdapter.java:500)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.readIncomplete(CacheDataRowAdapter.java:411)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.doInitFromLink(CacheDataRowAdapter.java:316)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.initFromLink(CacheDataRowAdapter.java:165)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter.initFromLink(CacheDataRowAdapter.java:136)
>       at 
> org.apache.ignite.internal.processors.cache.tree.DataRow.<init>(DataRow.java:55)
>       at 
> org.apache.ignite.internal.processors.cache.tree.CacheDataRowStore.dataRow(CacheDataRowStore.java:129)
>       at 
> org.apache.ignite.internal.processors.cache.tree.CacheDataTree.getRow(CacheDataTree.java:422)
>       at 
> org.apache.ignite.internal.processors.cache.tree.CacheDataTree.getRow(CacheDataTree.java:63)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$GetOne.found(BPlusTree.java:3156)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$Search.run0(BPlusTree.java:317)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$GetPageHandler.run(BPlusTree.java:5921)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$Search.run(BPlusTree.java:290)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$GetPageHandler.run(BPlusTree.java:5907)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler.readPage(PageHandler.java:174)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.DataStructure.read(DataStructure.java:397)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.read(BPlusTree.java:6108)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.findDown(BPlusTree.java:1446)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.doFind(BPlusTree.java:1413)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.findOne(BPlusTree.java:1379)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.findOne(BPlusTree.java:1364)
>       at 
> org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl.find(IgniteCacheOffheapManagerImpl.java:2815)
>       at 
> org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl.read(IgniteCacheOffheapManagerImpl.java:633)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridPartitionedSingleGetFuture.localGet(GridPartitionedSingleGetFuture.java:483)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridPartitionedSingleGetFuture.tryLocalGet(GridPartitionedSingleGetFuture.java:445)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridPartitionedSingleGetFuture.mapKeyToNode(GridPartitionedSingleGetFuture.java:409)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridPartitionedSingleGetFuture.map(GridPartitionedSingleGetFuture.java:284)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridPartitionedSingleGetFuture.init(GridPartitionedSingleGetFuture.java:248)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.getAsync0(GridDhtAtomicCache.java:1456)
> {noformat}



--
This message was sent by Atlassian Jira
(v8.20.7#820007)

Reply via email to