[ 
https://issues.apache.org/jira/browse/IGNITE-16922?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17540101#comment-17540101
 ] 

Denis Chudov commented on IGNITE-16922:
---------------------------------------

This happens because Ignite stores TTL in row itself and needs to update row, 
and in-place update doesn't happen because of the condition in 
{{IgniteCacheOffheapManagerImpl.CacheDataStoreImpl#createRow}}:
{code:java}
            if (canUpdateOldRow(cctx, oldRow, dataRow) && 
rowStore.updateRow(oldRow.link(), dataRow, grp.statisticsHolderData()))
{code}
which return false and makes Ignite add new row first, and then remove old one.

> Getting an entry with expiry policy causes IgniteOutOfMemoryException
> ---------------------------------------------------------------------
>
>                 Key: IGNITE-16922
>                 URL: https://issues.apache.org/jira/browse/IGNITE-16922
>             Project: Ignite
>          Issue Type: Bug
>    Affects Versions: 2.13
>            Reporter: Alexey Kukushkin
>            Priority: Major
>              Labels: cggg
>   Original Estimate: 64h
>  Remaining Estimate: 64h
>
> {{IgniteCache#get(key)}} operation causes {{IgniteOutOfMemoryException}} if 
> {{AccessedExpiryPolicy}} or {{TouchedExpiryPolicy}} is enabled for the 
> {{key}} and Ignite has not enough storage for another entry of the same or 
> bigger size.
> This happens because:
> # Ignite needs to update TTL
> # TTL is part of the entry and Ignite overwrites full entry to update the TTL
> # The problem is Ignite runs common code that checks if Ignite has enough 
> storage to write the entry with updated TTL back. The check fails causing the 
> {{IgniteCache#get(key)}} operation to throw {{IgniteOutOfMemoryException}}.
> # This behavior is very confusing for Ignite users: why would a "read" 
> operation throw Ignite OOM?
> Can we update the TTL atomically and skip the storage size check?
> Please enhance Ignite not to throw Ignite OOM on {{get}}. 
> Stack trace:
> {code:java}
> [2022-05-20 
> 15:08:20,025][ERROR][sys-stripe-6-#8%ignite.IgniteOOMOnGet0%][IgniteTestResources]
>  Critical system error detected. Will be handled accordingly to configured 
> handler [hnd=NoOpFailureHandler [super=AbstractFailureHandler 
> [ignoredFailureTypes=UnmodifiableSet [SYSTEM_WORKER_BLOCKED, 
> SYSTEM_CRITICAL_OPERATION_TIMEOUT]]], failureCtx=FailureContext 
> [type=CRITICAL_ERROR, err=class o.a.i.i.mem.IgniteOutOfMemoryException: Out 
> of memory in data region [name=default, initSize=18.1 MiB, maxSize=18.1 MiB, 
> persistenceEnabled=false] Try the following:
>   ^-- Increase maximum off-heap memory size (DataRegionConfiguration.maxSize)
>   ^-- Enable Ignite persistence (DataRegionConfiguration.persistenceEnabled)
>   ^-- Enable eviction or expiration policies]]
> class org.apache.ignite.internal.mem.IgniteOutOfMemoryException: Out of 
> memory in data region [name=default, initSize=18.1 MiB, maxSize=18.1 MiB, 
> persistenceEnabled=false] Try the following:
>   ^-- Increase maximum off-heap memory size (DataRegionConfiguration.maxSize)
>   ^-- Enable Ignite persistence (DataRegionConfiguration.persistenceEnabled)
>   ^-- Enable eviction or expiration policies
>       at 
> org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager.ensureFreeSpaceForInsert(IgniteCacheDatabaseSharedManager.java:1234)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.RowStore.addRow(RowStore.java:108)
>       at 
> org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl.createRow(IgniteCacheOffheapManagerImpl.java:1962)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry$UpdateClosure.call(GridCacheMapEntry.java:5767)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry$UpdateClosure.call(GridCacheMapEntry.java:5695)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree$Invoke.invokeClosure(BPlusTree.java:4131)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.invokeDown(BPlusTree.java:2121)
>       at 
> org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree.invoke(BPlusTree.java:1997)
>       at 
> org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl.invoke0(IgniteCacheOffheapManagerImpl.java:1860)
>       at 
> org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl$CacheDataStoreImpl.invoke(IgniteCacheOffheapManagerImpl.java:1843)
>       at 
> org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl.invoke(IgniteCacheOffheapManagerImpl.java:471)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry.storeValue(GridCacheMapEntry.java:4164)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry.storeValue(GridCacheMapEntry.java:4140)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry.updateTtl(GridCacheMapEntry.java:2961)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry.updateTtl(GridCacheMapEntry.java:2934)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry.innerGet0(GridCacheMapEntry.java:825)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheMapEntry.innerGetVersioned(GridCacheMapEntry.java:704)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter.getAllAsync0(GridDhtCacheAdapter.java:851)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter.getDhtAllAsync(GridDhtCacheAdapter.java:691)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtGetSingleFuture.getAsync(GridDhtGetSingleFuture.java:413)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtGetSingleFuture.map0(GridDhtGetSingleFuture.java:289)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtGetSingleFuture.map(GridDhtGetSingleFuture.java:270)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtGetSingleFuture.init(GridDhtGetSingleFuture.java:186)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter.getDhtSingleAsync(GridDhtCacheAdapter.java:1156)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter.processNearSingleGetRequest(GridDhtCacheAdapter.java:1174)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache.access$200(GridDhtAtomicCache.java:151)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache$4.apply(GridDhtAtomicCache.java:278)
>       at 
> org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache$4.apply(GridDhtAtomicCache.java:273)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheIoManager.processMessage(GridCacheIoManager.java:1150)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheIoManager.onMessage0(GridCacheIoManager.java:591)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheIoManager.handleMessage(GridCacheIoManager.java:392)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheIoManager.handleMessage(GridCacheIoManager.java:318)
>       at 
> org.apache.ignite.internal.processors.cache.GridCacheIoManager$1.onMessage(GridCacheIoManager.java:308)
>       at 
> org.apache.ignite.internal.managers.communication.GridIoManager.invokeListener(GridIoManager.java:1727)
>       at 
> org.apache.ignite.internal.managers.communication.GridIoManager.processRegularMessage0(GridIoManager.java:1334)
>       at 
> org.apache.ignite.internal.managers.communication.GridIoManager$8.execute(GridIoManager.java:1218)
>       at 
> org.apache.ignite.internal.managers.communication.TraceRunnable.run(TraceRunnable.java:54)
>       at 
> org.apache.ignite.internal.util.StripedExecutor$Stripe.body(StripedExecutor.java:567)
>       at 
> org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:119)
>       at java.base/java.lang.Thread.run(Thread.java:833)
> {code}
> Reproducer:
> {code:java}
> package org.apache.ignite;
> import java.util.concurrent.ThreadLocalRandom;
> import java.util.concurrent.TimeUnit;
> import java.util.concurrent.atomic.AtomicBoolean;
> import java.util.concurrent.atomic.AtomicInteger;
> import javax.cache.expiry.Duration;
> import javax.cache.expiry.TouchedExpiryPolicy;
> import org.apache.ignite.configuration.CacheConfiguration;
> import org.apache.ignite.configuration.DataRegionConfiguration;
> import org.apache.ignite.configuration.DataStorageConfiguration;
> import org.apache.ignite.configuration.IgniteConfiguration;
> import org.apache.ignite.internal.IgniteInternalFuture;
> import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
> import org.junit.Test;
> import static java.lang.Math.max;
> import static 
> org.apache.ignite.testframework.GridTestUtils.runMultiThreadedAsync;
> public class IgniteOOMOnGet extends GridCommonAbstractTest {
>     @Override protected IgniteConfiguration getConfiguration(String 
> igniteInstanceName) throws Exception {
>         return super.getConfiguration(igniteInstanceName)
>             .setDataStorageConfiguration(
>                 new 
> DataStorageConfiguration().setDefaultDataRegionConfiguration(
>                     new DataRegionConfiguration().setMaxSize(19_000_000)
>                 )
>             );
>     }
>     @Test
>     public void test() throws Exception{
>         Ignite ignite = startGrid(0);
>         Ignite client = startClientGrid(1);
>         IgniteCache<Integer, byte[]> cache = client.createCache(
>             new CacheConfiguration<Integer, byte[]>(DEFAULT_CACHE_NAME)
>                 .setExpiryPolicyFactory(
>                     TouchedExpiryPolicy.factoryOf(new 
> Duration(TimeUnit.SECONDS, 100_000))
>                 )
>         );
>         AtomicInteger maxKey = new AtomicInteger();
>         for (int i = 0; i < 85; i++) {
>             cache.put(i, new byte[10_000]);
>             while (true) {
>                 int mk = maxKey.get();
>                 if (maxKey.compareAndSet(mk, max(mk, i)))
>                     break;
>             }
>         }
>         AtomicBoolean enabled = new AtomicBoolean(true);
>         IgniteInternalFuture fut = runMultiThreadedAsync(() -> {
>             ThreadLocalRandom rnd = ThreadLocalRandom.current();
>             while (enabled.get()) {
>                 cache.get(rnd.nextInt(maxKey.get()));
>             }
>         }, 8, "getterThread");
>         doSleep(10_000);
>         enabled.set(false);
>         fut.get();
>     }
> }
> {code}



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

Reply via email to