You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2020/01/24 13:17:43 UTC
[ignite] branch ignite-2.8 updated: IGNITE-12530 Pages list cache
limit added to prevent IgniteOOME on checkpoint - Fixes #7245.
This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch ignite-2.8
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/ignite-2.8 by this push:
new 3d38b55 IGNITE-12530 Pages list cache limit added to prevent IgniteOOME on checkpoint - Fixes #7245.
3d38b55 is described below
commit 3d38b550705e8b3409d72a4faf1e1a18ccc3972f
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Mon Jan 20 18:21:12 2020 +0300
IGNITE-12530 Pages list cache limit added to prevent IgniteOOME on checkpoint - Fixes #7245.
(cherry picked from commit 2a9239d20025d0f69003a33878f788952191f513)
---
.../processors/cache/mvcc/txlog/TxLog.java | 3 +-
.../GridCacheDatabaseSharedManager.java | 28 ++++++++
.../cache/persistence/GridCacheOffheapManager.java | 15 +++-
.../IgniteCacheDatabaseSharedManager.java | 3 +-
.../persistence/freelist/AbstractFreeList.java | 12 +++-
.../cache/persistence/freelist/CacheFreeList.java | 7 +-
.../cache/persistence/freelist/PagesList.java | 29 +++++---
.../cache/persistence/metastorage/MetaStorage.java | 3 +-
.../cache/persistence/pagemem/PageMemoryEx.java | 5 ++
.../cache/persistence/pagemem/PageMemoryImpl.java | 2 +-
.../partstorage/PartitionMetaStorageImpl.java | 6 +-
.../persistence/tree/reuse/ReuseListImpl.java | 8 ++-
.../db/checkpoint/CheckpointFreeListTest.java | 35 ++++++++++
.../persistence/freelist/FreeListCachingTest.java | 79 ++++++++++++++++++++--
.../database/BPlusTreeReuseSelfTest.java | 2 +-
.../processors/database/CacheFreeListSelfTest.java | 3 +-
16 files changed, 211 insertions(+), 29 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java
index 3d2a8fd..85d8fa5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/mvcc/txlog/TxLog.java
@@ -179,7 +179,8 @@ public class TxLog implements DbCheckpointListener {
reuseListRoot,
isNew,
txLogReuseListLockLsnr,
- ctx
+ ctx,
+ null
);
tree = new TxLogTree(
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
index 2eeb330..4dccd95 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
@@ -229,6 +229,19 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
/** MemoryPolicyConfiguration name reserved for meta store. */
public static final String METASTORE_DATA_REGION_NAME = "metastoreMemPlc";
+ /**
+ * Threshold to calculate limit for pages list on-heap caches.
+ * <p>
+ * Note: When a checkpoint is triggered, we need some amount of page memory to store pages list on-heap cache.
+ * If a checkpoint is triggered by "too many dirty pages" reason and pages list cache is rather big, we can get
+ * {@code IgniteOutOfMemoryException}. To prevent this, we can limit the total amount of cached page list buckets,
+ * assuming that checkpoint will be triggered if no more then 3/4 of pages will be marked as dirty (there will be
+ * at least 1/4 of clean pages) and each cached page list bucket can be stored to up to 2 pages (this value is not
+ * static, but depends on PagesCache.MAX_SIZE, so if PagesCache.MAX_SIZE > PagesListNodeIO#getCapacity it can take
+ * more than 2 pages). Also some amount of page memory needed to store page list metadata.
+ */
+ private static final double PAGE_LIST_CACHE_LIMIT_THRESHOLD = 0.1;
+
/** Skip sync. */
private final boolean skipSync = getBoolean(IGNITE_PDS_CHECKPOINT_TEST_SKIP_SYNC);
@@ -405,6 +418,9 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
/** Pointer to a memory recovery record that should be included into the next checkpoint record. */
private volatile WALPointer memoryRecoveryRecordPtr;
+ /** Page list cache limits per data region. */
+ private final Map<String, AtomicLong> pageListCacheLimits = new ConcurrentHashMap<>();
+
/**
* @param ctx Kernal context.
*/
@@ -3366,6 +3382,18 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
}
/**
+ * @return Holder for page list cache limit for given data region.
+ */
+ public AtomicLong pageListCacheLimitHolder(DataRegion dataRegion) {
+ if (dataRegion.config().isPersistenceEnabled()) {
+ return pageListCacheLimits.computeIfAbsent(dataRegion.config().getName(), name -> new AtomicLong(
+ (long)(((PageMemoryEx)dataRegion.pageMemory()).totalPages() * PAGE_LIST_CACHE_LIMIT_THRESHOLD)));
+ }
+
+ return null;
+ }
+
+ /**
* Partition destroy queue.
*/
private static class PartitionDestroyQueue {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
index b321321..af7998e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
@@ -30,6 +30,7 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
import javax.cache.processor.EntryProcessor;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
@@ -131,6 +132,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
/** */
private ReuseListImpl reuseList;
+ /** Page list cache limit for data region of this cache group. */
+ private AtomicLong pageListCacheLimit;
+
/** Flag indicates that all group partitions have restored their state from page memory / disk. */
private volatile boolean partitionStatesRestored;
@@ -152,6 +156,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
RootPage reuseListRoot = metas.reuseListRoot;
+ pageListCacheLimit = ((GridCacheDatabaseSharedManager)ctx.database()).pageListCacheLimitHolder(grp.dataRegion());
+
reuseList = new ReuseListImpl(
grp.groupId(),
grp.cacheOrGroupName(),
@@ -160,7 +166,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
reuseListRoot.pageId().pageId(),
reuseListRoot.isAllocated(),
diagnosticMgr.pageLockTracker().createPageLockTracker(reuseListName),
- ctx.kernalContext()
+ ctx.kernalContext(),
+ pageListCacheLimit
);
RootPage metastoreRoot = metas.treeRoot;
@@ -1694,7 +1701,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
reuseRoot.pageId().pageId(),
reuseRoot.isAllocated(),
ctx.diagnostic().pageLockTracker().createPageLockTracker(freeListName),
- ctx.kernalContext()
+ ctx.kernalContext(),
+ pageListCacheLimit
) {
/** {@inheritDoc} */
@Override protected long allocatePageNoReuse() throws IgniteCheckedException {
@@ -1718,7 +1726,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
partMetastoreReuseListRoot.pageId().pageId(),
partMetastoreReuseListRoot.isAllocated(),
ctx.diagnostic().pageLockTracker().createPageLockTracker(partitionMetaStoreName),
- ctx.kernalContext()
+ ctx.kernalContext(),
+ pageListCacheLimit
) {
/** {@inheritDoc} */
@Override protected long allocatePageNoReuse() throws IgniteCheckedException {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
index a1a7913..52c14e6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
@@ -268,7 +268,8 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
0L,
true,
lsnr,
- cctx.kernalContext()
+ cctx.kernalContext(),
+ null
);
freeListMap.put(memPlcCfg.getName(), freeList);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
index 10fa687..b46417b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist;
import java.util.Arrays;
import java.util.Collection;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
@@ -94,6 +95,9 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
/** */
private final PageEvictionTracker evictionTracker;
+ /** Page list cache limit. */
+ private final AtomicLong pageListCacheLimit;
+
/**
*
*/
@@ -433,7 +437,8 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
long metaPageId,
boolean initNew,
PageLockListener lockLsnr,
- GridKernalContext ctx
+ GridKernalContext ctx,
+ AtomicLong pageListCacheLimit
) throws IgniteCheckedException {
super(cacheId, name, memPlc.pageMemory(), BUCKETS, wal, metaPageId, lockLsnr, ctx);
@@ -462,6 +467,8 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
this.memMetrics = memMetrics;
+ this.pageListCacheLimit = pageListCacheLimit;
+
init(metaPageId, initNew);
}
@@ -860,7 +867,8 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
@Override protected PagesCache getBucketCache(int bucket, boolean create) {
PagesCache pagesCache = bucketCaches.get(bucket);
- if (pagesCache == null && create && !bucketCaches.compareAndSet(bucket, null, pagesCache = new PagesCache()))
+ if (pagesCache == null && create &&
+ !bucketCaches.compareAndSet(bucket, null, pagesCache = new PagesCache(pageListCacheLimit)))
pagesCache = bucketCaches.get(bucket);
return pagesCache;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeList.java
index 3f7051f..a4a4363 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeList.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.cache.persistence.freelist;
+import java.util.concurrent.atomic.AtomicLong;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.metric.IoStatisticsHolder;
@@ -50,7 +51,8 @@ public class CacheFreeList extends AbstractFreeList<CacheDataRow> {
long metaPageId,
boolean initNew,
PageLockListener lockLsnr,
- GridKernalContext ctx
+ GridKernalContext ctx,
+ AtomicLong pageListCacheLimit
) throws IgniteCheckedException {
super(
cacheId,
@@ -62,7 +64,8 @@ public class CacheFreeList extends AbstractFreeList<CacheDataRow> {
metaPageId,
initNew,
lockLsnr,
- ctx
+ ctx,
+ pageListCacheLimit
);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java
index fc93fef..70e19d7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicLongArray;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
@@ -1911,7 +1912,6 @@ public abstract class PagesList extends DataStructure {
}
/** Class to store page-list cache onheap. */
- @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
public static class PagesCache {
/** Pages cache max size. */
private static final int MAX_SIZE =
@@ -1948,14 +1948,19 @@ public abstract class PagesList extends DataStructure {
/** Count of flush calls with empty cache. */
private int emptyFlushCnt;
+ /** Global (per data region) limit of caches for page lists. */
+ private final AtomicLong pagesCacheLimit;
+
/**
* Default constructor.
*/
- public PagesCache() {
+ public PagesCache(@Nullable AtomicLong pagesCacheLimit) {
assert U.isPow2(STRIPES_COUNT) : STRIPES_COUNT;
for (int i = 0; i < STRIPES_COUNT; i++)
stripeLocks[i] = new Object();
+
+ this.pagesCacheLimit = pagesCacheLimit;
}
/**
@@ -1972,8 +1977,10 @@ public abstract class PagesList extends DataStructure {
boolean rmvd = stripe != null && stripe.removeValue(0, pageId) >= 0;
- if (rmvd)
- sizeUpdater.decrementAndGet(this);
+ if (rmvd) {
+ if (sizeUpdater.decrementAndGet(this) == 0 && pagesCacheLimit != null)
+ pagesCacheLimit.incrementAndGet();
+ }
return rmvd;
}
@@ -1995,7 +2002,8 @@ public abstract class PagesList extends DataStructure {
GridLongList stripe = stripes[stripeIdx];
if (stripe != null && !stripe.isEmpty()) {
- sizeUpdater.decrementAndGet(this);
+ if (sizeUpdater.decrementAndGet(this) == 0 && pagesCacheLimit != null)
+ pagesCacheLimit.incrementAndGet();
return stripe.remove();
}
@@ -2008,7 +2016,6 @@ public abstract class PagesList extends DataStructure {
/**
* Flush all stripes to one list and clear stripes.
*/
- @SuppressWarnings("NonAtomicOperationOnVolatileField")
public GridLongList flush() {
GridLongList res = null;
@@ -2048,7 +2055,8 @@ public abstract class PagesList extends DataStructure {
if (res == null)
res = new GridLongList(size);
- sizeUpdater.addAndGet(this, -stripe.size());
+ if (sizeUpdater.addAndGet(this, -stripe.size()) == 0 && pagesCacheLimit != null)
+ pagesCacheLimit.incrementAndGet();
res.addAll(stripe);
@@ -2070,6 +2078,10 @@ public abstract class PagesList extends DataStructure {
assert pageId != 0L;
// Ok with race here.
+ if (size == 0 && pagesCacheLimit != null && pagesCacheLimit.get() <= 0)
+ return false; // Pages cache limit exceeded.
+
+ // Ok with race here.
if (size >= MAX_SIZE)
return false;
@@ -2086,7 +2098,8 @@ public abstract class PagesList extends DataStructure {
else {
stripe.add(pageId);
- sizeUpdater.incrementAndGet(this);
+ if (sizeUpdater.getAndIncrement(this) == 0 && pagesCacheLimit != null)
+ pagesCacheLimit.decrementAndGet();
return true;
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java
index f437126..670992c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java
@@ -269,7 +269,8 @@ public class MetaStorage implements DbCheckpointListener, ReadWriteMetastorage {
reuseListRoot.pageId().pageId(),
reuseListRoot.isAllocated(),
diagnosticMgr.pageLockTracker().createPageLockTracker(freeListName),
- cctx.kernalContext()
+ cctx.kernalContext(),
+ null
) {
@Override protected long allocatePageNoReuse() throws IgniteCheckedException {
return pageMem.allocatePage(grpId, partId, FLAG_DATA);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryEx.java
index f9fdb0d..5d104ae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryEx.java
@@ -165,4 +165,9 @@ public interface PageMemoryEx extends PageMemory {
* @return Future that will be completed when all pages are cleared.
*/
public IgniteInternalFuture<Void> clearAsync(LoadedPagesMap.KeyPredicate pred, boolean cleanDirty);
+
+ /**
+ * Total pages can be placed to memory.
+ */
+ public long totalPages();
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java
index 7adf1c5..bef3316 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java
@@ -1113,7 +1113,7 @@ public class PageMemoryImpl implements PageMemoryEx {
/**
* @return Total pages can be placed in all segments.
*/
- public long totalPages() {
+ @Override public long totalPages() {
if (segments == null)
return 0;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorageImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorageImpl.java
index db1c796..0e9062a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorageImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorageImpl.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.processors.cache.persistence.partstorage;
import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicLong;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.pagemem.PageUtils;
@@ -58,9 +59,10 @@ public class PartitionMetaStorageImpl<T extends Storable> extends AbstractFreeLi
long metaPageId,
boolean initNew,
PageLockListener lsnr,
- GridKernalContext ctx
+ GridKernalContext ctx,
+ AtomicLong pageListCacheLimit
) throws IgniteCheckedException {
- super(cacheId, name, memMetrics, memPlc, reuseList, wal, metaPageId, initNew, lsnr, ctx);
+ super(cacheId, name, memMetrics, memPlc, reuseList, wal, metaPageId, initNew, lsnr, ctx, pageListCacheLimit);
}
/**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/reuse/ReuseListImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/reuse/ReuseListImpl.java
index 088a6ee..a59b308 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/reuse/ReuseListImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/reuse/ReuseListImpl.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.cache.persistence.tree.reuse;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.GridKernalContext;
@@ -38,7 +39,7 @@ public class ReuseListImpl extends PagesList implements ReuseList {
private volatile Stripe[] bucket;
/** Onheap pages cache. */
- private final PagesCache bucketCache = new PagesCache();
+ private final PagesCache bucketCache;
/**
* @param cacheId Cache ID.
@@ -57,7 +58,8 @@ public class ReuseListImpl extends PagesList implements ReuseList {
long metaPageId,
boolean initNew,
PageLockListener lockLsnr,
- GridKernalContext ctx
+ GridKernalContext ctx,
+ AtomicLong pageListCacheLimit
) throws IgniteCheckedException {
super(
cacheId,
@@ -70,6 +72,8 @@ public class ReuseListImpl extends PagesList implements ReuseList {
ctx
);
+ bucketCache = new PagesCache(pageListCacheLimit);
+
reuseList = this;
init(metaPageId, initNew);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/checkpoint/CheckpointFreeListTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/checkpoint/CheckpointFreeListTest.java
index 100a959..f3d3842 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/checkpoint/CheckpointFreeListTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/db/checkpoint/CheckpointFreeListTest.java
@@ -33,12 +33,14 @@ import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CacheMode;
@@ -51,6 +53,8 @@ import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.IgniteKernal;
import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
+import org.apache.ignite.internal.processors.cache.persistence.DbCheckpointListener;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cache.persistence.GridCacheOffheapManager;
import org.apache.ignite.internal.processors.cache.persistence.freelist.PagesList;
import org.apache.ignite.internal.util.typedef.T2;
@@ -334,6 +338,37 @@ public class CheckpointFreeListTest extends GridCommonAbstractTest {
AtomicBoolean done = new AtomicBoolean();
AtomicReference<Throwable> error = new AtomicReference<>();
+ GridCacheDatabaseSharedManager db = (GridCacheDatabaseSharedManager)ignite.context().cache().context().database();
+
+ AtomicLong pageListCacheLimitHolder = db.pageListCacheLimitHolder(ignite.context().cache()
+ .cache(DEFAULT_CACHE_NAME).context().dataRegion());
+
+ long initPageListCacheLimit = pageListCacheLimitHolder.get();
+
+ // Add listener after cache is started, so this listener will be triggered after listener for cache.
+ db.addCheckpointListener(new DbCheckpointListener() {
+ @Override public void onMarkCheckpointBegin(Context ctx) throws IgniteCheckedException {
+ // Check under checkpoint write lock that page list cache limit is correctly restored.
+ // Need to wait for condition here, since checkpointer can store free-list metadata asynchronously.
+ if (!waitForCondition(() -> initPageListCacheLimit == pageListCacheLimitHolder.get(), 1_000L)) {
+ IgniteCheckedException e = new IgniteCheckedException("Page list cache limit doesn't restored " +
+ "correctly [init=" + initPageListCacheLimit + ", cur=" + pageListCacheLimitHolder.get() + ']');
+
+ error.set(e);
+
+ throw e;
+ }
+ }
+
+ @Override public void onCheckpointBegin(Context ctx) {
+ // No-op.
+ }
+
+ @Override public void beforeCheckpointBegin(Context ctx) {
+ // No-op.
+ }
+ });
+
IgniteInternalFuture<Long> fut = GridTestUtils.runMultiThreadedAsync(() -> {
Random rnd = new Random();
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListCachingTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListCachingTest.java
index 0ca2b11..47d777e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListCachingTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeListCachingTest.java
@@ -21,8 +21,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLongArray;
import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteDataStreamer;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.configuration.CacheConfiguration;
@@ -30,9 +32,11 @@ import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cache.persistence.GridCacheOffheapManager;
+import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
@@ -64,11 +68,15 @@ public class FreeListCachingTest extends GridCommonAbstractTest {
cfg.setConsistentId(igniteInstanceName);
- cfg.setDataStorageConfiguration(new DataStorageConfiguration()
- .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
+ DataStorageConfiguration dsCfg = new DataStorageConfiguration();
+
+ int pageSize = dsCfg.getPageSize() == 0 ? DataStorageConfiguration.DFLT_PAGE_SIZE : dsCfg.getPageSize();
+
+ dsCfg.setDefaultDataRegionConfiguration(new DataRegionConfiguration()
.setPersistenceEnabled(true)
- .setMaxSize(300L * 1024 * 1024)
- ));
+ .setMaxSize(pageSize * 40_000L));
+
+ cfg.setDataStorageConfiguration(dsCfg);
return cfg;
}
@@ -215,4 +223,67 @@ public class FreeListCachingTest extends GridCommonAbstractTest {
assertTrue("Some buckets should be cached [partId=" + cacheData.partId() + ']', totalCacheSize > 0);
});
}
+
+ /**
+ * @throws Exception If test failed.
+ */
+ @Test
+ public void testPageListCacheLimit() throws Exception {
+ IgniteEx ignite = startGrid(0);
+
+ ignite.cluster().active(true);
+
+ ignite.getOrCreateCache("cache1");
+ ignite.getOrCreateCache("cache2");
+
+ GridCacheContext<?, ?> cctx1 = ignite.context().cache().cache("cache1").context();
+ GridCacheContext<?, ?> cctx2 = ignite.context().cache().cache("cache2").context();
+
+ GridCacheOffheapManager offheap1 = (GridCacheOffheapManager)cctx1.offheap();
+ GridCacheOffheapManager offheap2 = (GridCacheOffheapManager)cctx2.offheap();
+
+ GridCacheDatabaseSharedManager db = (GridCacheDatabaseSharedManager)ignite.context().cache().context().database();
+
+ assertEquals(db.pageListCacheLimitHolder(cctx1.dataRegion()), db.pageListCacheLimitHolder(cctx2.dataRegion()));
+
+ long limit = db.pageListCacheLimitHolder(cctx1.dataRegion()).get();
+
+ try (IgniteDataStreamer<Object, Object> streamer1 = ignite.dataStreamer("cache1");
+ IgniteDataStreamer<Object, Object> streamer2 = ignite.dataStreamer("cache2")) {
+ // Fill caches to trigger "too many dirty pages" checkpoint.
+ for (int i = 0; i < 50_000; i++) {
+ streamer1.addData(i, new byte[i % 2048]);
+ streamer2.addData(i, new byte[i % 2048]);
+
+ // Calculates page list caches count and validate this value periodically.
+ if (i % 5_000 == 0) {
+ streamer1.flush();
+ streamer2.flush();
+
+ AtomicInteger pageCachesCnt = new AtomicInteger();
+
+ for (GridCacheOffheapManager offheap : F.asList(offheap1, offheap2)) {
+ offheap.cacheDataStores().forEach(cacheData -> {
+ if (cacheData.rowStore() == null)
+ return;
+
+ PagesList list = (PagesList)cacheData.rowStore().freeList();
+
+ for (int b = 0; b < list.bucketsSize.length(); b++) {
+ PagesList.PagesCache pagesCache = list.getBucketCache(b, false);
+
+ if (pagesCache != null && pagesCache.size() > 0)
+ pageCachesCnt.incrementAndGet();
+ }
+ });
+ }
+
+ // There can be a race and actual page list caches count can exceed the limit in very rare cases.
+ assertTrue("Page list caches count is more than expected [count: " + pageCachesCnt.get() +
+ ", limit=" + limit + ']', pageCachesCnt.get() <= limit + ignite.configuration()
+ .getDataStreamerThreadPoolSize() - 1);
+ }
+ }
+ }
+ }
}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReuseSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReuseSelfTest.java
index 2c7d514..8d50fad 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReuseSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeReuseSelfTest.java
@@ -83,7 +83,7 @@ public class BPlusTreeReuseSelfTest extends BPlusTreeSelfTest {
boolean initNew,
GridKernalContext ctx
) throws IgniteCheckedException {
- super(cacheId, name, pageMem, wal, metaPageId, initNew, new TestPageLockListener(), ctx);
+ super(cacheId, name, pageMem, wal, metaPageId, initNew, new TestPageLockListener(), ctx, null);
}
/**
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListSelfTest.java
index e4a8c9b..68f1668 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/CacheFreeListSelfTest.java
@@ -529,7 +529,8 @@ public class CacheFreeListSelfTest extends GridCommonAbstractTest {
metaPageId,
true,
null,
- new GridTestKernalContext(log)
+ new GridTestKernalContext(log),
+ null
);
}