From 47885229fc4fe8c83cabc08111eb8322db1b98d5 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 27 May 2019 13:48:32 +0300 Subject: [PATCH 01/21] IGNITE-11584 Batch write into page memory. --- .../processors/cache/GridCacheEntryEx.java | 39 +- .../processors/cache/GridCacheMapEntry.java | 29 +- .../cache/IgniteCacheOffheapManager.java | 25 ++ .../cache/IgniteCacheOffheapManagerImpl.java | 57 +++ .../preloader/GridDhtPartitionDemander.java | 167 ++++++++- .../persistence/GridCacheOffheapManager.java | 14 + .../cache/persistence/RowStore.java | 11 + .../freelist/AbstractFreeList.java | 276 +++++++++++--- .../cache/persistence/freelist/FreeList.java | 7 + .../persistence/tree/util/PageHandler.java | 37 +- .../cache/GridCacheTestEntryEx.java | 3 +- .../rebalancing/PreloadingFlowTest.java | 351 ++++++++++++++++++ .../MemoryLeakAfterRebalanceSelfTest.java | 217 +++++++++++ .../database/CacheFreeListSelfTest.java | 86 ++++- .../testsuites/IgnitePdsMvccTestSuite.java | 3 + .../ignite/testsuites/IgnitePdsTestSuite.java | 3 + 16 files changed, 1249 insertions(+), 76 deletions(-) create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java index 9aec3996c3204..71702edf87ca2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java @@ -792,6 +792,42 @@ default boolean initialValue(CacheObject val, * @throws IgniteCheckedException In case of error. * @throws GridCacheEntryRemovedException If entry was removed. */ + default boolean initialValue(CacheObject val, + GridCacheVersion ver, + @Nullable MvccVersion mvccVer, + @Nullable MvccVersion newMvccVer, + byte mvccTxState, + byte newMvccTxState, + long ttl, + long expireTime, + boolean preload, + AffinityTopologyVersion topVer, + GridDrType drType, + boolean fromStore) throws IgniteCheckedException, GridCacheEntryRemovedException { + return initialValue(val, ver, null, null, TxState.NA, TxState.NA, + ttl, expireTime, preload, topVer, drType, fromStore, null); + } + + /** + * Sets new value if current version is 0 + * + * @param val New value. + * @param ver Version to use. + * @param mvccVer Mvcc version. + * @param newMvccVer New mvcc version. + * @param mvccTxState Tx state hint for mvcc version. + * @param newMvccTxState Tx state hint for new mvcc version. + * @param ttl Time to live. + * @param expireTime Expiration time. + * @param preload Flag indicating whether entry is being preloaded. + * @param topVer Topology version. + * @param drType DR type. + * @param fromStore {@code True} if value was loaded from store. + * @param row Pre-created data row, associated with this cache entry. + * @return {@code True} if initial value was set. + * @throws IgniteCheckedException In case of error. + * @throws GridCacheEntryRemovedException If entry was removed. + */ public boolean initialValue(CacheObject val, GridCacheVersion ver, @Nullable MvccVersion mvccVer, @@ -803,7 +839,8 @@ public boolean initialValue(CacheObject val, boolean preload, AffinityTopologyVersion topVer, GridDrType drType, - boolean fromStore) throws IgniteCheckedException, GridCacheEntryRemovedException; + boolean fromStore, + @Nullable CacheDataRow row) throws IgniteCheckedException, GridCacheEntryRemovedException; /** * Create versioned entry for this cache entry. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java index f1b7ec76b2152..02cc42b0dfe05 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java @@ -3310,7 +3310,8 @@ private boolean skipInterceptor(@Nullable GridCacheVersion explicitVer) { boolean preload, AffinityTopologyVersion topVer, GridDrType drType, - boolean fromStore + boolean fromStore, + CacheDataRow row ) throws IgniteCheckedException, GridCacheEntryRemovedException { ensureFreeSpace(); @@ -3386,7 +3387,7 @@ else if (val == null) cctx.offheap().mvccInitialValue(this, val, ver, expTime, mvccVer, newMvccVer); } else - storeValue(val, expTime, ver); + storeValue(val, expTime, ver, null, row); } } else { @@ -3417,7 +3418,7 @@ else if (val == null) } else // Optimization to access storage only once. - update = storeValue(val, expTime, ver, p); + update = storeValue(val, expTime, ver, p, row); } if (update) { @@ -4257,7 +4258,7 @@ private IgniteTxLocalAdapter currentTx() { protected boolean storeValue(@Nullable CacheObject val, long expireTime, GridCacheVersion ver) throws IgniteCheckedException { - return storeValue(val, expireTime, ver, null); + return storeValue(val, expireTime, ver, null, null); } /** @@ -4267,6 +4268,7 @@ protected boolean storeValue(@Nullable CacheObject val, * @param expireTime Expire time. * @param ver New entry version. * @param predicate Optional predicate. + * @param row Pre-created data row, associated with this cache entry. * @return {@code True} if storage was modified. * @throws IgniteCheckedException If update failed. */ @@ -4274,10 +4276,12 @@ protected boolean storeValue( @Nullable CacheObject val, long expireTime, GridCacheVersion ver, - @Nullable IgnitePredicate predicate) throws IgniteCheckedException { + @Nullable IgnitePredicate predicate, + @Nullable CacheDataRow row + ) throws IgniteCheckedException { assert lock.isHeldByCurrentThread(); - UpdateClosure closure = new UpdateClosure(this, val, ver, expireTime, predicate); + UpdateClosure closure = new UpdateClosure(this, val, ver, expireTime, predicate, row); cctx.offheap().invoke(cctx, key, localPartition(), closure); @@ -5716,16 +5720,19 @@ private static class UpdateClosure implements IgniteCacheOffheapManager.OffheapI * @param predicate Optional predicate. */ UpdateClosure(GridCacheMapEntry entry, @Nullable CacheObject val, GridCacheVersion ver, long expireTime, - @Nullable IgnitePredicate predicate) { + @Nullable IgnitePredicate predicate, @Nullable CacheDataRow newRow) { this.entry = entry; this.val = val; this.ver = ver; this.expireTime = expireTime; this.predicate = predicate; + this.newRow = newRow; } /** {@inheritDoc} */ @Override public void call(@Nullable CacheDataRow oldRow) throws IgniteCheckedException { + assert newRow == null || val != null; + if (oldRow != null) { oldRow.key(entry.key); @@ -5741,6 +5748,14 @@ private static class UpdateClosure implements IgniteCacheOffheapManager.OffheapI } if (val != null) { + // If there is a pre created row, we cannot update the old one. + // The old row will be removed after the operation is completed, as usual. + if (newRow != null) { + treeOp = IgniteTree.OperationType.PUT; + + return; + } + newRow = entry.cctx.offheap().dataStore(entry.localPartition()).createRow( entry.cctx, entry.key, diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index ab8d338e46b05..507bbc96c46cd 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache; +import java.util.Collection; import java.util.List; import java.util.Map; import javax.cache.Cache; @@ -190,6 +191,18 @@ public boolean expire(GridCacheContext cctx, IgniteInClosure2X storeAll( + GridCacheContext cctx, + GridDhtLocalPartition part, + Collection entries + ) throws IgniteCheckedException; + /** * @param cctx Cache context. * @param key Key. @@ -729,6 +742,18 @@ void update( long expireTime, @Nullable CacheDataRow oldRow) throws IgniteCheckedException; + + /** + * @param cctx Cache context. + * @param entries Entries. + * @return Created rows. + * @throws IgniteCheckedException If failed. + */ + public List storeAll( + GridCacheContext cctx, + Collection entries + ) throws IgniteCheckedException; + /** * @param cctx Cache context. * @param key Key. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index acd9ca04cad93..ddb2ef8c00880 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.cache; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -446,6 +447,15 @@ private Iterator cacheData(boolean primary, boolean backup, Affi dataStore(part).invoke(cctx, key, c); } + /** {@inheritDoc} */ + @Override public List storeAll( + GridCacheContext cctx, + GridDhtLocalPartition part, + Collection entries + ) throws IgniteCheckedException { + return dataStore(part).storeAll(cctx, entries); + } + /** {@inheritDoc} */ @Override public void update( GridCacheContext cctx, @@ -1693,6 +1703,53 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo } } + /** {@inheritDoc} */ + @Override public List storeAll( + GridCacheContext cctx, + Collection infos + ) throws IgniteCheckedException { + if (!busyLock.enterBusy()) + throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); + + List rows = new ArrayList<>(infos.size()); + + try { + assert cctx.shared().database().checkpointLockIsHeldByThread(); + + assert !cctx.mvccEnabled(); + + int cacheId = cctx.group().storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID; + + IoStatisticsHolder statHolder = grp.statisticsHolderData(); + + for (GridCacheEntryInfo info : infos) { + KeyCacheObject key = info.key(); + CacheObject val = info.value(); + + CacheObjectContext coCtx = cctx.cacheObjectContext(); + + key.valueBytes(coCtx); + val.valueBytes(coCtx); + + DataRow row = makeDataRow(key, val, info.version(), info.expireTime(), cacheId); + + rows.add(row); + } + + rowStore().addRows(rows, statHolder); + + if (grp.sharedGroup() && !cctx.group().storeCacheIdInDataPage()) { + for (CacheDataRow row : rows) + ((DataRow)row).cacheId(cctx.cacheId()); + } + } + finally { + busyLock.leaveBusy(); + } + + return rows; + } + /** {@inheritDoc} */ @Override public CacheDataRow createRow( GridCacheContext cctx, diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 0cf5abac01220..4ac2b6fef0d76 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -35,12 +35,15 @@ import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.DataPageEvictionMode; +import org.apache.ignite.configuration.DataRegionConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.IgniteNodeAttributes; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; +import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection; @@ -59,6 +62,9 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware; import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware; import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState; +import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; +import org.apache.ignite.internal.processors.cache.persistence.DataRegion; +import org.apache.ignite.internal.processors.cache.persistence.RowStore; import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter; import org.apache.ignite.internal.util.future.GridCompoundFuture; @@ -68,6 +74,7 @@ import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.CI1; +import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.S; @@ -89,6 +96,9 @@ * Thread pool for requesting partitions from other nodes and populating local cache. */ public class GridDhtPartitionDemander { + /** */ + private static final int CHECKPOINT_THRESHOLD = 100; + /** */ private final GridCacheSharedContext ctx; @@ -769,6 +779,8 @@ public void handleSupplyMessage( boolean last = supplyMsg.last().containsKey(p); + boolean batched = evictionAllowsBatch(grp.dataRegion(), supplyMsg.messageSize()); + if (part.state() == MOVING) { boolean reserved = part.reserve(); @@ -785,7 +797,7 @@ public void handleSupplyMessage( if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); else - preloadEntries(topVer, node, p, infos); + preloadEntries(topVer, node, p, infos, batched); // If message was last for this partition, // then we take ownership. @@ -869,6 +881,127 @@ public void handleSupplyMessage( } } + /** + * @param region Memory region. + * @param msgSize Rebalance message size. + * @return {@code True} if entries could processed in batch. + */ + private boolean evictionAllowsBatch(DataRegion region, int msgSize) { + DataRegionConfiguration plc = region.config(); + + if (plc.isPersistenceEnabled() || plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED) + return true; + + PageMemory pageMem = region.pageMemory(); + + int sysPageSize = pageMem.systemPageSize(); + + int pagesRequired = (msgSize / sysPageSize) * 2; + + long maxPages = plc.getMaxSize() / sysPageSize; + + // There are enough pages left. + if (pagesRequired < maxPages - pageMem.loadedPages()) + return true; + + // Empty pages pool size restricted. + if (pagesRequired > plc.getEmptyPagesPoolSize()) + return false; + + return pagesRequired < maxPages * (1.0d - plc.getEvictionThreshold()); + } + + /** + * @param from Node which sent entry. + * @param p Partition id. + * @param infos Preloaded entries. + * @param topVer Topology version. + * @param batchSize Batch size. + * @throws IgniteCheckedException If failed. + */ + private boolean preloadEntriesBatched( + AffinityTopologyVersion topVer, + ClusterNode from, + int p, + Iterator infos, + int batchSize + ) throws IgniteCheckedException { + Map> cctxs = new HashMap<>(); + + // Groupping by cache id, since we cannot place entries from different caches on the same page. + for (GridCacheEntryInfo e; infos.hasNext() && batchSize-- > 0; + e = infos.next(), cctxs.computeIfAbsent(e.cacheId(), v -> new ArrayList<>(8)).add(e)); + + for (Map.Entry> cctxEntry : cctxs.entrySet()) { + GridCacheContext cctx = + grp.sharedGroup() ? ctx.cacheContext(cctxEntry.getKey()) : grp.singleCacheContext(); + + if (cctx == null) + continue; + + if (cctx.isNear()) + cctx = cctx.dhtCache().context(); + + List cctxInfos = cctxEntry.getValue(); + + Iterator rowsItr = null; + + try { + GridDhtLocalPartition part = cctx.topology().localPartition(p); + + // Filter NULL values (it means that we should remove entry from cache). + Collection hasValues = F.view(cctxInfos, info -> info.value() != null); + + cctx.shared().database().ensureFreeSpace(cctx.dataRegion()); + + // Store all cache entries to data store before get locks. + rowsItr = cctx.offheap().storeAll(cctx, part, hasValues).iterator(); + + for (GridCacheEntryInfo info : cctxInfos) { + CacheDataRow row = info.value() == null ? null : rowsItr.next(); + + // Link created cache entry in BPlusTree. + if (!preloadEntry(from, p, info, topVer, cctx, row)) + return false; + + //TODO: IGNITE-11330: Update metrics for touched cache only. + for (GridCacheContext cctx0 : grp.caches()) { + if (cctx0.statisticsEnabled()) + cctx0.cache().metrics0().onRebalanceKeyReceived(); + } + } + } + catch (GridDhtInvalidPartitionException ignored) { + if (log.isDebugEnabled()) + log.debug("Partition became invalid during rebalancing (will ignore): " + p); + + return false; + } + finally { + // Remove all unprocessed rows on error. + while (rowsItr != null && rowsItr.hasNext()) + cleanupRow(cctx, rowsItr.next()); + } + } + + return true; + } + + /** + * Remove row from data store. + * + * @param cctx Cache context. + * @param row Row to remove. + * @throws IgniteCheckedException If failed. + */ + private void cleanupRow(GridCacheContext cctx, CacheDataRow row) throws IgniteCheckedException { + if (row != null) { + RowStore rowStore = cctx.offheap().dataStore(cctx.topology().localPartition(row.partition())).rowStore(); + + rowStore.removeRow(row.link(), grp.statisticsHolderData()); + } + } + /** * Adds mvcc entries with theirs history to partition p. * @@ -962,10 +1095,11 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node * @param p Partition id. * @param infos Entries info for preload. * @param topVer Topology version. + * @param batched Preload entries in batch mode. * @throws IgniteInterruptedCheckedException If interrupted. */ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - Iterator infos) throws IgniteCheckedException { + Iterator infos, boolean batched) throws IgniteCheckedException { GridCacheContext cctx = null; // Loop through all received entries and try to preload them. @@ -973,7 +1107,19 @@ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, in ctx.database().checkpointReadLock(); try { - for (int i = 0; i < 100; i++) { + if (batched) { + if (!preloadEntriesBatched(topVer, node, p, infos, CHECKPOINT_THRESHOLD)) { + if (log.isTraceEnabled()) + log.trace("Got entries for invalid partition during " + + "preloading (will skip) [p=" + p + ']'); + + return; + } + + continue; + } + + for (int i = 0; i < CHECKPOINT_THRESHOLD; i++) { if (!infos.hasNext()) break; @@ -988,7 +1134,7 @@ else if (cctx.isNear()) cctx = cctx.dhtCache().context(); } - if (!preloadEntry(node, p, entry, topVer, cctx)) { + if (!preloadEntry(node, p, entry, topVer, cctx, null)) { if (log.isTraceEnabled()) log.trace("Got entries for invalid partition during " + "preloading (will skip) [p=" + p + ", entry=" + entry + ']'); @@ -1017,6 +1163,7 @@ else if (cctx.isNear()) * @param entry Preloaded entry. * @param topVer Topology version. * @param cctx Cache context. + * @param row Pre-created data row, associated with this cache entry. * @return {@code False} if partition has become invalid during preloading. * @throws IgniteInterruptedCheckedException If interrupted. */ @@ -1025,7 +1172,8 @@ private boolean preloadEntry( int p, GridCacheEntryInfo entry, AffinityTopologyVersion topVer, - GridCacheContext cctx + GridCacheContext cctx, + CacheDataRow row ) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); @@ -1053,7 +1201,8 @@ private boolean preloadEntry( true, topVer, cctx.isDrEnabled() ? DR_PRELOAD : DR_NONE, - false + false, + row )) { cached.touch(); // Start tracking. @@ -1065,18 +1214,20 @@ private boolean preloadEntry( else { cached.touch(); // Start tracking. + cleanupRow(cctx, row); // Remove pre-created row. + if (log.isTraceEnabled()) log.trace("Rebalancing entry is already in cache (will ignore) [key=" + cached.key() + ", part=" + p + ']'); } } - else if (log.isTraceEnabled()) - log.trace("Rebalance predicate evaluated to false for entry (will ignore): " + entry); } catch (GridCacheEntryRemovedException ignored) { if (log.isTraceEnabled()) log.trace("Entry has been concurrently removed while rebalancing (will ignore) [key=" + cached.key() + ", part=" + p + ']'); + + cleanupRow(cctx, row); } catch (GridDhtInvalidPartitionException ignored) { if (log.isDebugEnabled()) 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 8207b2bc2dc28..4e015552ddf9d 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 @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.cache.persistence; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -59,6 +60,7 @@ import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.GridCacheContext; import org.apache.ignite.internal.processors.cache.GridCacheEntryEx; +import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo; import org.apache.ignite.internal.processors.cache.GridCacheMvccEntryInfo; import org.apache.ignite.internal.processors.cache.GridCacheTtlManager; import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl; @@ -2399,6 +2401,18 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { delegate.invoke(cctx, key, c); } + /** {@inheritDoc} */ + @Override public List storeAll( + GridCacheContext cctx, + Collection entries + ) throws IgniteCheckedException { + assert ctx.database().checkpointLockIsHeldByThread(); + + CacheDataStore delegate = init0(false); + + return delegate.storeAll(cctx, entries); + } + /** {@inheritDoc} */ @Override public void remove(GridCacheContext cctx, KeyCacheObject key, int partId) throws IgniteCheckedException { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index bdd1c2db3782c..624daf7d6e17e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence; +import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.pagemem.PageIdUtils; import org.apache.ignite.internal.pagemem.PageMemory; @@ -117,6 +118,16 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit ", link=" + U.hexLong(row.link()) + ']'; } + /** + * @param rows Rows. + * @throws IgniteCheckedException If failed. + */ + public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { + assert ctx.database().checkpointLockIsHeldByThread(); + + freeList.insertDataRows(rows.iterator(), statHolder); + } + /** * @param link Row link. * @param row New row data. 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 b677116353a38..c834b9ecc257d 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 @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist; +import java.util.Iterator; import java.util.concurrent.atomic.AtomicReferenceArray; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; @@ -159,15 +160,6 @@ private final class WriteRowHandler extends PageHandler { written = (written == 0 && oldFreeSpace >= rowSize) ? addRow(pageId, page, pageAddr, io, row, rowSize) : addRowFragment(pageId, page, pageAddr, io, row, written, rowSize); - // Reread free space after update. - int newFreeSpace = io.getFreeSpace(pageAddr); - - if (newFreeSpace > MIN_PAGE_FREE_SPACE) { - int bucket = bucket(newFreeSpace, false); - - put(null, pageId, page, pageAddr, bucket, statHolder); - } - if (written == rowSize) evictionTracker.touchPage(pageId); @@ -473,71 +465,265 @@ private long allocateDataPage(int part) throws IgniteCheckedException { /** {@inheritDoc} */ @Override public void insertDataRow(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { - int rowSize = row.size(); + try { + int written = writeLargeFragments(row, statHolder); - int written = 0; + if (written == COMPLETE) + return; - try { - do { - if (written != 0) - memMetrics.incrementLargeEntriesPages(); + int remaining = row.size() - written; - int remaining = rowSize - written; + long pageId = getPage(bucket(remaining, false) + 1, REUSE_BUCKET, row, statHolder); - long pageId = 0L; + AbstractDataPageIO initIo = null; - if (remaining < MIN_SIZE_FOR_DATA_PAGE) { - for (int b = bucket(remaining, false) + 1; b < BUCKETS - 1; b++) { - pageId = takeEmptyPage(b, row.ioVersions(), statHolder); + if (pageId == 0L) { + pageId = allocateDataPage(row.partition()); - if (pageId != 0L) - break; - } + initIo = row.ioVersions().latest(); + } + + long page = pageMem.acquirePage(grpId, pageId, statHolder); + + boolean releaseAfterWrite = true; + + try { + long pageAddr = writeLock(pageId, page); + + assert pageAddr != 0; + + AbstractDataPageIO io = initIo != null ? initIo : PageIO.getPageIO(pageAddr); + + boolean ok = false; + + try { + PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, writeRow, initIo, wal, + null, row, written, statHolder); + + ok = true; } + finally { + putPage(io.getFreeSpace(pageAddr), pageId, page, pageAddr, statHolder); - if (pageId == 0L) { // Handle reuse bucket. - if (reuseList == this) - pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder); - else - pageId = reuseList.takeRecycledPage(); + if (releaseAfterWrite = writeRow.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0)) + writeUnlock(pageId, page, pageAddr, ok); + } + } + finally { + if (releaseAfterWrite) + pageMem.releasePage(grpId, pageId, page); + } + } + catch (IgniteCheckedException | Error e) { + throw e; + } + catch (Throwable t) { + throw new CorruptedFreeListException("Failed to insert data row", t); + } + } + + /** {@inheritDoc} */ + @Override public void insertDataRows(Iterator iter, IoStatisticsHolder statHolder) throws IgniteCheckedException { + assert iter.hasNext(); + + try { + T row = iter.next(); + + int written; + + do { + written = writeLargeFragments(row, statHolder); + + if (written == COMPLETE) { + if (iter.hasNext()) + row = iter.next(); + + continue; } AbstractDataPageIO initIo = null; - if (pageId == 0L) { + int minBucket = bucket(row.size() % MIN_SIZE_FOR_DATA_PAGE, false); + + // Search for the most free page with enough space. + long pageId = getPage(REUSE_BUCKET - 1, minBucket, row, statHolder); + + if (pageId == 0) { pageId = allocateDataPage(row.partition()); initIo = row.ioVersions().latest(); } - else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. - pageId = initReusedPage(row, pageId, row.partition(), statHolder); - else // Page is taken from free space bucket. For in-memory mode partition must be changed. - pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); - written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder); + long page = pageMem.acquirePage(grpId, pageId, statHolder); - assert written != FAIL_I; // We can't fail here. - } - while (written != COMPLETE); + boolean releaseAfterWrite = true; + + try { + long pageAddr = writeLock(pageId, page); + + assert pageAddr != 0; + + AbstractDataPageIO io = initIo != null ? initIo : PageIO.getPageIO(pageAddr); + + boolean ok = false; + + try { + do { + if (row.link() == 0) + written = writeLargeFragments(row, statHolder); + + if (written != COMPLETE) { + written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, + writeRow, initIo, wal, null, row, written, statHolder); + } + + assert written == COMPLETE : written; + + initIo = null; + + if (!iter.hasNext()) + break; + + row = iter.next(); + } + while (io.getFreeSpace(pageAddr) >= (row.size() % MIN_SIZE_FOR_DATA_PAGE)); + + ok = true; + } + finally { + putPage(io.getFreeSpace(pageAddr), pageId, page, pageAddr, statHolder); + + if (releaseAfterWrite = writeRow.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0)) + writeUnlock(pageId, page, pageAddr, ok); + } + } + finally { + if (releaseAfterWrite) + pageMem.releasePage(grpId, pageId, page); + } + } while (written != COMPLETE || row.link() == 0); } - catch (IgniteCheckedException | Error e) { - throw e; + catch (RuntimeException e) { + throw new CorruptedFreeListException("Failed to insert data rows", e); } - catch (Throwable t) { - throw new CorruptedFreeListException("Failed to insert data row", t); + } + + /** + * Get a page from the free list. + * + * @param min Minimum bucket. + * @param max Maximum bucket. + * @param row Row to process. + * @param statHolder Statistics holder to track IO operations. + * @return Page ID or {@code 0} if none available. + * @throws IgniteCheckedException If failed. + */ + private long getPage(int min, int max, T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { + long pageId = 0; + + int direction = min < max ? 1 : -1; + + for (int b = min; b != max; b += direction) { + assert b != REUSE_BUCKET; + + pageId = takeEmptyPage(b, row.ioVersions(), statHolder); + + if (pageId != 0L) + break; + } + + if (pageId == 0) { + if (reuseList == this) + pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder); + else + pageId = reuseList.takeRecycledPage(); } + + if (pageId == 0) + return 0; + + if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. + return initReusedPage(row, pageId, statHolder); + else // For in-memory mode partition must be changed. + return PageIdUtils.changePartitionId(pageId, row.partition()); + } + + /** + * Insert page into the free list. + * + * @param freeSpace Page free space. + * @param pageId Page ID. + * @param page Page pointer. + * @param pageAddr Page address. + * @param statHolder Statistics holder to track IO operations. + * @throws IgniteCheckedException If failed. + */ + private void putPage( + int freeSpace, + long pageId, + long page, + long pageAddr, + IoStatisticsHolder statHolder + ) throws IgniteCheckedException { + if (freeSpace > MIN_PAGE_FREE_SPACE) { + int bucket = bucket(freeSpace, false); + + put(null, pageId, page, pageAddr, bucket, statHolder); + } + + assert PageIO.getCrc(pageAddr) == 0; //TODO GG-11480 + } + + /** + * Write fragments of the row, which occupy the whole memory page. + * + * @param row Row to process. + * @param statHolder Statistics holder to track IO operations. + * @return Number of bytes written. + * @throws IgniteCheckedException If failed. + */ + private int writeLargeFragments(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { + if (row.size() < MIN_SIZE_FOR_DATA_PAGE) + return 0; + + // Write large row fragments. + int written = 0; + + do { + long pageId = reuseList == this ? + takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder) : reuseList.takeRecycledPage(); + + AbstractDataPageIO initIo = null; + + if (pageId == 0L) { + pageId = allocateDataPage(row.partition()); + + initIo = row.ioVersions().latest(); + } + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) + pageId = initReusedPage(row, pageId, statHolder); + else + pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); + + written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder); + + assert written != FAIL_I; // We can't fail here. + + memMetrics.incrementLargeEntriesPages(); + } + while (written != COMPLETE && (row.size() - written) >= MIN_SIZE_FOR_DATA_PAGE); + + return written; } /** * @param reusedPageId Reused page id. - * @param partId Partition id. * @param statHolder Statistics holder to track IO operations. * @return Prepared page id. * * @see PagesList#initReusedPage(long, long, long, int, byte, PageIO) */ - private long initReusedPage(T row, long reusedPageId, int partId, - IoStatisticsHolder statHolder) throws IgniteCheckedException { + private long initReusedPage(T row, long reusedPageId, IoStatisticsHolder statHolder) throws IgniteCheckedException { long reusedPage = acquirePage(reusedPageId, statHolder); try { long reusedPageAddr = writeLock(reusedPageId, reusedPage); @@ -546,7 +732,7 @@ private long initReusedPage(T row, long reusedPageId, int partId, try { return initReusedPage(reusedPageId, reusedPage, reusedPageAddr, - partId, PageIdAllocator.FLAG_DATA, row.ioVersions().latest()); + row.partition(), PageIdAllocator.FLAG_DATA, row.ioVersions().latest()); } finally { writeUnlock(reusedPageId, reusedPage, reusedPageAddr, true); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java index 28f5a50964c93..dda0b9135f11f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist; +import java.util.Iterator; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.processors.cache.persistence.Storable; @@ -32,6 +33,12 @@ public interface FreeList { */ public void insertDataRow(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException; + /** + * @param iter Rows iterator. + * @throws IgniteCheckedException If failed. + */ + public void insertDataRows(Iterator iter, IoStatisticsHolder statHolder) throws IgniteCheckedException; + /** * @param link Row link. * @param row New row data. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java index 283b3bae1aa86..9217351003073 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java @@ -282,15 +282,7 @@ public static R writePage( boolean ok = false; try { - if (init != null) { - // It is a new page and we have to initialize it. - doInitPage(pageMem, grpId, pageId, page, pageAddr, init, wal); - walPlc = FALSE; - } - else - init = PageIO.getPageIO(pageAddr); - - R res = h.run(grpId, pageId, page, pageAddr, init, walPlc, arg, intArg, statHolder); + R res = writePage(pageMem, grpId, pageId, page, pageAddr, lsnr, h, init, wal, walPlc, arg, intArg, statHolder); ok = true; @@ -309,6 +301,33 @@ public static R writePage( } } + public static R writePage( + PageMemory pageMem, + int grpId, + long pageId, + long page, + long pageAddr, + PageLockListener lsnr, + PageHandler h, + PageIO init, + IgniteWriteAheadLogManager wal, + Boolean walPlc, + X arg, + int intArg, + IoStatisticsHolder statHolder + ) throws IgniteCheckedException { + if (init != null) { + // It is a new page and we have to initialize it. + doInitPage(pageMem, grpId, pageId, page, pageAddr, init, wal); + walPlc = FALSE; + } + else + init = PageIO.getPageIO(pageAddr); + + return h.run(grpId, pageId, page, pageAddr, init, walPlc, arg, intArg, statHolder); + } + + /** * @param pageMem Page memory. * @param grpId Group ID. diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java index 8d1ab878f2970..f6900b8401ad6 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java @@ -711,7 +711,8 @@ void recheckLock() { boolean preload, AffinityTopologyVersion topVer, GridDrType drType, - boolean fromStore + boolean fromStore, + CacheDataRow row ) throws IgniteCheckedException, GridCacheEntryRemovedException { assert false; diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java new file mode 100644 index 0000000000000..a804181ea1bdc --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.cache.distributed.rebalancing; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteDataStreamer; +import org.apache.ignite.IgniteException; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cluster.ClusterNode; +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.IgniteEx; +import org.apache.ignite.internal.IgniteInternalFuture; +import org.apache.ignite.internal.IgniteInterruptedCheckedException; +import org.apache.ignite.internal.managers.communication.GridIoMessage; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage; +import org.apache.ignite.internal.util.typedef.PA; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteInClosure; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.spi.IgniteSpiException; +import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.WithSystemProperty; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.apache.ignite.IgniteSystemProperties.IGNITE_BASELINE_AUTO_ADJUST_ENABLED; + +/** + * Measures time required for preloading data. + */ +public class PreloadingFlowTest extends GridCommonAbstractTest { + /** */ + private static final int CACHE_SIZE = 2_000_000; + + /** */ + private static final int ITERATIONS = 10; + + /** */ + private CountDownLatch preloadStartSync; + + /** */ + public CacheAtomicityMode cacheAtomicityMode; + + /** */ + public boolean persistenceEnabled; + + /** {@inheritDoc} */ + @Override protected long getTestTimeout() { + return TimeUnit.MINUTES.toMillis(30); + } + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + cfg.setDataStorageConfiguration(new DataStorageConfiguration(). + setDefaultDataRegionConfiguration(new DataRegionConfiguration() + .setPersistenceEnabled(persistenceEnabled))); + + cfg.setRebalanceThreadPoolSize(4); + + cfg.setCacheConfiguration(new CacheConfiguration(DEFAULT_CACHE_NAME) + .setCacheMode(CacheMode.REPLICATED) + .setAtomicityMode(cacheAtomicityMode) + .setRebalanceBatchesPrefetchCount(8)); + + boolean supplyNode = getTestIgniteInstanceIndex(igniteInstanceName) == 0; + + cfg.setCommunicationSpi(new TestCommunicationSpi(supplyNode ? preloadStartSync = new CountDownLatch(1) : null)); + + return cfg; + } + + /** + * + */ + @Before + public void setup() throws Exception { + cleanPersistenceDir(); + } + + /** + * + */ + @After + public void tearDown() throws Exception { + stopAllGrids(); + + cleanPersistenceDir(); + } + + /** + * + */ + @Test + @WithSystemProperty(key = IGNITE_BASELINE_AUTO_ADJUST_ENABLED, value = "false") + public void measurePreloadTime() throws Exception { + List results = new ArrayList<>(); + + boolean[] persistenceModes = new boolean[] {false, true}; + + try { + for (boolean persistence : persistenceModes) { + persistenceEnabled = persistence; + + for (CacheAtomicityMode atomicity : CacheAtomicityMode.values()) { + cacheAtomicityMode = atomicity; + + long totalTime = 0; + + for (int i = 0; i < ITERATIONS; i++) { + long time = preload(CACHE_SIZE, i == 0); + + log.info("*** iter=" + i + ", persistence=" + persistence + + ", atomicity=" + atomicity + ", time=" + time); + + totalTime += time; + } + + results.add(String.format(" Average time: %d (persistence=%b, atomicity=%s)", + totalTime / ITERATIONS, persistenceEnabled, cacheAtomicityMode)); + } + } + } + finally { + log.info("*****************************************************************************************"); + + for (String result : results) + log.info(result); + + log.info("*****************************************************************************************"); + } + } + + /** + * + */ + private long preload(int size, boolean validateCacheAfterLoad) throws Exception { + long time; + + try { + Ignite node = startGrid(0); + + node.cluster().active(true); + + log.info("Load cache data."); + + Map data = prepare(size); + + loadCache(node, DEFAULT_CACHE_NAME, data); + + awaitPartitionMapExchange(); + + IgniteEx node2 = startGrid(1); + + node.cluster().setBaselineTopology(node2.cluster().nodes()); + + IgniteInternalCache cache = node2.cachex(DEFAULT_CACHE_NAME); + + boolean rebalanceStarted = GridTestUtils.waitForCondition(new PA() { + @Override public boolean apply() { + return !cache.context().preloader().rebalanceFuture().isDone(); + } + }, 10_000); + + assertTrue(rebalanceStarted); + + preloadStartSync.countDown(); + + IgniteInternalFuture fut = cache.context().preloader().rebalanceFuture(); + + long start = U.currentTimeMillis(); + + fut.get(30, TimeUnit.SECONDS); + + time = U.currentTimeMillis() - start; + + if (validateCacheAfterLoad) { + log.info("Validation."); + + stopGrid(0); + + awaitPartitionMapExchange(); + + node2.cache(DEFAULT_CACHE_NAME); + + for (Map.Entry e : data.entrySet()) + assertEquals(e.getValue(), cache.get(e.getKey())); + } + } + finally { + tearDown(); + } + + return time; + } + + /** + * @param cnt Count of entries. + * @return Preapred data. + */ + private Map prepare(int cnt) { + Map data = new LinkedHashMap<>(U.capacity(cnt)); + + byte[] bytes = new byte[50]; + + for (int i = 0; i < cnt; i++) + data.put(i, new TestObject("val-" + i, "version-" + i, i, i * Integer.MAX_VALUE, bytes)); + + return data; + } + + /** + * + */ + private void loadCache(Ignite node, String name, Map data) { + try (IgniteDataStreamer streamer = node.dataStreamer(name)) { + streamer.addData(data); + } + } + + /** + * + */ + private static class TestCommunicationSpi extends TcpCommunicationSpi { + /** */ + private final CountDownLatch latch; + + /** + * @param latch Preloading start latch. + */ + private TestCommunicationSpi(CountDownLatch latch) { + this.latch = latch; + } + + /** {@inheritDoc} */ + @Override public void sendMessage( + final ClusterNode node, + final Message msg, + final IgniteInClosure ackC + ) throws IgniteSpiException { + try { + boolean supplyMsg = msg instanceof GridIoMessage && + ((GridIoMessage)msg).message() instanceof GridDhtPartitionSupplyMessage; + + if (supplyMsg && latch != null) + U.await(latch, 10, TimeUnit.SECONDS); + + super.sendMessage(node, msg, ackC); + } + catch (IgniteInterruptedCheckedException e) { + throw new IgniteSpiException(e); + } + } + } + + /** + * Test object. + */ + public static class TestObject { + /** */ + private String field1; + + /** */ + private String field2; + + /** */ + private int field3; + + /** */ + private long field4; + + /** */ + private byte[] field5; + + /** + * Default constructor. + */ + public TestObject() { + // No-op. + } + + /** + * + */ + public TestObject(String field1, String field2, int field3, long field4, byte[] field5) { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + this.field4 = field4; + this.field5 = field5; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + TestObject obj = (TestObject)o; + + return field3 == obj.field3 && + field4 == obj.field4 && + Objects.equals(field1, obj.field1) && + Objects.equals(field2, obj.field2) && + Arrays.equals(field5, obj.field5); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + int res = Objects.hash(field1, field2, field3, field4); + + res = 31 * res + Arrays.hashCode(field5); + + return res; + } + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java new file mode 100644 index 0000000000000..f22d14f7e3c12 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.cache.persistence.pagemem; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javax.cache.Cache; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteException; +import org.apache.ignite.IgniteSystemProperties; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.cluster.ClusterNode; +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.IgniteEx; +import org.apache.ignite.internal.IgniteInterruptedCheckedException; +import org.apache.ignite.internal.managers.communication.GridIoMessage; +import org.apache.ignite.internal.processors.cache.GridCacheContext; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage; +import org.apache.ignite.internal.util.lang.GridCloseableIterator; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteInClosure; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.spi.IgniteSpiException; +import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.WithSystemProperty; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * Check page memory consistency after preloading. + */ +@RunWith(Parameterized.class) +public class MemoryLeakAfterRebalanceSelfTest extends GridCommonAbstractTest { + /** */ + private final CountDownLatch preloadStartLatch = new CountDownLatch(1); + + /** */ + @Parameterized.Parameter + public CacheAtomicityMode cacheAtomicityMode; + + /** */ + @Parameterized.Parameters(name = " [atomicity={0}]") + public static Iterable parameters() { + return Arrays.asList(new Object[][] {{CacheAtomicityMode.ATOMIC}, {CacheAtomicityMode.TRANSACTIONAL}}); + } + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + cfg.setDataStorageConfiguration(new DataStorageConfiguration(). + setDefaultDataRegionConfiguration(new DataRegionConfiguration() + .setPersistenceEnabled(true))); + + cfg.setRebalanceThreadPoolSize(4); + + cfg.setCacheConfiguration(new CacheConfiguration(DEFAULT_CACHE_NAME) + .setAffinity(new RendezvousAffinityFunction(false, 16)) + .setCacheMode(CacheMode.REPLICATED) + .setAtomicityMode(cacheAtomicityMode)); + + boolean supplier = getTestIgniteInstanceIndex(igniteInstanceName) == 0; + + cfg.setCommunicationSpi(new TestCommunicationSpi(supplier ? preloadStartLatch : null)); + + return cfg; + } + + /** Initialization. */ + @Before + public void before() throws Exception { + cleanPersistenceDir(); + } + + /** Clean up. */ + @After + public void after() throws Exception { + stopAllGrids(); + + cleanPersistenceDir(); + } + + /** + * @throws Exception If failed. + */ + @Test + @WithSystemProperty(key = IgniteSystemProperties.IGNITE_BASELINE_AUTO_ADJUST_ENABLED, value = "false") + public void testPreloadingWithConcurrentUpdates() throws Exception { + int size = GridTestUtils.SF.applyLB(500_000, 5_000); + + // Prepare data. + Map data = new HashMap<>(U.capacity(size)); + + for (int i = 0; i < size; i++) + data.put(i, i + " v.1"); + + // Start 1 node. + Ignite node = startGrid(0); + + node.cluster().active(true); + + // Load data. + node.cache(DEFAULT_CACHE_NAME).putAll(data); + + // Start 2 node. + IgniteEx node2 = startGrid(1); + + node.cluster().setBaselineTopology(node.cluster().nodes()); + + IgniteInternalCache cache = node2.cachex(DEFAULT_CACHE_NAME); + + // Simulate concurrent updates when preloading. + for (int i = 0; i < size; i += 10) { + String val = i + " v.2"; + + cache.put(i, val); + data.put(i, val); + + // Start preloading. + if (i > size / 2) + preloadStartLatch.countDown(); + } + + awaitPartitionMapExchange(); + + // Stop node 1. + stopGrid(0); + + awaitPartitionMapExchange(); + + assertEquals(data.size(), cache.size()); + + GridCacheContext cctx = cache.context(); + + // Ensure that there are no duplicate entries left on data pages in page memory. + try (GridCloseableIterator> itr = cctx.offheap().cacheEntriesIterator( + cctx, + true, + true, + cctx.topology().readyTopologyVersion(), + false, + null, + true)) { + + while (itr.hasNext()) { + Cache.Entry entry = itr.next(); + + Integer key = entry.getKey(); + + String exp = data.remove(key); + + assertEquals(exp, entry.getValue()); + } + } + + assertTrue(data.isEmpty()); + } + + /** */ + private static class TestCommunicationSpi extends TcpCommunicationSpi { + /** */ + private final CountDownLatch latch; + + /** */ + private TestCommunicationSpi(CountDownLatch latch) { + this.latch = latch; + } + + /** {@inheritDoc} */ + @Override public void sendMessage( + final ClusterNode node, + final Message msg, + final IgniteInClosure ackC + ) throws IgniteSpiException { + try { + boolean supplyMsg = msg instanceof GridIoMessage && + ((GridIoMessage)msg).message() instanceof GridDhtPartitionSupplyMessage; + + if (supplyMsg && latch != null) + U.await(latch, 10, TimeUnit.SECONDS); + + super.sendMessage(node, msg, ackC); + } catch (IgniteInterruptedCheckedException e) { + throw new IgniteSpiException(e); + } + } + } +} 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 12b804f93a86c..3244d7c99432b 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 @@ -18,9 +18,11 @@ package org.apache.ignite.internal.processors.database; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.Callable; @@ -65,6 +67,9 @@ public class CacheFreeListSelfTest extends GridCommonAbstractTest { /** */ private static final long MB = 1024L * 1024L; + /** */ + private static final int BATCH_SIZE = 100; + /** */ private PageMemory pageMem; @@ -118,6 +123,46 @@ public void testInsertDeleteSingleThreaded_16384() throws Exception { checkInsertDeleteSingleThreaded(16384); } + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_1024() throws Exception { + checkInsertDeleteMultiThreaded(1024, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_2048() throws Exception { + checkInsertDeleteMultiThreaded(2048, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_4096() throws Exception { + checkInsertDeleteMultiThreaded(4096, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_8192() throws Exception { + checkInsertDeleteMultiThreaded(8192, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_16384() throws Exception { + checkInsertDeleteMultiThreaded(16384, true); + } + /** * @throws Exception if failed. */ @@ -163,6 +208,15 @@ public void testInsertDeleteMultiThreaded_16384() throws Exception { * @throws Exception If failed. */ protected void checkInsertDeleteMultiThreaded(final int pageSize) throws Exception { + checkInsertDeleteMultiThreaded(pageSize, false); + } + + /** + * @param pageSize Page size. + * @param batched Batch mode flag. + * @throws Exception If failed. + */ + protected void checkInsertDeleteMultiThreaded(final int pageSize, boolean batched) throws Exception { final FreeList list = createFreeList(pageSize); Random rnd = new Random(); @@ -188,6 +242,8 @@ protected void checkInsertDeleteMultiThreaded(final int pageSize) throws Excepti GridTestUtils.runMultiThreaded(new Callable() { @Override public Object call() throws Exception { + List rows = new ArrayList<>(BATCH_SIZE); + Random rnd = ThreadLocalRandom.current(); for (int i = 0; i < 200_000; i++) { @@ -218,16 +274,36 @@ protected void checkInsertDeleteMultiThreaded(final int pageSize) throws Excepti TestDataRow row = new TestDataRow(keySize, valSize); - list.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE); + if (batched) { + assert row.link() == 0; + rows.add(row); + } + else { + list.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE); + + assertTrue(row.link() != 0L); + + TestDataRow old = stored.put(row.link(), row); - assertTrue(row.link() != 0L); + assertNull(old); + } + + if (rows.size() == BATCH_SIZE) { + list.insertDataRows(rows.iterator(), IoStatisticsHolderNoOp.INSTANCE); + + for (TestDataRow row0 : rows) { + assertTrue(row0.link() != 0L); - TestDataRow old = stored.put(row.link(), row); + TestDataRow old = stored.put(row0.link(), row0); - assertNull(old); + assertNull(old); + } + + rows.clear(); + } } else { - while (true) { + while (!stored.isEmpty()) { Iterator it = stored.values().iterator(); if (it.hasNext()) { diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsMvccTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsMvccTestSuite.java index 220c41ff9d620..12efe5302489c 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsMvccTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsMvccTestSuite.java @@ -35,6 +35,7 @@ import org.apache.ignite.internal.processors.cache.persistence.pagemem.BPlusTreeReuseListPageMemoryImplTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.FillFactorMetricTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.IndexStoragePageMemoryImplTest; +import org.apache.ignite.internal.processors.cache.persistence.pagemem.MemoryLeakAfterRebalanceSelfTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImplNoLoadTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImplTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryNoStoreLeakTest; @@ -83,6 +84,8 @@ public static List> suite() { ignoredTests.add(IgnitePdsDestroyCacheTest.class); ignoredTests.add(IgnitePdsDestroyCacheWithoutCheckpointsTest.class); + ignoredTests.add(MemoryLeakAfterRebalanceSelfTest.class); + return new ArrayList<>(IgnitePdsTestSuite.suite(ignoredTests)); } } diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java index f865bb8363967..74e763b82c378 100644 --- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java +++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java @@ -42,6 +42,7 @@ import org.apache.ignite.internal.processors.cache.persistence.pagemem.BPlusTreeReuseListPageMemoryImplTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.FillFactorMetricTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.IndexStoragePageMemoryImplTest; +import org.apache.ignite.internal.processors.cache.persistence.pagemem.MemoryLeakAfterRebalanceSelfTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImplNoLoadTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImplTest; import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryLazyAllocationTest; @@ -98,6 +99,8 @@ public static List> suite(Collection ignoredTests) { //GridTestUtils.addTestIfNeeded(suite, PageIdDistributionTest.class, ignoredTests); //GridTestUtils.addTestIfNeeded(suite, TrackingPageIOTest.class, ignoredTests); + GridTestUtils.addTestIfNeeded(suite, MemoryLeakAfterRebalanceSelfTest.class, ignoredTests); + // BTree tests with store page memory. GridTestUtils.addTestIfNeeded(suite, BPlusTreePageMemoryImplTest.class, ignoredTests); GridTestUtils.addTestIfNeeded(suite, BPlusTreeReuseListPageMemoryImplTest.class, ignoredTests); From 45586b7e34ea1d944948762f29e7e4b806ca5b31 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 28 May 2019 12:44:34 +0300 Subject: [PATCH 02/21] IGNITE-11584 Coode deduplication revert. --- .../freelist/AbstractFreeList.java | 228 ++++++++---------- .../persistence/tree/util/PageHandler.java | 20 +- 2 files changed, 125 insertions(+), 123 deletions(-) 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 c834b9ecc257d..1d5c4091e77f5 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 @@ -131,13 +131,27 @@ private final class UpdateRowHandler extends PageHandler { } } - /** */ - private final PageHandler writeRow = new WriteRowHandler(); + /** Write handler which puts memory page into the free list after an update. */ + private final PageHandler writeRow = new WriteRowHandler(true); + + /** Write handler which doesn't put memory page into the free list after an update. */ + private final PageHandler writeRowNoPut = new WriteRowHandler(false); /** * */ private final class WriteRowHandler extends PageHandler { + /** */ + private final boolean putPageIntoFreeList; + + /** + * @param putPageIntoFreeList Put page into the free list after an update. + */ + WriteRowHandler(boolean putPageIntoFreeList) { + this.putPageIntoFreeList = putPageIntoFreeList; + } + + /** {@inheritDoc} */ @Override public Integer run( int cacheId, long pageId, @@ -160,6 +174,17 @@ private final class WriteRowHandler extends PageHandler { written = (written == 0 && oldFreeSpace >= rowSize) ? addRow(pageId, page, pageAddr, io, row, rowSize) : addRowFragment(pageId, page, pageAddr, io, row, written, rowSize); + if (putPageIntoFreeList) { + // Reread free space after update. + int newFreeSpace = io.getFreeSpace(pageAddr); + + if (newFreeSpace > MIN_PAGE_FREE_SPACE) { + int bucket = bucket(newFreeSpace, false); + + put(null, pageId, page, pageAddr, bucket, statHolder); + } + } + if (written == rowSize) evictionTracker.touchPage(pageId); @@ -465,54 +490,52 @@ private long allocateDataPage(int part) throws IgniteCheckedException { /** {@inheritDoc} */ @Override public void insertDataRow(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { - try { - int written = writeLargeFragments(row, statHolder); - - if (written == COMPLETE) - return; + int rowSize = row.size(); - int remaining = row.size() - written; - - long pageId = getPage(bucket(remaining, false) + 1, REUSE_BUCKET, row, statHolder); - - AbstractDataPageIO initIo = null; - - if (pageId == 0L) { - pageId = allocateDataPage(row.partition()); + int written = 0; - initIo = row.ioVersions().latest(); - } + try { + do { + if (written != 0) + memMetrics.incrementLargeEntriesPages(); - long page = pageMem.acquirePage(grpId, pageId, statHolder); + int remaining = rowSize - written; - boolean releaseAfterWrite = true; + long pageId = 0L; - try { - long pageAddr = writeLock(pageId, page); + if (remaining < MIN_SIZE_FOR_DATA_PAGE) { + for (int b = bucket(remaining, false) + 1; b < BUCKETS - 1; b++) { + pageId = takeEmptyPage(b, row.ioVersions(), statHolder); - assert pageAddr != 0; + if (pageId != 0L) + break; + } + } - AbstractDataPageIO io = initIo != null ? initIo : PageIO.getPageIO(pageAddr); + if (pageId == 0L) { // Handle reuse bucket. + if (reuseList == this) + pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder); + else + pageId = reuseList.takeRecycledPage(); + } - boolean ok = false; + AbstractDataPageIO initIo = null; - try { - PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, writeRow, initIo, wal, - null, row, written, statHolder); + if (pageId == 0L) { + pageId = allocateDataPage(row.partition()); - ok = true; + initIo = row.ioVersions().latest(); } - finally { - putPage(io.getFreeSpace(pageAddr), pageId, page, pageAddr, statHolder); + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. + pageId = initReusedPage(row, pageId, statHolder); + else // Page is taken from free space bucket. For in-memory mode partition must be changed. + pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); - if (releaseAfterWrite = writeRow.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0)) - writeUnlock(pageId, page, pageAddr, ok); - } - } - finally { - if (releaseAfterWrite) - pageMem.releasePage(grpId, pageId, page); + written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder); + + assert written != FAIL_I; // We can't fail here. } + while (written != COMPLETE); } catch (IgniteCheckedException | Error e) { throw e; @@ -534,6 +557,7 @@ private long allocateDataPage(int part) throws IgniteCheckedException { do { written = writeLargeFragments(row, statHolder); + // If there is no remainder - go to the next row. if (written == COMPLETE) { if (iter.hasNext()) row = iter.next(); @@ -541,46 +565,64 @@ private long allocateDataPage(int part) throws IgniteCheckedException { continue; } - AbstractDataPageIO initIo = null; - - int minBucket = bucket(row.size() % MIN_SIZE_FOR_DATA_PAGE, false); + long pageId = 0; // Search for the most free page with enough space. - long pageId = getPage(REUSE_BUCKET - 1, minBucket, row, statHolder); + int minBucket = bucket(row.size() - written, false); + + for (int b = REUSE_BUCKET - 1; b != minBucket; b--) { + pageId = takeEmptyPage(b, row.ioVersions(), statHolder); + + if (pageId != 0L) + break; + } + + if (pageId == 0) { + if (reuseList == this) + pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder); + else + pageId = reuseList.takeRecycledPage(); + } + + AbstractDataPageIO initIo = null; if (pageId == 0) { pageId = allocateDataPage(row.partition()); initIo = row.ioVersions().latest(); } + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. + pageId = initReusedPage(row, pageId, statHolder); + else // For in-memory mode partition must be changed. + pageId = PageIdUtils.changePartitionId(pageId, row.partition()); - long page = pageMem.acquirePage(grpId, pageId, statHolder); - - boolean releaseAfterWrite = true; + // Lock page. + long page = acquirePage(pageId, statHolder); try { long pageAddr = writeLock(pageId, page); assert pageAddr != 0; - AbstractDataPageIO io = initIo != null ? initIo : PageIO.getPageIO(pageAddr); + AbstractDataPageIO io = row.ioVersions().latest(); boolean ok = false; try { + // Fill the page up to the end. do { if (row.link() == 0) written = writeLargeFragments(row, statHolder); if (written != COMPLETE) { written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, - writeRow, initIo, wal, null, row, written, statHolder); + writeRowNoPut, initIo, wal, null, row, written, statHolder); + + initIo = null; } assert written == COMPLETE : written; - initIo = null; - if (!iter.hasNext()) break; @@ -588,92 +630,36 @@ private long allocateDataPage(int part) throws IgniteCheckedException { } while (io.getFreeSpace(pageAddr) >= (row.size() % MIN_SIZE_FOR_DATA_PAGE)); + int freeSpace = io.getFreeSpace(pageAddr); + + // Put page into the free list if needed. + if (freeSpace > MIN_PAGE_FREE_SPACE) { + int bucket = bucket(freeSpace, false); + + put(null, pageId, page, pageAddr, bucket, statHolder); + } + + assert PageIO.getCrc(pageAddr) == 0; //TODO GG-11480 + ok = true; } finally { - putPage(io.getFreeSpace(pageAddr), pageId, page, pageAddr, statHolder); + assert writeRowNoPut.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0); - if (releaseAfterWrite = writeRow.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0)) - writeUnlock(pageId, page, pageAddr, ok); + writeUnlock(pageId, page, pageAddr, ok); } } finally { - if (releaseAfterWrite) - pageMem.releasePage(grpId, pageId, page); + releasePage(pageId, page); } - } while (written != COMPLETE || row.link() == 0); + } + while (written != COMPLETE || row.link() == 0); } catch (RuntimeException e) { throw new CorruptedFreeListException("Failed to insert data rows", e); } } - /** - * Get a page from the free list. - * - * @param min Minimum bucket. - * @param max Maximum bucket. - * @param row Row to process. - * @param statHolder Statistics holder to track IO operations. - * @return Page ID or {@code 0} if none available. - * @throws IgniteCheckedException If failed. - */ - private long getPage(int min, int max, T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { - long pageId = 0; - - int direction = min < max ? 1 : -1; - - for (int b = min; b != max; b += direction) { - assert b != REUSE_BUCKET; - - pageId = takeEmptyPage(b, row.ioVersions(), statHolder); - - if (pageId != 0L) - break; - } - - if (pageId == 0) { - if (reuseList == this) - pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder); - else - pageId = reuseList.takeRecycledPage(); - } - - if (pageId == 0) - return 0; - - if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. - return initReusedPage(row, pageId, statHolder); - else // For in-memory mode partition must be changed. - return PageIdUtils.changePartitionId(pageId, row.partition()); - } - - /** - * Insert page into the free list. - * - * @param freeSpace Page free space. - * @param pageId Page ID. - * @param page Page pointer. - * @param pageAddr Page address. - * @param statHolder Statistics holder to track IO operations. - * @throws IgniteCheckedException If failed. - */ - private void putPage( - int freeSpace, - long pageId, - long page, - long pageAddr, - IoStatisticsHolder statHolder - ) throws IgniteCheckedException { - if (freeSpace > MIN_PAGE_FREE_SPACE) { - int bucket = bucket(freeSpace, false); - - put(null, pageId, page, pageAddr, bucket, statHolder); - } - - assert PageIO.getCrc(pageAddr) == 0; //TODO GG-11480 - } - /** * Write fragments of the row, which occupy the whole memory page. * @@ -705,7 +691,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) else pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); - written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder); + written = write(pageId, writeRowNoPut, initIo, row, written, FAIL_I, statHolder); assert written != FAIL_I; // We can't fail here. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java index 9217351003073..79041d90f463e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java @@ -282,7 +282,8 @@ public static R writePage( boolean ok = false; try { - R res = writePage(pageMem, grpId, pageId, page, pageAddr, lsnr, h, init, wal, walPlc, arg, intArg, statHolder); + R res = writePage( + pageMem, grpId, pageId, page, pageAddr, lsnr, h, init, wal, walPlc, arg, intArg, statHolder); ok = true; @@ -301,6 +302,22 @@ public static R writePage( } } + /** + * @param pageMem Page memory. + * @param grpId Group ID. + * @param pageId Page ID. + * @param pageAddr Page address. + * @param lsnr Lock listener. + * @param h Handler. + * @param init IO for new page initialization or {@code null} if it is an existing page. + * @param wal Write ahead log. + * @param walPlc Full page WAL record policy. + * @param arg Argument. + * @param intArg Argument of type {@code int}. + * @param statHolder Statistics holder to track IO operations. + * @return Handler result. + * @throws IgniteCheckedException If failed. + */ public static R writePage( PageMemory pageMem, int grpId, @@ -327,7 +344,6 @@ public static R writePage( return h.run(grpId, pageId, page, pageAddr, init, walPlc, arg, intArg, statHolder); } - /** * @param pageMem Page memory. * @param grpId Group ID. From a1b901eb789d32d1c6ad745e5a644243a90b74ed Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Thu, 30 May 2019 16:54:48 +0300 Subject: [PATCH 03/21] IGNITE-11584 Reuse list only. --- .../freelist/AbstractFreeList.java | 117 ++++++++---------- 1 file changed, 55 insertions(+), 62 deletions(-) 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 1d5c4091e77f5..e9c3822c9f42e 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 @@ -547,42 +547,19 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken /** {@inheritDoc} */ @Override public void insertDataRows(Iterator iter, IoStatisticsHolder statHolder) throws IgniteCheckedException { - assert iter.hasNext(); - try { - T row = iter.next(); - - int written; + T row = null; - do { - written = writeLargeFragments(row, statHolder); + int written = COMPLETE; - // If there is no remainder - go to the next row. - if (written == COMPLETE) { - if (iter.hasNext()) - row = iter.next(); + while (iter.hasNext() || written != COMPLETE) { + if (written == COMPLETE) + row = iter.next(); + if ((written = writeWholePages(row, statHolder)) == COMPLETE) continue; - } - - long pageId = 0; - - // Search for the most free page with enough space. - int minBucket = bucket(row.size() - written, false); - for (int b = REUSE_BUCKET - 1; b != minBucket; b--) { - pageId = takeEmptyPage(b, row.ioVersions(), statHolder); - - if (pageId != 0L) - break; - } - - if (pageId == 0) { - if (reuseList == this) - pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder); - else - pageId = reuseList.takeRecycledPage(); - } + long pageId = takeReusedPage(row, statHolder); AbstractDataPageIO initIo = null; @@ -591,12 +568,8 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken initIo = row.ioVersions().latest(); } - else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. - pageId = initReusedPage(row, pageId, statHolder); - else // For in-memory mode partition must be changed. - pageId = PageIdUtils.changePartitionId(pageId, row.partition()); - // Lock page. + // Acquire and lock page. long page = acquirePage(pageId, statHolder); try { @@ -604,31 +577,36 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken assert pageAddr != 0; - AbstractDataPageIO io = row.ioVersions().latest(); + AbstractDataPageIO io = initIo != null ? initIo : PageIO.getPageIO(pageAddr); - boolean ok = false; + boolean dirty = false; try { // Fill the page up to the end. - do { - if (row.link() == 0) - written = writeLargeFragments(row, statHolder); + while (iter.hasNext() || written != COMPLETE) { + if (written == COMPLETE) { + row = iter.next(); + + written = 0; + + if (io.getFreeSpace(pageAddr) < (row.size() % MIN_SIZE_FOR_DATA_PAGE)) + break; + } + + if (written == 0) + written = writeWholePages(row, statHolder); if (written != COMPLETE) { written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, writeRowNoPut, initIo, wal, null, row, written, statHolder); initIo = null; - } - - assert written == COMPLETE : written; - if (!iter.hasNext()) - break; + dirty = true; + } - row = iter.next(); + assert written == COMPLETE; } - while (io.getFreeSpace(pageAddr) >= (row.size() % MIN_SIZE_FOR_DATA_PAGE)); int freeSpace = io.getFreeSpace(pageAddr); @@ -640,20 +618,17 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken } assert PageIO.getCrc(pageAddr) == 0; //TODO GG-11480 - - ok = true; } finally { assert writeRowNoPut.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0); - writeUnlock(pageId, page, pageAddr, ok); + writeUnlock(pageId, page, pageAddr, dirty); } } finally { releasePage(pageId, page); } } - while (written != COMPLETE || row.link() == 0); } catch (RuntimeException e) { throw new CorruptedFreeListException("Failed to insert data rows", e); @@ -665,31 +640,27 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken * * @param row Row to process. * @param statHolder Statistics holder to track IO operations. - * @return Number of bytes written. + * @return Number of bytes written, {@link #COMPLETE} if the row was fully written. * @throws IgniteCheckedException If failed. */ - private int writeLargeFragments(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { + private int writeWholePages(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { if (row.size() < MIN_SIZE_FOR_DATA_PAGE) return 0; - // Write large row fragments. + assert row.link() == 0 : row.link(); + int written = 0; do { - long pageId = reuseList == this ? - takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder) : reuseList.takeRecycledPage(); + long pageId = takeReusedPage(row, statHolder); AbstractDataPageIO initIo = null; - if (pageId == 0L) { + if (pageId == 0) { pageId = allocateDataPage(row.partition()); initIo = row.ioVersions().latest(); } - else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) - pageId = initReusedPage(row, pageId, statHolder); - else - pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); written = write(pageId, writeRowNoPut, initIo, row, written, FAIL_I, statHolder); @@ -697,11 +668,33 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) memMetrics.incrementLargeEntriesPages(); } - while (written != COMPLETE && (row.size() - written) >= MIN_SIZE_FOR_DATA_PAGE); + while (row.size() - written >= MIN_SIZE_FOR_DATA_PAGE); return written; } + /** + * Get a page from reuse bucket of the free list. + * + * @param row Row. + * @param statHolder Statistics holder to track IO operations. + * @return Page ID or {@code 0} if none available. + * @throws IgniteCheckedException If failed. + */ + private long takeReusedPage(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { + long pageId = reuseList == this ? + takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder) : reuseList.takeRecycledPage(); + + if (pageId != 0L) { + if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) + pageId = initReusedPage(row, pageId, statHolder); + else + pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); + } + + return pageId; + } + /** * @param reusedPageId Reused page id. * @param statHolder Statistics holder to track IO operations. From ce9db39b1508dc9d59552c16d11f15070f520f00 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 3 Jun 2019 11:59:05 +0300 Subject: [PATCH 04/21] IGNITE-11584 Rework. --- .../freelist/AbstractFreeList.java | 128 ++++++------------ 1 file changed, 42 insertions(+), 86 deletions(-) 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 e9c3822c9f42e..cc9671f38c022 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 @@ -547,27 +547,47 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken /** {@inheritDoc} */ @Override public void insertDataRows(Iterator iter, IoStatisticsHolder statHolder) throws IgniteCheckedException { + if (!iter.hasNext()) + return; + try { - T row = null; + T row = iter.next(); - int written = COMPLETE; + int written = 0; while (iter.hasNext() || written != COMPLETE) { - if (written == COMPLETE) - row = iter.next(); + long pageId = 0L; - if ((written = writeWholePages(row, statHolder)) == COMPLETE) - continue; + int remaining = row.size() - written; + + if (remaining > 0 && remaining < MIN_SIZE_FOR_DATA_PAGE) { + // Search for the most free page with enough space. + for (int b = bucket(remaining, false) + 1; b < REUSE_BUCKET; b++) { + pageId = takeEmptyPage(b, row.ioVersions(), statHolder); - long pageId = takeReusedPage(row, statHolder); + if (pageId != 0L) + break; + } + } + + if (pageId == 0L) { // Handle reuse bucket. + if (reuseList == this) + pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder); + else + pageId = reuseList.takeRecycledPage(); + } AbstractDataPageIO initIo = null; - if (pageId == 0) { + if (pageId == 0L) { pageId = allocateDataPage(row.partition()); initIo = row.ioVersions().latest(); } + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. + pageId = initReusedPage(row, pageId, statHolder); + else // Page is taken from free space bucket. For in-memory mode partition must be changed. + pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); // Acquire and lock page. long page = acquirePage(pageId, statHolder); @@ -582,33 +602,29 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken boolean dirty = false; try { + int freeSpace; + // Fill the page up to the end. - while (iter.hasNext() || written != COMPLETE) { - if (written == COMPLETE) { - row = iter.next(); + do { + written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, + writeRowNoPut, initIo, wal, null, row, written, statHolder); - written = 0; + dirty = true; - if (io.getFreeSpace(pageAddr) < (row.size() % MIN_SIZE_FOR_DATA_PAGE)) - break; - } + initIo = null; - if (written == 0) - written = writeWholePages(row, statHolder); + freeSpace = io.getFreeSpace(pageAddr); - if (written != COMPLETE) { - written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, - writeRowNoPut, initIo, wal, null, row, written, statHolder); + assert freeSpace == 0 || written == COMPLETE; - initIo = null; + if (written != COMPLETE || !iter.hasNext()) + break; - dirty = true; - } + row = iter.next(); - assert written == COMPLETE; + written = 0; } - - int freeSpace = io.getFreeSpace(pageAddr); + while (freeSpace > 0 && (freeSpace >= row.size() || row.size() > MIN_SIZE_FOR_DATA_PAGE)); // Put page into the free list if needed. if (freeSpace > MIN_PAGE_FREE_SPACE) { @@ -635,66 +651,6 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken } } - /** - * Write fragments of the row, which occupy the whole memory page. - * - * @param row Row to process. - * @param statHolder Statistics holder to track IO operations. - * @return Number of bytes written, {@link #COMPLETE} if the row was fully written. - * @throws IgniteCheckedException If failed. - */ - private int writeWholePages(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { - if (row.size() < MIN_SIZE_FOR_DATA_PAGE) - return 0; - - assert row.link() == 0 : row.link(); - - int written = 0; - - do { - long pageId = takeReusedPage(row, statHolder); - - AbstractDataPageIO initIo = null; - - if (pageId == 0) { - pageId = allocateDataPage(row.partition()); - - initIo = row.ioVersions().latest(); - } - - written = write(pageId, writeRowNoPut, initIo, row, written, FAIL_I, statHolder); - - assert written != FAIL_I; // We can't fail here. - - memMetrics.incrementLargeEntriesPages(); - } - while (row.size() - written >= MIN_SIZE_FOR_DATA_PAGE); - - return written; - } - - /** - * Get a page from reuse bucket of the free list. - * - * @param row Row. - * @param statHolder Statistics holder to track IO operations. - * @return Page ID or {@code 0} if none available. - * @throws IgniteCheckedException If failed. - */ - private long takeReusedPage(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { - long pageId = reuseList == this ? - takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder) : reuseList.takeRecycledPage(); - - if (pageId != 0L) { - if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) - pageId = initReusedPage(row, pageId, statHolder); - else - pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); - } - - return pageId; - } - /** * @param reusedPageId Reused page id. * @param statHolder Statistics holder to track IO operations. From 24116e826cfbfa17f98436e348e7d009e631fd8d Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 4 Jun 2019 12:58:59 +0300 Subject: [PATCH 05/21] IGNITE-11584 Don't break into more parts. --- .../cache/persistence/RowStore.java | 2 +- .../freelist/AbstractFreeList.java | 38 ++++++++++++------- .../cache/persistence/freelist/FreeList.java | 6 +-- .../database/CacheFreeListSelfTest.java | 6 +-- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index 624daf7d6e17e..28c3b210698c7 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -125,7 +125,7 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); - freeList.insertDataRows(rows.iterator(), statHolder); + freeList.insertDataRows(rows, statHolder); } /** 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 cc9671f38c022..9d73695de43dd 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 @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist; +import java.util.Collection; import java.util.Iterator; import java.util.concurrent.atomic.AtomicReferenceArray; import org.apache.ignite.IgniteCheckedException; @@ -135,7 +136,7 @@ private final class UpdateRowHandler extends PageHandler { private final PageHandler writeRow = new WriteRowHandler(true); /** Write handler which doesn't put memory page into the free list after an update. */ - private final PageHandler writeRowNoPut = new WriteRowHandler(false); + private final PageHandler writeRowKeepPage = new WriteRowHandler(false); /** * @@ -504,7 +505,7 @@ private long allocateDataPage(int part) throws IgniteCheckedException { long pageId = 0L; if (remaining < MIN_SIZE_FOR_DATA_PAGE) { - for (int b = bucket(remaining, false) + 1; b < BUCKETS - 1; b++) { + for (int b = bucket(remaining, false) + 1; b < REUSE_BUCKET; b++) { pageId = takeEmptyPage(b, row.ioVersions(), statHolder); if (pageId != 0L) @@ -546,22 +547,24 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken } /** {@inheritDoc} */ - @Override public void insertDataRows(Iterator iter, IoStatisticsHolder statHolder) throws IgniteCheckedException { - if (!iter.hasNext()) + @Override public void insertDataRows(Collection rows, + IoStatisticsHolder statHolder) throws IgniteCheckedException { + if (rows.isEmpty()) return; try { + Iterator iter = rows.iterator(); + T row = iter.next(); int written = 0; - while (iter.hasNext() || written != COMPLETE) { - long pageId = 0L; - + do { int remaining = row.size() - written; + long pageId = 0L; + if (remaining > 0 && remaining < MIN_SIZE_FOR_DATA_PAGE) { - // Search for the most free page with enough space. for (int b = bucket(remaining, false) + 1; b < REUSE_BUCKET; b++) { pageId = takeEmptyPage(b, row.ioVersions(), statHolder); @@ -597,7 +600,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken assert pageAddr != 0; - AbstractDataPageIO io = initIo != null ? initIo : PageIO.getPageIO(pageAddr); + AbstractDataPageIO io = initIo == null ? PageIO.getPageIO(pageAddr) : initIo; boolean dirty = false; @@ -605,9 +608,9 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken int freeSpace; // Fill the page up to the end. - do { + while (true) { written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, - writeRowNoPut, initIo, wal, null, row, written, statHolder); + writeRowKeepPage, initIo, wal, null, row, written, statHolder); dirty = true; @@ -623,8 +626,13 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken row = iter.next(); written = 0; + + int alignedSize = row.size() > MIN_SIZE_FOR_DATA_PAGE ? + row.size() % MIN_SIZE_FOR_DATA_PAGE : row.size(); + + if (freeSpace < alignedSize || alignedSize == 0) + break; } - while (freeSpace > 0 && (freeSpace >= row.size() || row.size() > MIN_SIZE_FOR_DATA_PAGE)); // Put page into the free list if needed. if (freeSpace > MIN_PAGE_FREE_SPACE) { @@ -636,7 +644,8 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken assert PageIO.getCrc(pageAddr) == 0; //TODO GG-11480 } finally { - assert writeRowNoPut.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0); + // Should always unlock data page after write. + assert writeRowKeepPage.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0); writeUnlock(pageId, page, pageAddr, dirty); } @@ -644,7 +653,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken finally { releasePage(pageId, page); } - } + } while (written != COMPLETE || iter.hasNext()); } catch (RuntimeException e) { throw new CorruptedFreeListException("Failed to insert data rows", e); @@ -652,6 +661,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken } /** + * @param row Row. * @param reusedPageId Reused page id. * @param statHolder Statistics holder to track IO operations. * @return Prepared page id. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java index dda0b9135f11f..8af592b2fe193 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java @@ -17,7 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist; -import java.util.Iterator; +import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.processors.cache.persistence.Storable; @@ -34,10 +34,10 @@ public interface FreeList { public void insertDataRow(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException; /** - * @param iter Rows iterator. + * @param rows Rows. * @throws IgniteCheckedException If failed. */ - public void insertDataRows(Iterator iter, IoStatisticsHolder statHolder) throws IgniteCheckedException; + public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException; /** * @param link Row link. 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 3244d7c99432b..1094d304ddb26 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 @@ -274,10 +274,8 @@ protected void checkInsertDeleteMultiThreaded(final int pageSize, boolean batche TestDataRow row = new TestDataRow(keySize, valSize); - if (batched) { - assert row.link() == 0; + if (batched) rows.add(row); - } else { list.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE); @@ -289,7 +287,7 @@ protected void checkInsertDeleteMultiThreaded(final int pageSize, boolean batche } if (rows.size() == BATCH_SIZE) { - list.insertDataRows(rows.iterator(), IoStatisticsHolderNoOp.INSTANCE); + list.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE); for (TestDataRow row0 : rows) { assertTrue(row0.link() != 0L); From 5bebc32136b563fffbf5c6bdbbf95b40b3466bb4 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 5 Jun 2019 15:40:14 +0300 Subject: [PATCH 06/21] IGNITE-11584 Removed flow-test, added benchmark. --- .../pagemem/JmhCacheFreelistBenchmark.java | 297 +++++++++++++++ .../rebalancing/PreloadingFlowTest.java | 351 ------------------ 2 files changed, 297 insertions(+), 351 deletions(-) create mode 100644 modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java delete mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java new file mode 100644 index 0000000000000..7493423fddb41 --- /dev/null +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.benchmarks.jmh.pagemem; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteLogger; +import org.apache.ignite.configuration.DataRegionConfiguration; +import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider; +import org.apache.ignite.internal.pagemem.PageIdAllocator; +import org.apache.ignite.internal.pagemem.PageMemory; +import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl; +import org.apache.ignite.internal.processors.cache.CacheObjectImpl; +import org.apache.ignite.internal.processors.cache.KeyCacheObjectImpl; +import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; +import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapter; +import org.apache.ignite.internal.processors.cache.persistence.DataRegion; +import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl; +import org.apache.ignite.internal.processors.cache.persistence.Storable; +import org.apache.ignite.internal.processors.cache.persistence.evict.NoOpPageEvictionTracker; +import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeListImpl; +import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; +import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; +import org.apache.ignite.internal.stat.IoStatisticsHolder; +import org.apache.ignite.internal.stat.IoStatisticsHolderNoOp; +import org.apache.ignite.logger.java.JavaLogger; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +/** + * Performance comparision between FreeList.insertRow(..) and FreeList.insertRows(..). + */ +@BenchmarkMode(Mode.AverageTime) +@Fork(value = 1, jvmArgsAppend = {"-Xms1g", "-server", "-XX:+AggressiveOpts", "-XX:MaxMetaspaceSize=256m", "-ea"}) +@OutputTimeUnit(MICROSECONDS) +@State(Scope.Benchmark) +@Threads(1) +@Warmup(iterations = 10, time = 200, timeUnit = MILLISECONDS) +@Measurement(iterations = 11, time = 200, timeUnit = MILLISECONDS) +public class JmhCacheFreelistBenchmark { + /** */ + private static final long MEMORY_REGION_SIZE = 10 * 1024 * 1024 * 1024L; // 10 GB + + /** */ + private static final int PAGE_SIZE = 4096; + + /** */ + private static final int ROWS_COUNT = 200; + + /** */ + public enum DATA_ROW_SIZE { + /** */ + r4_64(4, 64), + + /** */ + r100_300(100, 300), + + /** */ + r300_700(300, 700), + + /** */ + r700_1200(700, 1200), + + /** */ + r1200_3000(1_200, 3_000), + + /** */ + r1000_8000(1_000, 8_000), + + /** Large objects only. */ + r4000_16000(4_000, 16_000), + + /** Mixed objects, mostly large objects. */ + r100_32000(100, 32_000); + + /** */ + private final int min; + + /** */ + private final int max; + + /** */ + DATA_ROW_SIZE(int min, int max) { + this.min = min; + this.max = max; + } + } + + /** + * Check {@link FreeList#insertDataRow(Storable, IoStatisticsHolder)} performance. + */ + @Benchmark + public void insertRow(FreeListProvider provider, Data rows) throws IgniteCheckedException { + for (CacheDataRow row : rows) + provider.freeList.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE); + } + + /** + * Check {@link FreeList#insertDataRows(Collection, IoStatisticsHolder)} performance. + */ + @Benchmark + public void insertRows(FreeListProvider provider, Data rows) throws IgniteCheckedException { + provider.freeList.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE); + } + + /** */ + @State(Scope.Thread) + public static class Data extends AbstractCollection { + /** */ + @Param + private DATA_ROW_SIZE range; + + /** */ + private Collection rows = new ArrayList<>(ROWS_COUNT); + + /** */ + @Setup(Level.Iteration) + public void prepare() { + Random rnd = ThreadLocalRandom.current(); + + int randomRange = range.max - range.min; + + for (int i = 0; i < ROWS_COUNT; i++) { + int keySize = (range.min + rnd.nextInt(randomRange)) / 2; + int valSize = (range.min + rnd.nextInt(randomRange)) / 2; + + CacheDataRow row = new TestDataRow(keySize, valSize); + + rows.add(row); + } + } + + /** */ + @TearDown(Level.Iteration) + public void cleanup() { + rows.clear(); + } + + /** {@inheritDoc} */ + @Override public Iterator iterator() { + return rows.iterator(); + } + + /** {@inheritDoc} */ + @Override public int size() { + return rows.size(); + } + } + + /** */ + @State(Scope.Thread) + public static class FreeListProvider { + /** */ + private final DataRegionConfiguration plcCfg = + new DataRegionConfiguration().setInitialSize(MEMORY_REGION_SIZE).setMaxSize(MEMORY_REGION_SIZE); + + /** */ + private final JavaLogger log = new JavaLogger(); + + /** */ + private PageMemory pageMem; + + /** */ + private FreeList freeList; + + /** */ + @Setup(Level.Trial) + public void setup() throws IgniteCheckedException { + pageMem = createPageMemory(log, PAGE_SIZE, plcCfg); + + freeList = createFreeList(pageMem, plcCfg); + } + + /** */ + @TearDown(Level.Trial) + public void tearDown() { + pageMem.stop(true); + } + + /** + * @return Page memory. + */ + protected PageMemory createPageMemory(IgniteLogger log, int pageSize, DataRegionConfiguration plcCfg) { + PageMemory pageMem = new PageMemoryNoStoreImpl(log, + new UnsafeMemoryProvider(log), + null, + pageSize, + plcCfg, + new DataRegionMetricsImpl(plcCfg), + true); + + pageMem.start(); + + return pageMem; + } + + /** + * @param pageMem Page memory. + * @return Free list. + * @throws IgniteCheckedException If failed. + */ + private FreeList createFreeList( + PageMemory pageMem, + DataRegionConfiguration plcCfg + ) throws IgniteCheckedException { + long metaPageId = pageMem.allocatePage(1, 1, PageIdAllocator.FLAG_DATA); + + DataRegionMetricsImpl regionMetrics = new DataRegionMetricsImpl(plcCfg); + + DataRegion dataRegion = new DataRegion(pageMem, plcCfg, regionMetrics, new NoOpPageEvictionTracker()); + + return new CacheFreeListImpl(1, "freelist", regionMetrics, dataRegion, null, + null, metaPageId, true); + } + } + + /** */ + private static class TestDataRow extends CacheDataRowAdapter { + /** */ + private long link; + + /** + * @param keySize Key size. + * @param valSize Value size. + */ + private TestDataRow(int keySize, int valSize) { + super( + new KeyCacheObjectImpl(0, new byte[keySize], 0), + new CacheObjectImpl(0, new byte[valSize]), + new GridCacheVersion(keySize, valSize, 1), + 0 + ); + } + + /** {@inheritDoc} */ + @Override public long link() { + return link; + } + + /** {@inheritDoc} */ + @Override public void link(long link) { + this.link = link; + } + } + + /** + * Run benchmark. + * + * @param args Args. + */ + public static void main(String[] args) throws RunnerException { + final Options options = new OptionsBuilder() + .include(JmhCacheFreelistBenchmark.class.getSimpleName()) + .build(); + + new Runner(options).run(); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java deleted file mode 100644 index a804181ea1bdc..0000000000000 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/PreloadingFlowTest.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.ignite.internal.processors.cache.distributed.rebalancing; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import org.apache.ignite.Ignite; -import org.apache.ignite.IgniteDataStreamer; -import org.apache.ignite.IgniteException; -import org.apache.ignite.cache.CacheAtomicityMode; -import org.apache.ignite.cache.CacheMode; -import org.apache.ignite.cluster.ClusterNode; -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.IgniteEx; -import org.apache.ignite.internal.IgniteInternalFuture; -import org.apache.ignite.internal.IgniteInterruptedCheckedException; -import org.apache.ignite.internal.managers.communication.GridIoMessage; -import org.apache.ignite.internal.processors.cache.IgniteInternalCache; -import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage; -import org.apache.ignite.internal.util.typedef.PA; -import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.lang.IgniteInClosure; -import org.apache.ignite.plugin.extensions.communication.Message; -import org.apache.ignite.spi.IgniteSpiException; -import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi; -import org.apache.ignite.testframework.GridTestUtils; -import org.apache.ignite.testframework.junits.WithSystemProperty; -import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static org.apache.ignite.IgniteSystemProperties.IGNITE_BASELINE_AUTO_ADJUST_ENABLED; - -/** - * Measures time required for preloading data. - */ -public class PreloadingFlowTest extends GridCommonAbstractTest { - /** */ - private static final int CACHE_SIZE = 2_000_000; - - /** */ - private static final int ITERATIONS = 10; - - /** */ - private CountDownLatch preloadStartSync; - - /** */ - public CacheAtomicityMode cacheAtomicityMode; - - /** */ - public boolean persistenceEnabled; - - /** {@inheritDoc} */ - @Override protected long getTestTimeout() { - return TimeUnit.MINUTES.toMillis(30); - } - - /** {@inheritDoc} */ - @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { - IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); - - cfg.setDataStorageConfiguration(new DataStorageConfiguration(). - setDefaultDataRegionConfiguration(new DataRegionConfiguration() - .setPersistenceEnabled(persistenceEnabled))); - - cfg.setRebalanceThreadPoolSize(4); - - cfg.setCacheConfiguration(new CacheConfiguration(DEFAULT_CACHE_NAME) - .setCacheMode(CacheMode.REPLICATED) - .setAtomicityMode(cacheAtomicityMode) - .setRebalanceBatchesPrefetchCount(8)); - - boolean supplyNode = getTestIgniteInstanceIndex(igniteInstanceName) == 0; - - cfg.setCommunicationSpi(new TestCommunicationSpi(supplyNode ? preloadStartSync = new CountDownLatch(1) : null)); - - return cfg; - } - - /** - * - */ - @Before - public void setup() throws Exception { - cleanPersistenceDir(); - } - - /** - * - */ - @After - public void tearDown() throws Exception { - stopAllGrids(); - - cleanPersistenceDir(); - } - - /** - * - */ - @Test - @WithSystemProperty(key = IGNITE_BASELINE_AUTO_ADJUST_ENABLED, value = "false") - public void measurePreloadTime() throws Exception { - List results = new ArrayList<>(); - - boolean[] persistenceModes = new boolean[] {false, true}; - - try { - for (boolean persistence : persistenceModes) { - persistenceEnabled = persistence; - - for (CacheAtomicityMode atomicity : CacheAtomicityMode.values()) { - cacheAtomicityMode = atomicity; - - long totalTime = 0; - - for (int i = 0; i < ITERATIONS; i++) { - long time = preload(CACHE_SIZE, i == 0); - - log.info("*** iter=" + i + ", persistence=" + persistence + - ", atomicity=" + atomicity + ", time=" + time); - - totalTime += time; - } - - results.add(String.format(" Average time: %d (persistence=%b, atomicity=%s)", - totalTime / ITERATIONS, persistenceEnabled, cacheAtomicityMode)); - } - } - } - finally { - log.info("*****************************************************************************************"); - - for (String result : results) - log.info(result); - - log.info("*****************************************************************************************"); - } - } - - /** - * - */ - private long preload(int size, boolean validateCacheAfterLoad) throws Exception { - long time; - - try { - Ignite node = startGrid(0); - - node.cluster().active(true); - - log.info("Load cache data."); - - Map data = prepare(size); - - loadCache(node, DEFAULT_CACHE_NAME, data); - - awaitPartitionMapExchange(); - - IgniteEx node2 = startGrid(1); - - node.cluster().setBaselineTopology(node2.cluster().nodes()); - - IgniteInternalCache cache = node2.cachex(DEFAULT_CACHE_NAME); - - boolean rebalanceStarted = GridTestUtils.waitForCondition(new PA() { - @Override public boolean apply() { - return !cache.context().preloader().rebalanceFuture().isDone(); - } - }, 10_000); - - assertTrue(rebalanceStarted); - - preloadStartSync.countDown(); - - IgniteInternalFuture fut = cache.context().preloader().rebalanceFuture(); - - long start = U.currentTimeMillis(); - - fut.get(30, TimeUnit.SECONDS); - - time = U.currentTimeMillis() - start; - - if (validateCacheAfterLoad) { - log.info("Validation."); - - stopGrid(0); - - awaitPartitionMapExchange(); - - node2.cache(DEFAULT_CACHE_NAME); - - for (Map.Entry e : data.entrySet()) - assertEquals(e.getValue(), cache.get(e.getKey())); - } - } - finally { - tearDown(); - } - - return time; - } - - /** - * @param cnt Count of entries. - * @return Preapred data. - */ - private Map prepare(int cnt) { - Map data = new LinkedHashMap<>(U.capacity(cnt)); - - byte[] bytes = new byte[50]; - - for (int i = 0; i < cnt; i++) - data.put(i, new TestObject("val-" + i, "version-" + i, i, i * Integer.MAX_VALUE, bytes)); - - return data; - } - - /** - * - */ - private void loadCache(Ignite node, String name, Map data) { - try (IgniteDataStreamer streamer = node.dataStreamer(name)) { - streamer.addData(data); - } - } - - /** - * - */ - private static class TestCommunicationSpi extends TcpCommunicationSpi { - /** */ - private final CountDownLatch latch; - - /** - * @param latch Preloading start latch. - */ - private TestCommunicationSpi(CountDownLatch latch) { - this.latch = latch; - } - - /** {@inheritDoc} */ - @Override public void sendMessage( - final ClusterNode node, - final Message msg, - final IgniteInClosure ackC - ) throws IgniteSpiException { - try { - boolean supplyMsg = msg instanceof GridIoMessage && - ((GridIoMessage)msg).message() instanceof GridDhtPartitionSupplyMessage; - - if (supplyMsg && latch != null) - U.await(latch, 10, TimeUnit.SECONDS); - - super.sendMessage(node, msg, ackC); - } - catch (IgniteInterruptedCheckedException e) { - throw new IgniteSpiException(e); - } - } - } - - /** - * Test object. - */ - public static class TestObject { - /** */ - private String field1; - - /** */ - private String field2; - - /** */ - private int field3; - - /** */ - private long field4; - - /** */ - private byte[] field5; - - /** - * Default constructor. - */ - public TestObject() { - // No-op. - } - - /** - * - */ - public TestObject(String field1, String field2, int field3, long field4, byte[] field5) { - this.field1 = field1; - this.field2 = field2; - this.field3 = field3; - this.field4 = field4; - this.field5 = field5; - } - - /** {@inheritDoc} */ - @Override public boolean equals(Object o) { - if (this == o) - return true; - - if (o == null || getClass() != o.getClass()) - return false; - - TestObject obj = (TestObject)o; - - return field3 == obj.field3 && - field4 == obj.field4 && - Objects.equals(field1, obj.field1) && - Objects.equals(field2, obj.field2) && - Arrays.equals(field5, obj.field5); - } - - /** {@inheritDoc} */ - @Override public int hashCode() { - int res = Objects.hash(field1, field2, field3, field4); - - res = 31 * res + Arrays.hashCode(field5); - - return res; - } - } -} From 15cc5bf1893f3ca7ebad1940715b5f594cc8e3d3 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 5 Jun 2019 16:52:40 +0300 Subject: [PATCH 07/21] IGNITE-11584 Fixed cacheId on data page. --- .../pagemem/JmhCacheFreelistBenchmark.java | 4 +- .../preloader/GridDhtPartitionDemander.java | 18 ++-- .../freelist/AbstractFreeList.java | 94 ++++++++++++++----- 3 files changed, 82 insertions(+), 34 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java index 7493423fddb41..013f63ba485f2 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java @@ -153,7 +153,7 @@ public static class Data extends AbstractCollection { private Collection rows = new ArrayList<>(ROWS_COUNT); /** */ - @Setup(Level.Iteration) + @Setup(Level.Invocation) public void prepare() { Random rnd = ThreadLocalRandom.current(); @@ -170,7 +170,7 @@ public void prepare() { } /** */ - @TearDown(Level.Iteration) + @TearDown(Level.Invocation) public void cleanup() { rows.clear(); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 4ac2b6fef0d76..554f12fe0ad2d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -96,7 +96,7 @@ * Thread pool for requesting partitions from other nodes and populating local cache. */ public class GridDhtPartitionDemander { - /** */ + /** The maximum number of entries that can be preloaded between checkpoints. */ private static final int CHECKPOINT_THRESHOLD = 100; /** */ @@ -779,7 +779,7 @@ public void handleSupplyMessage( boolean last = supplyMsg.last().containsKey(p); - boolean batched = evictionAllowsBatch(grp.dataRegion(), supplyMsg.messageSize()); + boolean batched = canStoreWithoutEvictions(supplyMsg.messageSize()); if (part.state() == MOVING) { boolean reserved = part.reserve(); @@ -882,21 +882,21 @@ public void handleSupplyMessage( } /** - * @param region Memory region. - * @param msgSize Rebalance message size. - * @return {@code True} if entries could processed in batch. + * @param size Data size. + * @return {@code True} if rebalanced cache group allows writing a specified amount of data without evictions. */ - private boolean evictionAllowsBatch(DataRegion region, int msgSize) { - DataRegionConfiguration plc = region.config(); + private boolean canStoreWithoutEvictions(int size) { + DataRegionConfiguration plc = grp.dataRegion().config(); if (plc.isPersistenceEnabled() || plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED) return true; - PageMemory pageMem = region.pageMemory(); + PageMemory pageMem = grp.dataRegion().pageMemory(); int sysPageSize = pageMem.systemPageSize(); - int pagesRequired = (msgSize / sysPageSize) * 2; + // The number of pages is calculated taking into account memory fragmentation and possible concurrent updates. + int pagesRequired = (size / sysPageSize) * 2; long maxPages = plc.getMaxSize() / sysPageSize; 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 9d73695de43dd..9371b225ca987 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 @@ -555,11 +555,17 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken try { Iterator iter = rows.iterator(); - T row = iter.next(); + T row = null; - int written = 0; + int written = COMPLETE; + + while (iter.hasNext() || written != COMPLETE) { + if (written == COMPLETE) + row = iter.next(); + + if ((written = writeWholePages(row, statHolder)) == COMPLETE) + continue; - do { int remaining = row.size() - written; long pageId = 0L; @@ -605,35 +611,34 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken boolean dirty = false; try { - int freeSpace; - // Fill the page up to the end. - while (true) { - written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, this, - writeRowKeepPage, initIo, wal, null, row, written, statHolder); + while (iter.hasNext() || written != COMPLETE) { + if (written == COMPLETE) { + row = iter.next(); - dirty = true; + written = 0; - initIo = null; + if (io.getFreeSpace(pageAddr) < (row.size() % MIN_SIZE_FOR_DATA_PAGE)) + break; + } - freeSpace = io.getFreeSpace(pageAddr); + if (written == 0) + written = writeWholePages(row, statHolder); - assert freeSpace == 0 || written == COMPLETE; + if (written != COMPLETE) { + written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, lockLsnr, + writeRowKeepPage, initIo, wal, null, row, written, statHolder); - if (written != COMPLETE || !iter.hasNext()) - break; + initIo = null; - row = iter.next(); + dirty = true; + } - written = 0; - - int alignedSize = row.size() > MIN_SIZE_FOR_DATA_PAGE ? - row.size() % MIN_SIZE_FOR_DATA_PAGE : row.size(); - - if (freeSpace < alignedSize || alignedSize == 0) - break; + assert written == COMPLETE; } + int freeSpace = io.getFreeSpace(pageAddr); + // Put page into the free list if needed. if (freeSpace > MIN_PAGE_FREE_SPACE) { int bucket = bucket(freeSpace, false); @@ -653,13 +658,56 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken finally { releasePage(pageId, page); } - } while (written != COMPLETE || iter.hasNext()); + } } catch (RuntimeException e) { throw new CorruptedFreeListException("Failed to insert data rows", e); } } + /** + * Write fragments of the row, which occupy the whole memory page. + * + * @param row Row to process. + * @param statHolder Statistics holder to track IO operations. + * @return Number of bytes written, {@link #COMPLETE} if the row was fully written. + * @throws IgniteCheckedException If failed. + */ + private int writeWholePages(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException { + if (row.size() < MIN_SIZE_FOR_DATA_PAGE) + return 0; + + assert row.link() == 0 : row.link(); + + int written = 0; + + do { + long pageId = reuseList == this ? takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder) : + reuseList.takeRecycledPage(); + + AbstractDataPageIO initIo = null; + + if (pageId == 0L) { + pageId = allocateDataPage(row.partition()); + + initIo = row.ioVersions().latest(); + } + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket. + pageId = initReusedPage(row, pageId, statHolder); + else // Page is taken from free space bucket. For in-memory mode partition must be changed. + pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); + + written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder); + + assert written != FAIL_I; // We can't fail here. + + memMetrics.incrementLargeEntriesPages(); + } + while (row.size() - written >= MIN_SIZE_FOR_DATA_PAGE); + + return written; + } + /** * @param row Row. * @param reusedPageId Reused page id. From 9ff593ffeffbeb2e36ee2ca45a6da947e9a318a1 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Fri, 7 Jun 2019 15:22:33 +0300 Subject: [PATCH 08/21] IGNITE-11584 Code cleanup, bench fixed after merge. --- .../pagemem/JmhCacheFreelistBenchmark.java | 15 +- .../processors/cache/GridCacheUtils.java | 34 +++ .../preloader/GridDhtPartitionDemander.java | 227 ++++++++---------- .../freelist/AbstractFreeList.java | 45 ++-- .../MemoryLeakAfterRebalanceSelfTest.java | 44 ++-- .../database/CacheFreeListSelfTest.java | 124 +++++++--- 6 files changed, 289 insertions(+), 200 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java index 013f63ba485f2..4f656494e1fa7 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java @@ -38,7 +38,7 @@ import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl; import org.apache.ignite.internal.processors.cache.persistence.Storable; import org.apache.ignite.internal.processors.cache.persistence.evict.NoOpPageEvictionTracker; -import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeListImpl; +import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeList; import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.stat.IoStatisticsHolder; @@ -248,8 +248,17 @@ private FreeList createFreeList( DataRegion dataRegion = new DataRegion(pageMem, plcCfg, regionMetrics, new NoOpPageEvictionTracker()); - return new CacheFreeListImpl(1, "freelist", regionMetrics, dataRegion, null, - null, metaPageId, true); + return new CacheFreeList( + 1, + "freelist", + regionMetrics, + dataRegion, + null, + null, + metaPageId, + true, + null + ); } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java index cc5dd42ef6cc9..337dcfd65cd3a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java @@ -54,6 +54,7 @@ import org.apache.ignite.cache.store.CacheStoreSessionListener; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.DataPageEvictionMode; import org.apache.ignite.configuration.DataRegionConfiguration; import org.apache.ignite.configuration.DataStorageConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; @@ -65,6 +66,7 @@ import org.apache.ignite.internal.cluster.ClusterGroupEmptyCheckedException; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException; +import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.distributed.GridDistributedLockCancelledException; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter; @@ -72,6 +74,7 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLog; +import org.apache.ignite.internal.processors.cache.persistence.DataRegion; import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage; import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx; import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry; @@ -1998,6 +2001,37 @@ public static boolean isCacheTemplateName(String cacheName) { return cacheName.endsWith("*"); } + /** + * @param memPlc Data region. + * @param size Data size in bytes. + * @return {@code True} if a specified amount of data can be stored in the memory region without evictions. + */ + public static boolean isEnoughSpaceForData(DataRegion memPlc, int size) { + DataRegionConfiguration plc = memPlc.config(); + + if (plc.isPersistenceEnabled() || plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED) + return true; + + PageMemory pageMem = memPlc.pageMemory(); + + int sysPageSize = pageMem.systemPageSize(); + + // The number of pages is calculated taking into account memory fragmentation and possible concurrent updates. + int pagesRequired = (size / sysPageSize) * 2; + + long maxPages = plc.getMaxSize() / sysPageSize; + + // There are enough pages left. + if (pagesRequired < maxPages - pageMem.loadedPages()) + return true; + + // Empty pages pool size restricted. + if (pagesRequired > plc.getEmptyPagesPoolSize()) + return false; + + return pagesRequired < maxPages * (1.0d - plc.getEvictionThreshold()); + } + /** * */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 554f12fe0ad2d..3e68852a7424a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -35,15 +35,12 @@ import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; -import org.apache.ignite.configuration.DataPageEvictionMode; -import org.apache.ignite.configuration.DataRegionConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.IgniteNodeAttributes; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; -import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection; @@ -56,6 +53,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheMvccEntryInfo; import org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; +import org.apache.ignite.internal.processors.cache.GridCacheUtils; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtInvalidPartitionException; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionTopology; @@ -63,7 +61,6 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware; import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState; import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; -import org.apache.ignite.internal.processors.cache.persistence.DataRegion; import org.apache.ignite.internal.processors.cache.persistence.RowStore; import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter; @@ -779,8 +776,6 @@ public void handleSupplyMessage( boolean last = supplyMsg.last().containsKey(p); - boolean batched = canStoreWithoutEvictions(supplyMsg.messageSize()); - if (part.state() == MOVING) { boolean reserved = part.reserve(); @@ -796,8 +791,13 @@ public void handleSupplyMessage( if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); - else + else { + // In-memory evictions can be configured in such a way that batch mode will lead to OOME. + boolean batched = GridCacheUtils.isEnoughSpaceForData(grp.dataRegion(), + supplyMsg.messageSize()); + preloadEntries(topVer, node, p, infos, batched); + } // If message was last for this partition, // then we take ownership. @@ -881,127 +881,6 @@ public void handleSupplyMessage( } } - /** - * @param size Data size. - * @return {@code True} if rebalanced cache group allows writing a specified amount of data without evictions. - */ - private boolean canStoreWithoutEvictions(int size) { - DataRegionConfiguration plc = grp.dataRegion().config(); - - if (plc.isPersistenceEnabled() || plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED) - return true; - - PageMemory pageMem = grp.dataRegion().pageMemory(); - - int sysPageSize = pageMem.systemPageSize(); - - // The number of pages is calculated taking into account memory fragmentation and possible concurrent updates. - int pagesRequired = (size / sysPageSize) * 2; - - long maxPages = plc.getMaxSize() / sysPageSize; - - // There are enough pages left. - if (pagesRequired < maxPages - pageMem.loadedPages()) - return true; - - // Empty pages pool size restricted. - if (pagesRequired > plc.getEmptyPagesPoolSize()) - return false; - - return pagesRequired < maxPages * (1.0d - plc.getEvictionThreshold()); - } - - /** - * @param from Node which sent entry. - * @param p Partition id. - * @param infos Preloaded entries. - * @param topVer Topology version. - * @param batchSize Batch size. - * @throws IgniteCheckedException If failed. - */ - private boolean preloadEntriesBatched( - AffinityTopologyVersion topVer, - ClusterNode from, - int p, - Iterator infos, - int batchSize - ) throws IgniteCheckedException { - Map> cctxs = new HashMap<>(); - - // Groupping by cache id, since we cannot place entries from different caches on the same page. - for (GridCacheEntryInfo e; infos.hasNext() && batchSize-- > 0; - e = infos.next(), cctxs.computeIfAbsent(e.cacheId(), v -> new ArrayList<>(8)).add(e)); - - for (Map.Entry> cctxEntry : cctxs.entrySet()) { - GridCacheContext cctx = - grp.sharedGroup() ? ctx.cacheContext(cctxEntry.getKey()) : grp.singleCacheContext(); - - if (cctx == null) - continue; - - if (cctx.isNear()) - cctx = cctx.dhtCache().context(); - - List cctxInfos = cctxEntry.getValue(); - - Iterator rowsItr = null; - - try { - GridDhtLocalPartition part = cctx.topology().localPartition(p); - - // Filter NULL values (it means that we should remove entry from cache). - Collection hasValues = F.view(cctxInfos, info -> info.value() != null); - - cctx.shared().database().ensureFreeSpace(cctx.dataRegion()); - - // Store all cache entries to data store before get locks. - rowsItr = cctx.offheap().storeAll(cctx, part, hasValues).iterator(); - - for (GridCacheEntryInfo info : cctxInfos) { - CacheDataRow row = info.value() == null ? null : rowsItr.next(); - - // Link created cache entry in BPlusTree. - if (!preloadEntry(from, p, info, topVer, cctx, row)) - return false; - - //TODO: IGNITE-11330: Update metrics for touched cache only. - for (GridCacheContext cctx0 : grp.caches()) { - if (cctx0.statisticsEnabled()) - cctx0.cache().metrics0().onRebalanceKeyReceived(); - } - } - } - catch (GridDhtInvalidPartitionException ignored) { - if (log.isDebugEnabled()) - log.debug("Partition became invalid during rebalancing (will ignore): " + p); - - return false; - } - finally { - // Remove all unprocessed rows on error. - while (rowsItr != null && rowsItr.hasNext()) - cleanupRow(cctx, rowsItr.next()); - } - } - - return true; - } - - /** - * Remove row from data store. - * - * @param cctx Cache context. - * @param row Row to remove. - * @throws IgniteCheckedException If failed. - */ - private void cleanupRow(GridCacheContext cctx, CacheDataRow row) throws IgniteCheckedException { - if (row != null) { - RowStore rowStore = cctx.offheap().dataStore(cctx.topology().localPartition(row.partition())).rowStore(); - - rowStore.removeRow(row.link(), grp.statisticsHolderData()); - } - } - /** * Adds mvcc entries with theirs history to partition p. * @@ -1155,6 +1034,96 @@ else if (cctx.isNear()) } } + /** + * @param from Node which sent entry. + * @param p Partition id. + * @param infos Preloaded entries. + * @param topVer Topology version. + * @param batchSize Batch size. + * @throws IgniteCheckedException If failed. + */ + private boolean preloadEntriesBatched( + AffinityTopologyVersion topVer, + ClusterNode from, + int p, + Iterator infos, + int batchSize + ) throws IgniteCheckedException { + Map> cctxs = new HashMap<>(); + + // Groupping by cache id, since we cannot place entries from different caches on the same page. + for (GridCacheEntryInfo e; infos.hasNext() && batchSize-- > 0; + e = infos.next(), cctxs.computeIfAbsent(e.cacheId(), v -> new ArrayList<>(8)).add(e)); + + for (Map.Entry> cctxEntry : cctxs.entrySet()) { + GridCacheContext cctx = + grp.sharedGroup() ? ctx.cacheContext(cctxEntry.getKey()) : grp.singleCacheContext(); + + if (cctx == null) + continue; + + if (cctx.isNear()) + cctx = cctx.dhtCache().context(); + + List cctxInfos = cctxEntry.getValue(); + + Iterator rowsIter = null; + + try { + GridDhtLocalPartition part = cctx.topology().localPartition(p); + + // Filter NULL values (it means that we should remove entry from cache). + Collection updates = F.view(cctxInfos, info -> info.value() != null); + + cctx.shared().database().ensureFreeSpace(cctx.dataRegion()); + + // Store all cache entries to data store before get locks. + rowsIter = cctx.offheap().storeAll(cctx, part, updates).iterator(); + + for (GridCacheEntryInfo info : cctxInfos) { + CacheDataRow row = info.value() == null ? null : rowsIter.next(); + + if (!preloadEntry(from, p, info, topVer, cctx, row)) + return false; + + //TODO: IGNITE-11330: Update metrics for touched cache only. + for (GridCacheContext cctx0 : grp.caches()) { + if (cctx0.statisticsEnabled()) + cctx0.cache().metrics0().onRebalanceKeyReceived(); + } + } + } + catch (GridDhtInvalidPartitionException ignored) { + if (log.isDebugEnabled()) + log.debug("Partition became invalid during rebalancing (will ignore): " + p); + + return false; + } + finally { + // Remove all unprocessed rows on error. + while (rowsIter != null && rowsIter.hasNext()) + cleanupRow(cctx, rowsIter.next()); + } + } + + return true; + } + + /** + * Remove row from data store. + * + * @param cctx Cache context. + * @param row Row to remove. + * @throws IgniteCheckedException If failed. + */ + private void cleanupRow(GridCacheContext cctx, CacheDataRow row) throws IgniteCheckedException { + if (row != null) { + RowStore rowStore = cctx.offheap().dataStore(cctx.topology().localPartition(row.partition())).rowStore(); + + rowStore.removeRow(row.link(), grp.statisticsHolderData()); + } + } + /** * Adds {@code entry} to partition {@code p}. * @@ -1173,7 +1142,7 @@ private boolean preloadEntry( GridCacheEntryInfo entry, AffinityTopologyVersion topVer, GridCacheContext cctx, - CacheDataRow row + @Nullable CacheDataRow row ) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); 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 9371b225ca987..24208795b9740 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 @@ -546,12 +546,18 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken } } - /** {@inheritDoc} */ + /** + * Reduces the workload on the free list by writing multiple rows into a single memory page at once.
+ *
+ * Rows are sequentially added to the page as long as there is enough free space on it. If the row is large then + * those fragments that occupy the whole memory page are written to other pages, and the remainder is added to the + * current one. + * + * @param rows Rows. + * @throws IgniteCheckedException If failed. + */ @Override public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { - if (rows.isEmpty()) - return; - try { Iterator iter = rows.iterator(); @@ -560,17 +566,19 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken int written = COMPLETE; while (iter.hasNext() || written != COMPLETE) { - if (written == COMPLETE) + if (written == COMPLETE) { row = iter.next(); - if ((written = writeWholePages(row, statHolder)) == COMPLETE) - continue; + // If the data row was completely written without remainder, proceed to the next. + if ((written = writeWholePages(row, statHolder)) == COMPLETE) + continue; + } int remaining = row.size() - written; long pageId = 0L; - if (remaining > 0 && remaining < MIN_SIZE_FOR_DATA_PAGE) { + if (remaining < MIN_SIZE_FOR_DATA_PAGE) { for (int b = bucket(remaining, false) + 1; b < REUSE_BUCKET; b++) { pageId = takeEmptyPage(b, row.ioVersions(), statHolder); @@ -616,23 +624,20 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken if (written == COMPLETE) { row = iter.next(); - written = 0; + // If the data row was completely written without remainder, proceed to the next. + if ((written = writeWholePages(row, statHolder)) == COMPLETE) + continue; - if (io.getFreeSpace(pageAddr) < (row.size() % MIN_SIZE_FOR_DATA_PAGE)) + if (io.getFreeSpace(pageAddr) < row.size() - written) break; } - if (written == 0) - written = writeWholePages(row, statHolder); + written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, lockLsnr, + writeRowKeepPage, initIo, wal, null, row, written, statHolder); - if (written != COMPLETE) { - written = PageHandler.writePage(pageMem, grpId, pageId, page, pageAddr, lockLsnr, - writeRowKeepPage, initIo, wal, null, row, written, statHolder); + initIo = null; - initIo = null; - - dirty = true; - } + dirty = true; assert written == COMPLETE; } @@ -649,7 +654,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken assert PageIO.getCrc(pageAddr) == 0; //TODO GG-11480 } finally { - // Should always unlock data page after write. + // Should always unlock data page after an update. assert writeRowKeepPage.releaseAfterWrite(grpId, pageId, page, pageAddr, row, 0); writeUnlock(pageId, page, pageAddr, dirty); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java index f22d14f7e3c12..6609f7a5c8330 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java @@ -25,7 +25,6 @@ import javax.cache.Cache; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteException; -import org.apache.ignite.IgniteSystemProperties; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; @@ -47,7 +46,6 @@ import org.apache.ignite.spi.IgniteSpiException; import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi; import org.apache.ignite.testframework.GridTestUtils; -import org.apache.ignite.testframework.junits.WithSystemProperty; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.After; import org.junit.Before; @@ -77,20 +75,25 @@ public static Iterable parameters() { @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + boolean isSupplierNode = getTestIgniteInstanceIndex(igniteInstanceName) == 0; + + cfg.setCommunicationSpi(new TestCommunicationSpi(isSupplierNode ? preloadStartLatch : null)); + cfg.setDataStorageConfiguration(new DataStorageConfiguration(). - setDefaultDataRegionConfiguration(new DataRegionConfiguration() + setDefaultDataRegionConfiguration( + new DataRegionConfiguration() + .setMaxSize(200 * 1024 * 1024) .setPersistenceEnabled(true))); - cfg.setRebalanceThreadPoolSize(4); + CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME); - cfg.setCacheConfiguration(new CacheConfiguration(DEFAULT_CACHE_NAME) - .setAffinity(new RendezvousAffinityFunction(false, 16)) - .setCacheMode(CacheMode.REPLICATED) - .setAtomicityMode(cacheAtomicityMode)); + ccfg.setCacheMode(CacheMode.REPLICATED); + ccfg.setAtomicityMode(cacheAtomicityMode); + ccfg.setAffinity(new RendezvousAffinityFunction(false, 16)); - boolean supplier = getTestIgniteInstanceIndex(igniteInstanceName) == 0; + cfg.setCacheConfiguration(ccfg); - cfg.setCommunicationSpi(new TestCommunicationSpi(supplier ? preloadStartLatch : null)); + cfg.setRebalanceThreadPoolSize(4); return cfg; } @@ -113,7 +116,6 @@ public void after() throws Exception { * @throws Exception If failed. */ @Test - @WithSystemProperty(key = IgniteSystemProperties.IGNITE_BASELINE_AUTO_ADJUST_ENABLED, value = "false") public void testPreloadingWithConcurrentUpdates() throws Exception { int size = GridTestUtils.SF.applyLB(500_000, 5_000); @@ -128,14 +130,14 @@ public void testPreloadingWithConcurrentUpdates() throws Exception { node.cluster().active(true); + node.cluster().baselineAutoAdjustTimeout(0); + // Load data. node.cache(DEFAULT_CACHE_NAME).putAll(data); // Start 2 node. IgniteEx node2 = startGrid(1); - node.cluster().setBaselineTopology(node.cluster().nodes()); - IgniteInternalCache cache = node2.cachex(DEFAULT_CACHE_NAME); // Simulate concurrent updates when preloading. @@ -162,7 +164,7 @@ public void testPreloadingWithConcurrentUpdates() throws Exception { GridCacheContext cctx = cache.context(); // Ensure that there are no duplicate entries left on data pages in page memory. - try (GridCloseableIterator> itr = cctx.offheap().cacheEntriesIterator( + try (GridCloseableIterator> iter = cctx.offheap().cacheEntriesIterator( cctx, true, true, @@ -171,8 +173,8 @@ public void testPreloadingWithConcurrentUpdates() throws Exception { null, true)) { - while (itr.hasNext()) { - Cache.Entry entry = itr.next(); + while (iter.hasNext()) { + Cache.Entry entry = iter.next(); Integer key = entry.getKey(); @@ -188,11 +190,11 @@ public void testPreloadingWithConcurrentUpdates() throws Exception { /** */ private static class TestCommunicationSpi extends TcpCommunicationSpi { /** */ - private final CountDownLatch latch; + private final CountDownLatch delaySupplyMessagesLatch; /** */ - private TestCommunicationSpi(CountDownLatch latch) { - this.latch = latch; + private TestCommunicationSpi(CountDownLatch delaySupplyMessagesLatch) { + this.delaySupplyMessagesLatch = delaySupplyMessagesLatch; } /** {@inheritDoc} */ @@ -205,8 +207,8 @@ private TestCommunicationSpi(CountDownLatch latch) { boolean supplyMsg = msg instanceof GridIoMessage && ((GridIoMessage)msg).message() instanceof GridDhtPartitionSupplyMessage; - if (supplyMsg && latch != null) - U.await(latch, 10, TimeUnit.SECONDS); + if (supplyMsg && delaySupplyMessagesLatch != null) + U.await(delaySupplyMessagesLatch, 10, TimeUnit.SECONDS); super.sendMessage(node, msg, ackC); } catch (IgniteInterruptedCheckedException e) { 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 1094d304ddb26..3848594935558 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 @@ -87,80 +87,80 @@ public class CacheFreeListSelfTest extends GridCommonAbstractTest { * @throws Exception if failed. */ @Test - public void testInsertDeleteSingleThreaded_1024() throws Exception { - checkInsertDeleteSingleThreaded(1024); + public void testInsertDeleteSingleThreaded_batched_1024() throws Exception { + checkInsertDeleteSingleThreaded(1024, true); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteSingleThreaded_2048() throws Exception { - checkInsertDeleteSingleThreaded(2048); + public void testInsertDeleteSingleThreaded_batched_2048() throws Exception { + checkInsertDeleteSingleThreaded(2048, true); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteSingleThreaded_4096() throws Exception { - checkInsertDeleteSingleThreaded(4096); + public void testInsertDeleteSingleThreaded_batched_4096() throws Exception { + checkInsertDeleteSingleThreaded(4096, true); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteSingleThreaded_8192() throws Exception { - checkInsertDeleteSingleThreaded(8192); + public void testInsertDeleteSingleThreaded_batched_8192() throws Exception { + checkInsertDeleteSingleThreaded(8192, true); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteSingleThreaded_16384() throws Exception { - checkInsertDeleteSingleThreaded(16384); + public void testInsertDeleteSingleThreaded_batched_16384() throws Exception { + checkInsertDeleteSingleThreaded(16384, true); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteMultiThreaded_batched_1024() throws Exception { - checkInsertDeleteMultiThreaded(1024, true); + public void testInsertDeleteSingleThreaded_1024() throws Exception { + checkInsertDeleteSingleThreaded(1024); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteMultiThreaded_batched_2048() throws Exception { - checkInsertDeleteMultiThreaded(2048, true); + public void testInsertDeleteSingleThreaded_2048() throws Exception { + checkInsertDeleteSingleThreaded(2048); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteMultiThreaded_batched_4096() throws Exception { - checkInsertDeleteMultiThreaded(4096, true); + public void testInsertDeleteSingleThreaded_4096() throws Exception { + checkInsertDeleteSingleThreaded(4096); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteMultiThreaded_batched_8192() throws Exception { - checkInsertDeleteMultiThreaded(8192, true); + public void testInsertDeleteSingleThreaded_8192() throws Exception { + checkInsertDeleteSingleThreaded(8192); } /** * @throws Exception if failed. */ @Test - public void testInsertDeleteMultiThreaded_batched_16384() throws Exception { - checkInsertDeleteMultiThreaded(16384, true); + public void testInsertDeleteSingleThreaded_16384() throws Exception { + checkInsertDeleteSingleThreaded(16384); } /** @@ -203,11 +203,51 @@ public void testInsertDeleteMultiThreaded_16384() throws Exception { checkInsertDeleteMultiThreaded(16384); } + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_1024() throws Exception { + checkInsertDeleteMultiThreaded(1024, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_2048() throws Exception { + checkInsertDeleteMultiThreaded(2048, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_4096() throws Exception { + checkInsertDeleteMultiThreaded(4096, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_8192() throws Exception { + checkInsertDeleteMultiThreaded(8192, true); + } + + /** + * @throws Exception if failed. + */ + @Test + public void testInsertDeleteMultiThreaded_batched_16384() throws Exception { + checkInsertDeleteMultiThreaded(16384, true); + } + /** * @param pageSize Page size. * @throws Exception If failed. */ - protected void checkInsertDeleteMultiThreaded(final int pageSize) throws Exception { + protected void checkInsertDeleteMultiThreaded(int pageSize) throws Exception { checkInsertDeleteMultiThreaded(pageSize, false); } @@ -216,7 +256,7 @@ protected void checkInsertDeleteMultiThreaded(final int pageSize) throws Excepti * @param batched Batch mode flag. * @throws Exception If failed. */ - protected void checkInsertDeleteMultiThreaded(final int pageSize, boolean batched) throws Exception { + protected void checkInsertDeleteMultiThreaded(final int pageSize, final boolean batched) throws Exception { final FreeList list = createFreeList(pageSize); Random rnd = new Random(); @@ -325,9 +365,19 @@ protected void checkInsertDeleteMultiThreaded(final int pageSize, boolean batche } /** - * @throws Exception if failed. + * @param pageSize Page size. + * @throws Exception If failed. */ protected void checkInsertDeleteSingleThreaded(int pageSize) throws Exception { + checkInsertDeleteSingleThreaded(pageSize, false); + } + + /** + * @param pageSize Page size. + * @param batched Batch mode flag. + * @throws Exception if failed. + */ + protected void checkInsertDeleteSingleThreaded(int pageSize, boolean batched) throws Exception { FreeList list = createFreeList(pageSize); Random rnd = new Random(); @@ -351,6 +401,8 @@ protected void checkInsertDeleteSingleThreaded(int pageSize) throws Exception { boolean grow = true; + List rows = new ArrayList<>(BATCH_SIZE); + for (int i = 0; i < 1_000_000; i++) { if (grow) { if (stored.size() > 20_000) { @@ -375,13 +427,31 @@ protected void checkInsertDeleteSingleThreaded(int pageSize) throws Exception { TestDataRow row = new TestDataRow(keySize, valSize); - list.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE); + if (batched) + rows.add(row); + else { + list.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE); - assertTrue(row.link() != 0L); + assertTrue(row.link() != 0L); + + TestDataRow old = stored.put(row.link(), row); + + assertNull(old); + } - TestDataRow old = stored.put(row.link(), row); + if (rows.size() == BATCH_SIZE) { + list.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE); - assertNull(old); + for (TestDataRow row0 : rows) { + assertTrue(row0.link() != 0L); + + TestDataRow old = stored.put(row0.link(), row0); + + assertNull(old); + } + + rows.clear(); + } } else { Iterator it = stored.values().iterator(); From 36774c3254002d06561ca3b23d63793f7191209f Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Thu, 13 Jun 2019 16:09:59 +0300 Subject: [PATCH 09/21] IGNITE-11584 Removed redundant groupping by cacheid. --- .../processors/cache/GridCacheUtils.java | 11 +- .../cache/IgniteCacheOffheapManager.java | 17 +- .../cache/IgniteCacheOffheapManagerImpl.java | 41 ++-- .../preloader/GridDhtPartitionDemander.java | 199 +++++++----------- .../persistence/GridCacheOffheapManager.java | 7 +- .../cache/persistence/RowStore.java | 1 + .../freelist/AbstractFreeList.java | 9 +- .../cache/persistence/freelist/FreeList.java | 5 + .../MemoryLeakAfterRebalanceSelfTest.java | 84 +++----- 9 files changed, 147 insertions(+), 227 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java index 337dcfd65cd3a..83421f31211f7 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java @@ -2002,22 +2002,23 @@ public static boolean isCacheTemplateName(String cacheName) { } /** + * Calculates whether there is enough free space in a region to store a specified amount of data. + * * @param memPlc Data region. * @param size Data size in bytes. * @return {@code True} if a specified amount of data can be stored in the memory region without evictions. */ - public static boolean isEnoughSpaceForData(DataRegion memPlc, int size) { + public static boolean isEnoughSpaceForData(DataRegion memPlc, long size) { DataRegionConfiguration plc = memPlc.config(); - if (plc.isPersistenceEnabled() || plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED) + if (size <= 0 || plc.isPersistenceEnabled() || plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED) return true; PageMemory pageMem = memPlc.pageMemory(); int sysPageSize = pageMem.systemPageSize(); - // The number of pages is calculated taking into account memory fragmentation and possible concurrent updates. - int pagesRequired = (size / sysPageSize) * 2; + long pagesRequired = Math.round(size / (double)sysPageSize); long maxPages = plc.getMaxSize() / sysPageSize; @@ -2029,7 +2030,7 @@ public static boolean isEnoughSpaceForData(DataRegion memPlc, int size) { if (pagesRequired > plc.getEmptyPagesPoolSize()) return false; - return pagesRequired < maxPages * (1.0d - plc.getEvictionThreshold()); + return pagesRequired < Math.round(maxPages * (1.0d - plc.getEvictionThreshold())); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index 507bbc96c46cd..950398054f1aa 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -192,15 +192,16 @@ public void invoke(GridCacheContext cctx, KeyCacheObject key, GridDhtLocalPartit throws IgniteCheckedException; /** - * @param cctx Cache context. + * Put entries into the data store. + * * @param part Partition. * @param entries Entries. + * @return Created rows. * @throws IgniteCheckedException If failed. */ - public List storeAll( - GridCacheContext cctx, + public Collection insertAll( GridDhtLocalPartition part, - Collection entries + Collection entries ) throws IgniteCheckedException; /** @@ -744,14 +745,14 @@ void update( /** - * @param cctx Cache context. + * Put entries into the data store. + * * @param entries Entries. * @return Created rows. * @throws IgniteCheckedException If failed. */ - public List storeAll( - GridCacheContext cctx, - Collection entries + public Collection insertAll( + Collection entries ) throws IgniteCheckedException; /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index ddb2ef8c00880..3850b69bd3dec 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -447,15 +447,6 @@ private Iterator cacheData(boolean primary, boolean backup, Affi dataStore(part).invoke(cctx, key, c); } - /** {@inheritDoc} */ - @Override public List storeAll( - GridCacheContext cctx, - GridDhtLocalPartition part, - Collection entries - ) throws IgniteCheckedException { - return dataStore(part).storeAll(cctx, entries); - } - /** {@inheritDoc} */ @Override public void update( GridCacheContext cctx, @@ -471,6 +462,14 @@ private Iterator cacheData(boolean primary, boolean backup, Affi dataStore(part).update(cctx, key, val, ver, expireTime, oldRow); } + /** {@inheritDoc} */ + @Override public Collection insertAll( + GridDhtLocalPartition part, + Collection entries + ) throws IgniteCheckedException { + return dataStore(part).insertAll(entries); + } + /** {@inheritDoc} */ @Override public boolean mvccInitialValue( GridCacheMapEntry entry, @@ -1704,9 +1703,8 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo } /** {@inheritDoc} */ - @Override public List storeAll( - GridCacheContext cctx, - Collection infos + @Override public Collection insertAll( + Collection infos ) throws IgniteCheckedException { if (!busyLock.enterBusy()) throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); @@ -1714,15 +1712,12 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo List rows = new ArrayList<>(infos.size()); try { - assert cctx.shared().database().checkpointLockIsHeldByThread(); - - assert !cctx.mvccEnabled(); - - int cacheId = cctx.group().storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID; - IoStatisticsHolder statHolder = grp.statisticsHolderData(); for (GridCacheEntryInfo info : infos) { + GridCacheContext cctx = + grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); + KeyCacheObject key = info.key(); CacheObject val = info.value(); @@ -1731,6 +1726,8 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo key.valueBytes(coCtx); val.valueBytes(coCtx); + int cacheId = grp.storeCacheIdInDataPage() ? info.cacheId() : CU.UNDEFINED_CACHE_ID; + DataRow row = makeDataRow(key, val, info.version(), info.expireTime(), cacheId); rows.add(row); @@ -1738,15 +1735,19 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo rowStore().addRows(rows, statHolder); - if (grp.sharedGroup() && !cctx.group().storeCacheIdInDataPage()) { + Iterator iter = infos.iterator(); + + if (grp.sharedGroup() && !grp.storeCacheIdInDataPage()) { for (CacheDataRow row : rows) - ((DataRow)row).cacheId(cctx.cacheId()); + ((DataRow)row).cacheId(iter.next().cacheId()); } } finally { busyLock.leaveBusy(); } + assert rows.size() == infos.size(); + return rows; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 3e68852a7424a..c24306e1bade7 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -53,7 +53,6 @@ import org.apache.ignite.internal.processors.cache.GridCacheMvccEntryInfo; import org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; -import org.apache.ignite.internal.processors.cache.GridCacheUtils; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtInvalidPartitionException; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionTopology; @@ -61,7 +60,6 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware; import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState; import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; -import org.apache.ignite.internal.processors.cache.persistence.RowStore; import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter; import org.apache.ignite.internal.util.future.GridCompoundFuture; @@ -85,6 +83,7 @@ import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_PART_LOADED; import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_STARTED; import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_STOPPED; +import static org.apache.ignite.internal.processors.cache.GridCacheUtils.isEnoughSpaceForData; import static org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState.MOVING; import static org.apache.ignite.internal.processors.dr.GridDrType.DR_NONE; import static org.apache.ignite.internal.processors.dr.GridDrType.DR_PRELOAD; @@ -789,24 +788,28 @@ public void handleSupplyMessage( try { Iterator infos = e.getValue().infos().iterator(); - if (grp.mvccEnabled()) - mvccPreloadEntries(topVer, node, p, infos); - else { - // In-memory evictions can be configured in such a way that batch mode will lead to OOME. - boolean batched = GridCacheUtils.isEnoughSpaceForData(grp.dataRegion(), - supplyMsg.messageSize()); - - preloadEntries(topVer, node, p, infos, batched); + try { + if (grp.mvccEnabled()) + mvccPreloadEntries(topVer, node, p, infos); + else if (isEnoughSpaceForData(grp.dataRegion(), supplyMsg.messageSize() * 2)) + preloadEntriesBatched(topVer, node, p, infos); + else + preloadEntries(topVer, node, p, infos); } - - // If message was last for this partition, - // then we take ownership. - if (last) { - fut.partitionDone(nodeId, p, true); - + catch (GridDhtInvalidPartitionException ignored) { if (log.isDebugEnabled()) - log.debug("Finished rebalancing partition: " + - "[" + demandRoutineInfo(topicId, nodeId, supplyMsg) + ", p=" + p + "]"); + log.debug("Partition became invalid during rebalancing (will ignore): " + p); + } + finally { + // If message was last for this partition, + // then we take ownership. + if (last) { + fut.partitionDone(nodeId, p, true); + + if (log.isDebugEnabled()) + log.debug("Finished rebalancing partition: " + + "[" + demandRoutineInfo(topicId, nodeId, supplyMsg) + ", p=" + p + "]"); + } } } finally { @@ -904,7 +907,7 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node ctx.database().checkpointReadLock(); try { - for (int i = 0; i < 100; i++) { + for (int i = 0; i < CHECKPOINT_THRESHOLD; i++) { boolean hasMore = infos.hasNext(); assert hasMore || !entryHist.isEmpty(); @@ -936,14 +939,7 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node } if (cctx != null) { - if (!mvccPreloadEntry(cctx, node, entryHist, topVer, p)) { - if (log.isTraceEnabled()) - log.trace("Got entries for invalid partition during " + - "preloading (will skip) [p=" + p + - ", entry=" + entryHist.get(entryHist.size() - 1) + ']'); - - return; // Skip current partition. - } + mvccPreloadEntry(cctx, node, entryHist, topVer, p); //TODO: IGNITE-11330: Update metrics for touched cache only. for (GridCacheContext ctx : grp.caches()) { @@ -974,11 +970,10 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node * @param p Partition id. * @param infos Entries info for preload. * @param topVer Topology version. - * @param batched Preload entries in batch mode. * @throws IgniteInterruptedCheckedException If interrupted. */ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - Iterator infos, boolean batched) throws IgniteCheckedException { + Iterator infos) throws IgniteCheckedException { GridCacheContext cctx = null; // Loop through all received entries and try to preload them. @@ -986,18 +981,6 @@ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, in ctx.database().checkpointReadLock(); try { - if (batched) { - if (!preloadEntriesBatched(topVer, node, p, infos, CHECKPOINT_THRESHOLD)) { - if (log.isTraceEnabled()) - log.trace("Got entries for invalid partition during " + - "preloading (will skip) [p=" + p + ']'); - - return; - } - - continue; - } - for (int i = 0; i < CHECKPOINT_THRESHOLD; i++) { if (!infos.hasNext()) break; @@ -1013,13 +996,7 @@ else if (cctx.isNear()) cctx = cctx.dhtCache().context(); } - if (!preloadEntry(node, p, entry, topVer, cctx, null)) { - if (log.isTraceEnabled()) - log.trace("Got entries for invalid partition during " + - "preloading (will skip) [p=" + p + ", entry=" + entry + ']'); - - return; - } + preloadEntry(node, p, entry, topVer, cctx, null); //TODO: IGNITE-11330: Update metrics for touched cache only. for (GridCacheContext ctx : grp.caches()) { @@ -1035,93 +1012,73 @@ else if (cctx.isNear()) } /** + * @param topVer Topology version. * @param from Node which sent entry. * @param p Partition id. * @param infos Preloaded entries. - * @param topVer Topology version. - * @param batchSize Batch size. * @throws IgniteCheckedException If failed. */ - private boolean preloadEntriesBatched( + private void preloadEntriesBatched( AffinityTopologyVersion topVer, ClusterNode from, int p, - Iterator infos, - int batchSize + Iterator infos ) throws IgniteCheckedException { - Map> cctxs = new HashMap<>(); - - // Groupping by cache id, since we cannot place entries from different caches on the same page. - for (GridCacheEntryInfo e; infos.hasNext() && batchSize-- > 0; - e = infos.next(), cctxs.computeIfAbsent(e.cacheId(), v -> new ArrayList<>(8)).add(e)); - - for (Map.Entry> cctxEntry : cctxs.entrySet()) { - GridCacheContext cctx = - grp.sharedGroup() ? ctx.cacheContext(cctxEntry.getKey()) : grp.singleCacheContext(); + while (infos.hasNext()) { + List batch = new ArrayList<>(CHECKPOINT_THRESHOLD); - if (cctx == null) - continue; + while (infos.hasNext() && batch.size() < CHECKPOINT_THRESHOLD) { + GridCacheEntryInfo info = infos.next(); - if (cctx.isNear()) - cctx = cctx.dhtCache().context(); + GridCacheContext cctx = grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); - List cctxInfos = cctxEntry.getValue(); + if (cctx != null) + batch.add(info); + } Iterator rowsIter = null; + ctx.database().checkpointReadLock(); + try { - GridDhtLocalPartition part = cctx.topology().localPartition(p); + GridDhtLocalPartition part = grp.topology().localPartition(p); - // Filter NULL values (it means that we should remove entry from cache). - Collection updates = F.view(cctxInfos, info -> info.value() != null); + try { + // Filter NULL values (this means we need to remove the cache entry). + Collection updates = F.view(batch, info -> info.value() != null); - cctx.shared().database().ensureFreeSpace(cctx.dataRegion()); + // Create data rows on data pages before getting locks on cache entries. + rowsIter = grp.offheap().insertAll(part, updates).iterator(); - // Store all cache entries to data store before get locks. - rowsIter = cctx.offheap().storeAll(cctx, part, updates).iterator(); + for (GridCacheEntryInfo info : batch) { + CacheDataRow row = info.value() == null ? null : rowsIter.next(); - for (GridCacheEntryInfo info : cctxInfos) { - CacheDataRow row = info.value() == null ? null : rowsIter.next(); + GridCacheContext cctx = + grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); - if (!preloadEntry(from, p, info, topVer, cctx, row)) - return false; + if (cctx.isNear()) + cctx = cctx.dhtCache().context(); - //TODO: IGNITE-11330: Update metrics for touched cache only. - for (GridCacheContext cctx0 : grp.caches()) { - if (cctx0.statisticsEnabled()) - cctx0.cache().metrics0().onRebalanceKeyReceived(); + if (!preloadEntry(from, p, info, topVer, cctx, row) && row != null) + part.dataStore().rowStore().removeRow(row.link(), grp.statisticsHolderData()); + + //TODO: IGNITE-11330: Update metrics for touched cache only. + for (GridCacheContext cctx0 : grp.caches()) { + if (cctx0.statisticsEnabled()) + cctx0.cache().metrics0().onRebalanceKeyReceived(); + } } } - } - catch (GridDhtInvalidPartitionException ignored) { - if (log.isDebugEnabled()) - log.debug("Partition became invalid during rebalancing (will ignore): " + p); - - return false; + finally { + // Remove all unprocessed rows. + while (rowsIter != null && rowsIter.hasNext()) + part.dataStore().rowStore().removeRow(rowsIter.next().link(), grp.statisticsHolderData()); + } } finally { - // Remove all unprocessed rows on error. - while (rowsIter != null && rowsIter.hasNext()) - cleanupRow(cctx, rowsIter.next()); + ctx.database().checkpointReadUnlock(); } } - - return true; - } - - /** - * Remove row from data store. - * - * @param cctx Cache context. - * @param row Row to remove. - * @throws IgniteCheckedException If failed. - */ - private void cleanupRow(GridCacheContext cctx, CacheDataRow row) throws IgniteCheckedException { - if (row != null) { - RowStore rowStore = cctx.offheap().dataStore(cctx.topology().localPartition(row.partition())).rowStore(); - - rowStore.removeRow(row.link(), grp.statisticsHolderData()); - } } /** @@ -1133,7 +1090,7 @@ private void cleanupRow(GridCacheContext cctx, CacheDataRow row) throws IgniteCh * @param topVer Topology version. * @param cctx Cache context. * @param row Pre-created data row, associated with this cache entry. - * @return {@code False} if partition has become invalid during preloading. + * @return {@code True} if the initial value was set for the specified cache entry. * @throws IgniteInterruptedCheckedException If interrupted. */ private boolean preloadEntry( @@ -1179,12 +1136,12 @@ private boolean preloadEntry( cctx.events().addEvent(cached.partition(), cached.key(), cctx.localNodeId(), null, null, null, EVT_CACHE_REBALANCE_OBJECT_LOADED, entry.value(), true, null, false, null, null, null, true); + + return true; } else { cached.touch(); // Start tracking. - cleanupRow(cctx, row); // Remove pre-created row. - if (log.isTraceEnabled()) log.trace("Rebalancing entry is already in cache (will ignore) [key=" + cached.key() + ", part=" + p + ']'); @@ -1195,14 +1152,6 @@ private boolean preloadEntry( if (log.isTraceEnabled()) log.trace("Entry has been concurrently removed while rebalancing (will ignore) [key=" + cached.key() + ", part=" + p + ']'); - - cleanupRow(cctx, row); - } - catch (GridDhtInvalidPartitionException ignored) { - if (log.isDebugEnabled()) - log.debug("Partition became invalid during rebalancing (will ignore): " + p); - - return false; } } catch (IgniteInterruptedCheckedException e) { @@ -1213,7 +1162,7 @@ private boolean preloadEntry( ctx.localNode() + ", node=" + from.id() + ", key=" + entry.key() + ", part=" + p + ']', e); } - return true; + return false; } /** @@ -1224,7 +1173,7 @@ private boolean preloadEntry( * @param history Mvcc entry history. * @param topVer Topology version. * @param p Partition id. - * @return {@code False} if partition has become invalid during preloading. + * @return {@code True} if the initial value was set for the specified cache entry. * @throws IgniteInterruptedCheckedException If interrupted. */ private boolean mvccPreloadEntry( @@ -1257,6 +1206,8 @@ private boolean mvccPreloadEntry( cctx.events().addEvent(cached.partition(), cached.key(), cctx.localNodeId(), null, null, null, EVT_CACHE_REBALANCE_OBJECT_LOADED, null, true, null, false, null, null, null, true); + + return true; } else { cached.touch(); // Start tracking. @@ -1271,12 +1222,6 @@ private boolean mvccPreloadEntry( log.trace("Entry has been concurrently removed while rebalancing (will ignore) [key=" + cached.key() + ", part=" + p + ']'); } - catch (GridDhtInvalidPartitionException ignored) { - if (log.isDebugEnabled()) - log.debug("Partition became invalid during rebalancing (will ignore): " + p); - - return false; - } } catch (IgniteInterruptedCheckedException | ClusterTopologyCheckedException e) { throw e; @@ -1286,7 +1231,7 @@ private boolean mvccPreloadEntry( ctx.localNode() + ", node=" + from.id() + ", key=" + info.key() + ", part=" + p + ']', e); } - return true; + return false; } /** 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 4e015552ddf9d..35689872e50d9 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 @@ -2402,15 +2402,14 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { } /** {@inheritDoc} */ - @Override public List storeAll( - GridCacheContext cctx, - Collection entries + @Override public Collection insertAll( + Collection entries ) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); CacheDataStore delegate = init0(false); - return delegate.storeAll(cctx, entries); + return delegate.insertAll(entries); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index 28c3b210698c7..55a5157524527 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -120,6 +120,7 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit /** * @param rows Rows. + * @param statHolder Statistics holder to track IO operations. * @throws IgniteCheckedException If failed. */ public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { 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 24208795b9740..b38f8ec362886 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 @@ -138,9 +138,7 @@ private final class UpdateRowHandler extends PageHandler { /** Write handler which doesn't put memory page into the free list after an update. */ private final PageHandler writeRowKeepPage = new WriteRowHandler(false); - /** - * - */ + /** */ private final class WriteRowHandler extends PageHandler { /** */ private final boolean putPageIntoFreeList; @@ -554,6 +552,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken * current one. * * @param rows Rows. + * @param statHolder Statistics holder to track IO operations. * @throws IgniteCheckedException If failed. */ @Override public void insertDataRows(Collection rows, @@ -614,11 +613,11 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken assert pageAddr != 0; - AbstractDataPageIO io = initIo == null ? PageIO.getPageIO(pageAddr) : initIo; - boolean dirty = false; try { + AbstractDataPageIO io = initIo == null ? PageIO.getPageIO(pageAddr) : initIo; + // Fill the page up to the end. while (iter.hasNext() || written != COMPLETE) { if (written == COMPLETE) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java index 8af592b2fe193..0753fd7dd9f0e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java @@ -29,12 +29,14 @@ public interface FreeList { /** * @param row Row. + * @param statHolder Statistics holder to track IO operations. * @throws IgniteCheckedException If failed. */ public void insertDataRow(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException; /** * @param rows Rows. + * @param statHolder Statistics holder to track IO operations. * @throws IgniteCheckedException If failed. */ public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException; @@ -42,6 +44,7 @@ public interface FreeList { /** * @param link Row link. * @param row New row data. + * @param statHolder Statistics holder to track IO operations. * @return {@code True} if was able to update row. * @throws IgniteCheckedException If failed. */ @@ -53,6 +56,7 @@ public interface FreeList { * @param arg Handler argument. * @param Argument type. * @param Result type. + * @param statHolder Statistics holder to track IO operations. * @return Result. * @throws IgniteCheckedException If failed. */ @@ -61,6 +65,7 @@ public R updateDataRow(long link, PageHandler pageHnd, S arg, IoSta /** * @param link Row link. + * @param statHolder Statistics holder to track IO operations. * @throws IgniteCheckedException If failed. */ public void removeDataRowByLink(long link, IoStatisticsHolder statHolder) throws IgniteCheckedException; diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java index 6609f7a5c8330..19809d674d46e 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/MemoryLeakAfterRebalanceSelfTest.java @@ -20,31 +20,24 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import javax.cache.Cache; import org.apache.ignite.Ignite; -import org.apache.ignite.IgniteException; +import org.apache.ignite.IgniteCache; import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; -import org.apache.ignite.cluster.ClusterNode; 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.IgniteEx; -import org.apache.ignite.internal.IgniteInterruptedCheckedException; -import org.apache.ignite.internal.managers.communication.GridIoMessage; +import org.apache.ignite.internal.TestRecordingCommunicationSpi; import org.apache.ignite.internal.processors.cache.GridCacheContext; +import org.apache.ignite.internal.processors.cache.GridCacheGroupIdMessage; import org.apache.ignite.internal.processors.cache.IgniteInternalCache; import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage; import org.apache.ignite.internal.util.lang.GridCloseableIterator; import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.lang.IgniteInClosure; -import org.apache.ignite.plugin.extensions.communication.Message; -import org.apache.ignite.spi.IgniteSpiException; -import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi; import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.After; @@ -58,9 +51,6 @@ */ @RunWith(Parameterized.class) public class MemoryLeakAfterRebalanceSelfTest extends GridCommonAbstractTest { - /** */ - private final CountDownLatch preloadStartLatch = new CountDownLatch(1); - /** */ @Parameterized.Parameter public CacheAtomicityMode cacheAtomicityMode; @@ -75,9 +65,7 @@ public static Iterable parameters() { @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); - boolean isSupplierNode = getTestIgniteInstanceIndex(igniteInstanceName) == 0; - - cfg.setCommunicationSpi(new TestCommunicationSpi(isSupplierNode ? preloadStartLatch : null)); + cfg.setCommunicationSpi(new TestRecordingCommunicationSpi()); cfg.setDataStorageConfiguration(new DataStorageConfiguration(). setDefaultDataRegionConfiguration( @@ -126,30 +114,38 @@ public void testPreloadingWithConcurrentUpdates() throws Exception { data.put(i, i + " v.1"); // Start 1 node. - Ignite node = startGrid(0); + Ignite node0 = startGrid(0); + + node0.cluster().active(true); - node.cluster().active(true); + node0.cluster().baselineAutoAdjustTimeout(0); - node.cluster().baselineAutoAdjustTimeout(0); + IgniteCache cache0 = node0.cache(DEFAULT_CACHE_NAME); // Load data. - node.cache(DEFAULT_CACHE_NAME).putAll(data); + cache0.putAll(data); + + TestRecordingCommunicationSpi.spi(node0) + .blockMessages((node, msg) -> + msg instanceof GridDhtPartitionSupplyMessage + && ((GridCacheGroupIdMessage)msg).groupId() == groupIdForCache(node0, DEFAULT_CACHE_NAME) + ); // Start 2 node. - IgniteEx node2 = startGrid(1); + IgniteEx node1 = startGrid(1); - IgniteInternalCache cache = node2.cachex(DEFAULT_CACHE_NAME); + TestRecordingCommunicationSpi.spi(node0).waitForBlocked(); // Simulate concurrent updates when preloading. for (int i = 0; i < size; i += 10) { String val = i + " v.2"; - cache.put(i, val); + cache0.put(i, val); data.put(i, val); // Start preloading. - if (i > size / 2) - preloadStartLatch.countDown(); + if (i == size / 2) + TestRecordingCommunicationSpi.spi(node0).stopBlock(); } awaitPartitionMapExchange(); @@ -159,11 +155,13 @@ public void testPreloadingWithConcurrentUpdates() throws Exception { awaitPartitionMapExchange(); - assertEquals(data.size(), cache.size()); + IgniteInternalCache cache1 = node1.cachex(DEFAULT_CACHE_NAME); + + assertEquals(data.size(), cache1.size()); - GridCacheContext cctx = cache.context(); + GridCacheContext cctx = cache1.context(); - // Ensure that there are no duplicate entries left on data pages in page memory. + // Make sure that there are no duplicate entries on the data pages in the pages memory. try (GridCloseableIterator> iter = cctx.offheap().cacheEntriesIterator( cctx, true, @@ -186,34 +184,4 @@ public void testPreloadingWithConcurrentUpdates() throws Exception { assertTrue(data.isEmpty()); } - - /** */ - private static class TestCommunicationSpi extends TcpCommunicationSpi { - /** */ - private final CountDownLatch delaySupplyMessagesLatch; - - /** */ - private TestCommunicationSpi(CountDownLatch delaySupplyMessagesLatch) { - this.delaySupplyMessagesLatch = delaySupplyMessagesLatch; - } - - /** {@inheritDoc} */ - @Override public void sendMessage( - final ClusterNode node, - final Message msg, - final IgniteInClosure ackC - ) throws IgniteSpiException { - try { - boolean supplyMsg = msg instanceof GridIoMessage && - ((GridIoMessage)msg).message() instanceof GridDhtPartitionSupplyMessage; - - if (supplyMsg && delaySupplyMessagesLatch != null) - U.await(delaySupplyMessagesLatch, 10, TimeUnit.SECONDS); - - super.sendMessage(node, msg, ackC); - } catch (IgniteInterruptedCheckedException e) { - throw new IgniteSpiException(e); - } - } - } } From f808fbd26a65ed5fedb9c16e21a252628d1d4450 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 25 Jun 2019 17:39:09 +0300 Subject: [PATCH 10/21] IGNITE-11584 Use subList instead of arraylist creation, redundant cctx removed. --- .../pagemem/JmhCacheFreelistBenchmark.java | 4 +- .../cache/IgniteCacheOffheapManager.java | 41 +++----- .../cache/IgniteCacheOffheapManagerImpl.java | 98 ++++++++----------- .../preloader/GridDhtPartitionDemander.java | 55 ++++++----- .../persistence/GridCacheOffheapManager.java | 15 ++- .../cache/persistence/RowStore.java | 2 +- 6 files changed, 100 insertions(+), 115 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java index 4f656494e1fa7..381547317ad70 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java @@ -27,6 +27,8 @@ import org.apache.ignite.IgniteLogger; import org.apache.ignite.configuration.DataRegionConfiguration; import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider; +import org.apache.ignite.internal.metric.IoStatisticsHolder; +import org.apache.ignite.internal.metric.IoStatisticsHolderNoOp; import org.apache.ignite.internal.pagemem.PageIdAllocator; import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl; @@ -41,8 +43,6 @@ import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeList; import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; -import org.apache.ignite.internal.stat.IoStatisticsHolder; -import org.apache.ignite.internal.stat.IoStatisticsHolderNoOp; import org.apache.ignite.logger.java.JavaLogger; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index 950398054f1aa..64ca754d920a8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -191,19 +191,6 @@ public boolean expire(GridCacheContext cctx, IgniteInClosure2X insertAll( - GridDhtLocalPartition part, - Collection entries - ) throws IgniteCheckedException; - /** * @param cctx Cache context. * @param key Key. @@ -708,6 +695,22 @@ CacheDataRow createRow( long expireTime, @Nullable CacheDataRow oldRow) throws IgniteCheckedException; + /** + * Create data rows. + * + * @param infos Entry infos. + * @return Created rows. + * @throws IgniteCheckedException If failed. + */ + public Collection createRows( + Collection infos + ) throws IgniteCheckedException; + + /** + * @param row Row removed from cache. + */ + public void removeRow(CacheDataRow row) throws IgniteCheckedException; + /** * @param cctx Cache context. * @param cleanupRows Rows to cleanup. @@ -743,18 +746,6 @@ void update( long expireTime, @Nullable CacheDataRow oldRow) throws IgniteCheckedException; - - /** - * Put entries into the data store. - * - * @param entries Entries. - * @return Created rows. - * @throws IgniteCheckedException If failed. - */ - public Collection insertAll( - Collection entries - ) throws IgniteCheckedException; - /** * @param cctx Cache context. * @param key Key. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index 3850b69bd3dec..d2d2a3a162beb 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -462,14 +462,6 @@ private Iterator cacheData(boolean primary, boolean backup, Affi dataStore(part).update(cctx, key, val, ver, expireTime, oldRow); } - /** {@inheritDoc} */ - @Override public Collection insertAll( - GridDhtLocalPartition part, - Collection entries - ) throws IgniteCheckedException { - return dataStore(part).insertAll(entries); - } - /** {@inheritDoc} */ @Override public boolean mvccInitialValue( GridCacheMapEntry entry, @@ -1702,55 +1694,6 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo } } - /** {@inheritDoc} */ - @Override public Collection insertAll( - Collection infos - ) throws IgniteCheckedException { - if (!busyLock.enterBusy()) - throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); - - List rows = new ArrayList<>(infos.size()); - - try { - IoStatisticsHolder statHolder = grp.statisticsHolderData(); - - for (GridCacheEntryInfo info : infos) { - GridCacheContext cctx = - grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); - - KeyCacheObject key = info.key(); - CacheObject val = info.value(); - - CacheObjectContext coCtx = cctx.cacheObjectContext(); - - key.valueBytes(coCtx); - val.valueBytes(coCtx); - - int cacheId = grp.storeCacheIdInDataPage() ? info.cacheId() : CU.UNDEFINED_CACHE_ID; - - DataRow row = makeDataRow(key, val, info.version(), info.expireTime(), cacheId); - - rows.add(row); - } - - rowStore().addRows(rows, statHolder); - - Iterator iter = infos.iterator(); - - if (grp.sharedGroup() && !grp.storeCacheIdInDataPage()) { - for (CacheDataRow row : rows) - ((DataRow)row).cacheId(iter.next().cacheId()); - } - } - finally { - busyLock.leaveBusy(); - } - - assert rows.size() == infos.size(); - - return rows; - } - /** {@inheritDoc} */ @Override public CacheDataRow createRow( GridCacheContext cctx, @@ -1782,6 +1725,47 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo return dataRow; } + /** {@inheritDoc} */ + @Override public Collection createRows( + Collection infos + ) throws IgniteCheckedException { + if (!busyLock.enterBusy()) + throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); + + List rows = new ArrayList<>(infos.size()); + + for (GridCacheEntryInfo info : infos) { + rows.add(makeDataRow(info.key(), + info.value(), + info.version(), + info.expireTime(), + grp.storeCacheIdInDataPage() ? info.cacheId() : CU.UNDEFINED_CACHE_ID)); + } + + try { + rowStore.addRows(rows, grp.statisticsHolderData()); + + Iterator iter = infos.iterator(); + + if (grp.sharedGroup() && !grp.storeCacheIdInDataPage()) { + for (DataRow row : rows) + row.cacheId(iter.next().cacheId()); + } + } + finally { + busyLock.leaveBusy(); + } + + return rows; + } + + /** {@inheritDoc} */ + @Override public void removeRow(CacheDataRow row) throws IgniteCheckedException { + assert row != null; + + rowStore.removeRow(row.link(), grp.statisticsHolderData()); + } + /** * @param key Cache key. * @param val Cache value. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index c24306e1bade7..f187eabaeb0fa 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -786,7 +786,7 @@ public void handleSupplyMessage( part.beforeApplyBatch(last); try { - Iterator infos = e.getValue().infos().iterator(); + List infos = e.getValue().infos(); try { if (grp.mvccEnabled()) @@ -894,21 +894,23 @@ else if (isEnoughSpaceForData(grp.dataRegion(), supplyMsg.messageSize() * 2)) * @throws IgniteInterruptedCheckedException If interrupted. */ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - Iterator infos) throws IgniteCheckedException { - if (!infos.hasNext()) + Collection infos) throws IgniteCheckedException { + if (infos.isEmpty()) return; List entryHist = new ArrayList<>(); GridCacheContext cctx = grp.sharedGroup() ? null : grp.singleCacheContext(); + Iterator iter = infos.iterator(); + // Loop through all received entries and try to preload them. - while (infos.hasNext() || !entryHist.isEmpty()) { + while (iter.hasNext() || !entryHist.isEmpty()) { ctx.database().checkpointReadLock(); try { for (int i = 0; i < CHECKPOINT_THRESHOLD; i++) { - boolean hasMore = infos.hasNext(); + boolean hasMore = iter.hasNext(); assert hasMore || !entryHist.isEmpty(); @@ -917,7 +919,7 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node boolean flushHistory; if (hasMore) { - entry = (GridCacheMvccEntryInfo)infos.next(); + entry = (GridCacheMvccEntryInfo)iter.next(); GridCacheMvccEntryInfo prev = entryHist.isEmpty() ? null : entryHist.get(0); @@ -973,19 +975,21 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node * @throws IgniteInterruptedCheckedException If interrupted. */ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - Iterator infos) throws IgniteCheckedException { + Iterable infos) throws IgniteCheckedException { GridCacheContext cctx = null; + Iterator iter = infos.iterator(); + // Loop through all received entries and try to preload them. - while (infos.hasNext()) { + while (iter.hasNext()) { ctx.database().checkpointReadLock(); try { for (int i = 0; i < CHECKPOINT_THRESHOLD; i++) { - if (!infos.hasNext()) + if (!iter.hasNext()) break; - GridCacheEntryInfo entry = infos.next(); + GridCacheEntryInfo entry = iter.next(); if (cctx == null || (grp.sharedGroup() && entry.cacheId() != cctx.cacheId())) { cctx = grp.sharedGroup() ? grp.shared().cacheContext(entry.cacheId()) : grp.singleCacheContext(); @@ -1022,21 +1026,15 @@ private void preloadEntriesBatched( AffinityTopologyVersion topVer, ClusterNode from, int p, - Iterator infos + List infos ) throws IgniteCheckedException { - while (infos.hasNext()) { - List batch = new ArrayList<>(CHECKPOINT_THRESHOLD); - - while (infos.hasNext() && batch.size() < CHECKPOINT_THRESHOLD) { - GridCacheEntryInfo info = infos.next(); - - GridCacheContext cctx = grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); + int batchOff = 0; - if (cctx != null) - batch.add(info); - } + while (batchOff < infos.size()) { + List batch = + infos.subList(batchOff, Math.min(infos.size(), batchOff += CHECKPOINT_THRESHOLD)); - Iterator rowsIter = null; + Iterator rowsIter = null; ctx.database().checkpointReadLock(); @@ -1048,7 +1046,7 @@ private void preloadEntriesBatched( Collection updates = F.view(batch, info -> info.value() != null); // Create data rows on data pages before getting locks on cache entries. - rowsIter = grp.offheap().insertAll(part, updates).iterator(); + rowsIter = part.dataStore().createRows(updates).iterator(); for (GridCacheEntryInfo info : batch) { CacheDataRow row = info.value() == null ? null : rowsIter.next(); @@ -1056,11 +1054,14 @@ private void preloadEntriesBatched( GridCacheContext cctx = grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); - if (cctx.isNear()) + if (cctx != null && cctx.isNear()) cctx = cctx.dhtCache().context(); - if (!preloadEntry(from, p, info, topVer, cctx, row) && row != null) - part.dataStore().rowStore().removeRow(row.link(), grp.statisticsHolderData()); + if ((cctx == null || !preloadEntry(from, p, info, topVer, cctx, row)) && row != null) + part.dataStore().removeRow(row); + + if (cctx == null) + continue; //TODO: IGNITE-11330: Update metrics for touched cache only. for (GridCacheContext cctx0 : grp.caches()) { @@ -1072,7 +1073,7 @@ private void preloadEntriesBatched( finally { // Remove all unprocessed rows. while (rowsIter != null && rowsIter.hasNext()) - part.dataStore().rowStore().removeRow(rowsIter.next().link(), grp.statisticsHolderData()); + part.dataStore().removeRow(rowsIter.next()); } } finally { 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 35689872e50d9..a6f802a4b9b85 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 @@ -2376,6 +2376,15 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { return delegate.createRow(cctx, key, val, ver, expireTime, oldRow); } + /** {@inheritDoc} */ + @Override public void removeRow(CacheDataRow row) throws IgniteCheckedException { + assert ctx.database().checkpointLockIsHeldByThread(); + + CacheDataStore delegate = init0(false); + + delegate.removeRow(row); + } + /** {@inheritDoc} */ @Override public int cleanup(GridCacheContext cctx, @Nullable List cleanupRows) throws IgniteCheckedException { @@ -2402,14 +2411,14 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { } /** {@inheritDoc} */ - @Override public Collection insertAll( - Collection entries + @Override public Collection createRows( + Collection infos ) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); CacheDataStore delegate = init0(false); - return delegate.insertAll(entries); + return delegate.createRows(infos); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index 55a5157524527..cc23d9389febc 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -123,7 +123,7 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit * @param statHolder Statistics holder to track IO operations. * @throws IgniteCheckedException If failed. */ - public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { + public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); freeList.insertDataRows(rows, statHolder); From 9f8c93373b06c7516f59e82e8581ebb749e4edd5 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Thu, 27 Jun 2019 20:37:08 +0300 Subject: [PATCH 11/21] IGNITE-11584 Experimentally combine entry infos and data rows collections with bituple. --- .../cache/IgniteCacheOffheapManager.java | 13 +++++ .../cache/IgniteCacheOffheapManagerImpl.java | 34 ++++++++++++- .../preloader/GridDhtPartitionDemander.java | 48 ++++++++++--------- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index 64ca754d920a8..d6024404f22c8 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -452,6 +452,19 @@ public GridIterator cachePartitionIterator(int cacheId, final int public GridCloseableIterator reservedIterator(final int part, final AffinityTopologyVersion topVer) throws IgniteCheckedException; + /** + * Pre-create cache data rows. + * + * @param part Partition number. + * @param infos Entry infos. + * @return Closeable iterator that removes unprocessed rows from page memory on close. + * @throws IgniteCheckedException If failed. + */ + public GridCloseableIterator> preloadRows( + int part, + Collection infos + ) throws IgniteCheckedException; + /** * @param parts Partitions. * @return Partition data iterator. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index d2d2a3a162beb..55986e25cbbb5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -1176,6 +1176,37 @@ private long allocateForTree() throws IgniteCheckedException { }; } + /** {@inheritDoc} */ + @Override public GridCloseableIterator> preloadRows( + int partId, + Collection infos + ) throws IgniteCheckedException { + CacheDataStore dataStore = dataStore(partId); + + Collection rows = dataStore.createRows(F.view(infos, info -> info.value() != null)); + + return new GridCloseableIteratorAdapter>() { + private final Iterator rowsIter = rows.iterator(); + private final Iterator infosIter = infos.iterator(); + + @Override protected IgniteBiTuple onNext() { + GridCacheEntryInfo info = infosIter.next(); + + return new IgniteBiTuple<>(info, info.value() == null ? null : rowsIter.next()); + } + + @Override protected boolean onHasNext() { + return infosIter.hasNext(); + } + + @Override protected void onClose() throws IgniteCheckedException { + while (rowsIter.hasNext()) + dataStore.removeRow(rowsIter.next()); + + } + }; + } + /** {@inheritDoc} */ @Override public IgniteRebalanceIterator rebalanceIterator(IgniteDhtDemandedPartitionsMap parts, final AffinityTopologyVersion topVer) @@ -1761,7 +1792,8 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo /** {@inheritDoc} */ @Override public void removeRow(CacheDataRow row) throws IgniteCheckedException { - assert row != null; + if (row == null) + return; rowStore.removeRow(row.link(), grp.statisticsHolderData()); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index f187eabaeb0fa..4269dcf177f0c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -65,11 +65,11 @@ import org.apache.ignite.internal.util.future.GridCompoundFuture; import org.apache.ignite.internal.util.future.GridFinishedFuture; import org.apache.ignite.internal.util.future.GridFutureAdapter; +import org.apache.ignite.internal.util.lang.GridCloseableIterator; import org.apache.ignite.internal.util.lang.IgniteInClosureX; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.CI1; -import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.S; @@ -1031,33 +1031,27 @@ private void preloadEntriesBatched( int batchOff = 0; while (batchOff < infos.size()) { - List batch = + Collection batch = infos.subList(batchOff, Math.min(infos.size(), batchOff += CHECKPOINT_THRESHOLD)); - Iterator rowsIter = null; - ctx.database().checkpointReadLock(); try { GridDhtLocalPartition part = grp.topology().localPartition(p); - try { - // Filter NULL values (this means we need to remove the cache entry). - Collection updates = F.view(batch, info -> info.value() != null); - - // Create data rows on data pages before getting locks on cache entries. - rowsIter = part.dataStore().createRows(updates).iterator(); + // Create data rows on data pages before getting locks on cache entries. + try (GridCloseableIterator> iter = + grp.offheap().preloadRows(p, batch)) { - for (GridCacheEntryInfo info : batch) { - CacheDataRow row = info.value() == null ? null : rowsIter.next(); + while (iter.hasNext()) { + Map.Entry e = iter.next(); - GridCacheContext cctx = - grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); + GridCacheEntryInfo info = e.getKey(); + CacheDataRow row = e.getValue(); - if (cctx != null && cctx.isNear()) - cctx = cctx.dhtCache().context(); + GridCacheContext cctx = resolveCacheContext(info); - if ((cctx == null || !preloadEntry(from, p, info, topVer, cctx, row)) && row != null) + if (cctx == null || !preloadEntry(from, p, info, topVer, cctx, row)) part.dataStore().removeRow(row); if (cctx == null) @@ -1070,11 +1064,6 @@ private void preloadEntriesBatched( } } } - finally { - // Remove all unprocessed rows. - while (rowsIter != null && rowsIter.hasNext()) - part.dataStore().removeRow(rowsIter.next()); - } } finally { ctx.database().checkpointReadUnlock(); @@ -1246,6 +1235,21 @@ private String demandRoutineInfo(int topicId, UUID supplier, GridDhtPartitionSup return "grp=" + grp.cacheOrGroupName() + ", topVer=" + supplyMsg.topologyVersion() + ", supplier=" + supplier + ", topic=" + topicId; } + /** + * Get cache context. + * + * @param info Preloading entry. + * @return Cache context or {@code null} if context does not exists.. + */ + private GridCacheContext resolveCacheContext(GridCacheEntryInfo info) { + GridCacheContext cctx = grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); + + if (cctx != null && cctx.isNear()) + cctx = cctx.dhtCache().context(); + + return cctx; + } + /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridDhtPartitionDemander.class, this); From 9180cbaee4b9ebdbd2f9c00496e7b17e207a6222 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Fri, 28 Jun 2019 15:20:57 +0300 Subject: [PATCH 12/21] IGNITE-11586 (Minor) method renamed, javadoc updated. --- .../processors/cache/IgniteCacheOffheapManager.java | 7 ++++--- .../processors/cache/IgniteCacheOffheapManagerImpl.java | 2 +- .../dht/preloader/GridDhtPartitionDemander.java | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index d6024404f22c8..72468e609fb8b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -453,14 +453,15 @@ public GridCloseableIterator reservedIterator(final int part, fina throws IgniteCheckedException; /** - * Pre-create cache data rows. + * Create a batch of data rows in page memory which is not associated with the cache entries. * * @param part Partition number. * @param infos Entry infos. - * @return Closeable iterator that removes unprocessed rows from page memory on close. + * @return {@link AutoCloseable} iterator with the mapping of source entry info to created cache data row (row can + * be {@code null} if the source value is {@code null}). Iterator automatically removes unprocessed rows on close. * @throws IgniteCheckedException If failed. */ - public GridCloseableIterator> preloadRows( + public GridCloseableIterator> allocateRows( int part, Collection infos ) throws IgniteCheckedException; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index 55986e25cbbb5..6e8cbab4f5ef6 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -1177,7 +1177,7 @@ private long allocateForTree() throws IgniteCheckedException { } /** {@inheritDoc} */ - @Override public GridCloseableIterator> preloadRows( + @Override public GridCloseableIterator> allocateRows( int partId, Collection infos ) throws IgniteCheckedException { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 4269dcf177f0c..ee6b8dda749fc 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -1041,7 +1041,7 @@ private void preloadEntriesBatched( // Create data rows on data pages before getting locks on cache entries. try (GridCloseableIterator> iter = - grp.offheap().preloadRows(p, batch)) { + grp.offheap().allocateRows(p, batch)) { while (iter.hasNext()) { Map.Entry e = iter.next(); From f0d41af4697238fc55136a0f87fd7e89d3a433eb Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 1 Jul 2019 16:51:56 +0300 Subject: [PATCH 13/21] IGNITE-11584 allocateRows() moved to CacheDataStore, createRows() removed, javadocs improved. --- .../processors/cache/GridCacheUtils.java | 33 +------ .../cache/IgniteCacheOffheapManager.java | 32 ++---- .../cache/IgniteCacheOffheapManagerImpl.java | 66 ++++++------- .../preloader/GridDhtPartitionDemander.java | 97 +++++++++++++------ .../persistence/GridCacheOffheapManager.java | 5 +- 5 files changed, 112 insertions(+), 121 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java index 83421f31211f7..5ffe4ed36c945 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java @@ -66,7 +66,6 @@ import org.apache.ignite.internal.cluster.ClusterGroupEmptyCheckedException; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException; -import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.distributed.GridDistributedLockCancelledException; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter; @@ -2002,35 +2001,13 @@ public static boolean isCacheTemplateName(String cacheName) { } /** - * Calculates whether there is enough free space in a region to store a specified amount of data. + * Check whether in-memory evictions enabled. * - * @param memPlc Data region. - * @param size Data size in bytes. - * @return {@code True} if a specified amount of data can be stored in the memory region without evictions. + * @param memPlcCfg Data region configuration. + * @return {@code True} if eviction policy enabled and native persistence is disabled. */ - public static boolean isEnoughSpaceForData(DataRegion memPlc, long size) { - DataRegionConfiguration plc = memPlc.config(); - - if (size <= 0 || plc.isPersistenceEnabled() || plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED) - return true; - - PageMemory pageMem = memPlc.pageMemory(); - - int sysPageSize = pageMem.systemPageSize(); - - long pagesRequired = Math.round(size / (double)sysPageSize); - - long maxPages = plc.getMaxSize() / sysPageSize; - - // There are enough pages left. - if (pagesRequired < maxPages - pageMem.loadedPages()) - return true; - - // Empty pages pool size restricted. - if (pagesRequired > plc.getEmptyPagesPoolSize()) - return false; - - return pagesRequired < Math.round(maxPages * (1.0d - plc.getEvictionThreshold())); + public static boolean isEvictionsEnabled(DataRegionConfiguration memPlcCfg) { + return memPlcCfg.getPageEvictionMode() != DataPageEvictionMode.DISABLED && !memPlcCfg.isPersistenceEnabled(); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index 72468e609fb8b..33394f871c772 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -452,20 +452,6 @@ public GridIterator cachePartitionIterator(int cacheId, final int public GridCloseableIterator reservedIterator(final int part, final AffinityTopologyVersion topVer) throws IgniteCheckedException; - /** - * Create a batch of data rows in page memory which is not associated with the cache entries. - * - * @param part Partition number. - * @param infos Entry infos. - * @return {@link AutoCloseable} iterator with the mapping of source entry info to created cache data row (row can - * be {@code null} if the source value is {@code null}). Iterator automatically removes unprocessed rows on close. - * @throws IgniteCheckedException If failed. - */ - public GridCloseableIterator> allocateRows( - int part, - Collection infos - ) throws IgniteCheckedException; - /** * @param parts Partitions. * @return Partition data iterator. @@ -710,21 +696,23 @@ CacheDataRow createRow( @Nullable CacheDataRow oldRow) throws IgniteCheckedException; /** - * Create data rows. + * @param row Row removed from cache. + */ + public void removeRow(CacheDataRow row) throws IgniteCheckedException; + + /** + * Create a batch of data rows in page memory which is not associated with the cache entries. * * @param infos Entry infos. - * @return Created rows. + * @return {@link AutoCloseable} iterator with the mapping of source entry info to created cache data row (row + * can be {@code null} if the source value is {@code null}). Iterator automatically removes unprocessed data + * rows from page memory on close. * @throws IgniteCheckedException If failed. */ - public Collection createRows( + public GridCloseableIterator> allocateRows( Collection infos ) throws IgniteCheckedException; - /** - * @param row Row removed from cache. - */ - public void removeRow(CacheDataRow row) throws IgniteCheckedException; - /** * @param cctx Cache context. * @param cleanupRows Rows to cleanup. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index 6e8cbab4f5ef6..72d2186699e02 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -1176,37 +1176,6 @@ private long allocateForTree() throws IgniteCheckedException { }; } - /** {@inheritDoc} */ - @Override public GridCloseableIterator> allocateRows( - int partId, - Collection infos - ) throws IgniteCheckedException { - CacheDataStore dataStore = dataStore(partId); - - Collection rows = dataStore.createRows(F.view(infos, info -> info.value() != null)); - - return new GridCloseableIteratorAdapter>() { - private final Iterator rowsIter = rows.iterator(); - private final Iterator infosIter = infos.iterator(); - - @Override protected IgniteBiTuple onNext() { - GridCacheEntryInfo info = infosIter.next(); - - return new IgniteBiTuple<>(info, info.value() == null ? null : rowsIter.next()); - } - - @Override protected boolean onHasNext() { - return infosIter.hasNext(); - } - - @Override protected void onClose() throws IgniteCheckedException { - while (rowsIter.hasNext()) - dataStore.removeRow(rowsIter.next()); - - } - }; - } - /** {@inheritDoc} */ @Override public IgniteRebalanceIterator rebalanceIterator(IgniteDhtDemandedPartitionsMap parts, final AffinityTopologyVersion topVer) @@ -1757,15 +1726,15 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo } /** {@inheritDoc} */ - @Override public Collection createRows( + @Override public GridCloseableIterator> allocateRows( Collection infos ) throws IgniteCheckedException { - if (!busyLock.enterBusy()) - throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); + Collection rows = new ArrayList<>(infos.size()); - List rows = new ArrayList<>(infos.size()); + // Skipping infos with null values. + Collection nonNullInfos = F.view(infos, info -> info.value() != null); - for (GridCacheEntryInfo info : infos) { + for (GridCacheEntryInfo info : nonNullInfos) { rows.add(makeDataRow(info.key(), info.value(), info.version(), @@ -1773,10 +1742,13 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo grp.storeCacheIdInDataPage() ? info.cacheId() : CU.UNDEFINED_CACHE_ID)); } + if (!busyLock.enterBusy()) + throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); + try { rowStore.addRows(rows, grp.statisticsHolderData()); - Iterator iter = infos.iterator(); + Iterator iter = nonNullInfos.iterator(); if (grp.sharedGroup() && !grp.storeCacheIdInDataPage()) { for (DataRow row : rows) @@ -1787,7 +1759,25 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo busyLock.leaveBusy(); } - return rows; + return new GridCloseableIteratorAdapter>() { + private final Iterator rowsIter = rows.iterator(); + private final Iterator infosIter = infos.iterator(); + + @Override protected IgniteBiTuple onNext() { + GridCacheEntryInfo info = infosIter.next(); + + return new IgniteBiTuple<>(info, info.value() == null ? null : rowsIter.next()); + } + + @Override protected boolean onHasNext() { + return infosIter.hasNext(); + } + + @Override protected void onClose() throws IgniteCheckedException { + while (rowsIter.hasNext()) + removeRow(rowsIter.next()); + } + }; } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index ee6b8dda749fc..53bb4d26d7d3f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -35,12 +35,14 @@ import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.DataRegionConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.IgniteNodeAttributes; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; +import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection; @@ -74,6 +76,7 @@ import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteBiTuple; import org.apache.ignite.lang.IgniteInClosure; import org.apache.ignite.lang.IgnitePredicate; import org.apache.ignite.spi.IgniteSpiException; @@ -83,7 +86,6 @@ import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_PART_LOADED; import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_STARTED; import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_STOPPED; -import static org.apache.ignite.internal.processors.cache.GridCacheUtils.isEnoughSpaceForData; import static org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState.MOVING; import static org.apache.ignite.internal.processors.dr.GridDrType.DR_NONE; import static org.apache.ignite.internal.processors.dr.GridDrType.DR_PRELOAD; @@ -791,7 +793,8 @@ public void handleSupplyMessage( try { if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); - else if (isEnoughSpaceForData(grp.dataRegion(), supplyMsg.messageSize() * 2)) + else if (!CU.isEvictionsEnabled(grp.dataRegion().config()) || + isEnoughSpaceForData(supplyMsg.messageSize() * 2)) preloadEntriesBatched(topVer, node, p, infos); else preloadEntries(topVer, node, p, infos); @@ -894,7 +897,7 @@ else if (isEnoughSpaceForData(grp.dataRegion(), supplyMsg.messageSize() * 2)) * @throws IgniteInterruptedCheckedException If interrupted. */ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - Collection infos) throws IgniteCheckedException { + List infos) throws IgniteCheckedException { if (infos.isEmpty()) return; @@ -943,11 +946,7 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node if (cctx != null) { mvccPreloadEntry(cctx, node, entryHist, topVer, p); - //TODO: IGNITE-11330: Update metrics for touched cache only. - for (GridCacheContext ctx : grp.caches()) { - if (ctx.statisticsEnabled()) - ctx.cache().metrics0().onRebalanceKeyReceived(); - } + updateCacheMetrics(); } if (!hasMore) @@ -975,7 +974,7 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node * @throws IgniteInterruptedCheckedException If interrupted. */ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - Iterable infos) throws IgniteCheckedException { + List infos) throws IgniteCheckedException { GridCacheContext cctx = null; Iterator iter = infos.iterator(); @@ -1002,11 +1001,7 @@ else if (cctx.isNear()) preloadEntry(node, p, entry, topVer, cctx, null); - //TODO: IGNITE-11330: Update metrics for touched cache only. - for (GridCacheContext ctx : grp.caches()) { - if (ctx.statisticsEnabled()) - ctx.cache().metrics0().onRebalanceKeyReceived(); - } + updateCacheMetrics(); } } finally { @@ -1028,11 +1023,11 @@ private void preloadEntriesBatched( int p, List infos ) throws IgniteCheckedException { - int batchOff = 0; + int cnt = infos.size(); + int off = 0; - while (batchOff < infos.size()) { - Collection batch = - infos.subList(batchOff, Math.min(infos.size(), batchOff += CHECKPOINT_THRESHOLD)); + while (off < cnt) { + Collection batch = infos.subList(off, Math.min(cnt, off += CHECKPOINT_THRESHOLD)); ctx.database().checkpointReadLock(); @@ -1040,27 +1035,24 @@ private void preloadEntriesBatched( GridDhtLocalPartition part = grp.topology().localPartition(p); // Create data rows on data pages before getting locks on cache entries. - try (GridCloseableIterator> iter = - grp.offheap().allocateRows(p, batch)) { + try (GridCloseableIterator> iter = + part.dataStore().allocateRows(batch)) { while (iter.hasNext()) { - Map.Entry e = iter.next(); + IgniteBiTuple tup = iter.next(); - GridCacheEntryInfo info = e.getKey(); - CacheDataRow row = e.getValue(); + GridCacheEntryInfo info = tup.get1(); + CacheDataRow row = tup.get2(); GridCacheContext cctx = resolveCacheContext(info); - if (cctx == null || !preloadEntry(from, p, info, topVer, cctx, row)) - part.dataStore().removeRow(row); - if (cctx == null) - continue; + part.dataStore().removeRow(row); + else { + if (!preloadEntry(from, p, info, topVer, cctx, row)) + part.dataStore().removeRow(row); - //TODO: IGNITE-11330: Update metrics for touched cache only. - for (GridCacheContext cctx0 : grp.caches()) { - if (cctx0.statisticsEnabled()) - cctx0.cache().metrics0().onRebalanceKeyReceived(); + updateCacheMetrics(); } } } @@ -1250,6 +1242,49 @@ private GridCacheContext resolveCacheContext(GridCacheEntryInfo info) { return cctx; } + /** + * Update rebalancing metrics. + */ + private void updateCacheMetrics() { + // TODO: IGNITE-11330: Update metrics for touched cache only. + // Due to historical rebalancing "EstimatedRebalancingKeys" metric is currently calculated for the whole cache + // group (by partition counters), so "RebalancedKeys" and "RebalancingKeysRate" is calculated in the same way. + for (GridCacheContext cctx0 : grp.caches()) { + if (cctx0.statisticsEnabled()) + cctx0.cache().metrics0().onRebalanceKeyReceived(); + } + } + + /** + * Calculates whether there is enough free space in the current memory region for the specified amount of data. + * + * @param size Data size in bytes. + * @return {@code True} if a specified amount of data can be stored in the memory region without evictions. + */ + private boolean isEnoughSpaceForData(long size) { + assert size >= 0 : size; + + DataRegionConfiguration plc = grp.dataRegion().config(); + + PageMemory pageMem = grp.dataRegion().pageMemory(); + + int sysPageSize = pageMem.systemPageSize(); + + long pagesRequired = (long)Math.ceil(size / (double)sysPageSize); + + long maxPages = plc.getMaxSize() / sysPageSize; + + // There are enough pages left. + if (pagesRequired < maxPages - pageMem.loadedPages()) + return true; + + // Empty pages pool size restricted. + if (pagesRequired > plc.getEmptyPagesPoolSize()) + return false; + + return pagesRequired < Math.round(maxPages * (1.0d - plc.getEvictionThreshold())); + } + /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridDhtPartitionDemander.class, this); 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 a6f802a4b9b85..5c0903bf67a4b 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 @@ -100,6 +100,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner; import org.apache.ignite.internal.util.GridLongList; +import org.apache.ignite.internal.util.lang.GridCloseableIterator; import org.apache.ignite.internal.util.lang.GridCursor; import org.apache.ignite.internal.util.lang.IgniteInClosure2X; import org.apache.ignite.internal.util.tostring.GridToStringInclude; @@ -2411,14 +2412,14 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { } /** {@inheritDoc} */ - @Override public Collection createRows( + @Override public GridCloseableIterator> allocateRows( Collection infos ) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); CacheDataStore delegate = init0(false); - return delegate.createRows(infos); + return delegate.allocateRows(infos); } /** {@inheritDoc} */ From 49186875f689589e3f75212642493938606fbb9d Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 2 Jul 2019 15:03:06 +0300 Subject: [PATCH 14/21] IGNITE-11584 Minor: evictionsEnabled -> evictionsDisabled. --- .../processors/cache/GridCacheUtils.java | 9 ++++--- .../preloader/GridDhtPartitionDemander.java | 24 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java index 5ffe4ed36c945..3db958d140e3a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java @@ -73,7 +73,6 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxLog; -import org.apache.ignite.internal.processors.cache.persistence.DataRegion; import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage; import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx; import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry; @@ -2001,13 +2000,13 @@ public static boolean isCacheTemplateName(String cacheName) { } /** - * Check whether in-memory evictions enabled. + * Check whether in-memory evictions disabled. * * @param memPlcCfg Data region configuration. - * @return {@code True} if eviction policy enabled and native persistence is disabled. + * @return {@code True} if eviction policy disabled and native persistence is enabled. */ - public static boolean isEvictionsEnabled(DataRegionConfiguration memPlcCfg) { - return memPlcCfg.getPageEvictionMode() != DataPageEvictionMode.DISABLED && !memPlcCfg.isPersistenceEnabled(); + public static boolean isEvictionsDisabled(DataRegionConfiguration memPlcCfg) { + return memPlcCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED || memPlcCfg.isPersistenceEnabled(); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 53bb4d26d7d3f..549247996aa4a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -62,6 +62,7 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware; import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState; import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; +import org.apache.ignite.internal.processors.cache.persistence.DataRegion; import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter; import org.apache.ignite.internal.util.future.GridCompoundFuture; @@ -793,8 +794,8 @@ public void handleSupplyMessage( try { if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); - else if (!CU.isEvictionsEnabled(grp.dataRegion().config()) || - isEnoughSpaceForData(supplyMsg.messageSize() * 2)) + else if (CU.isEvictionsDisabled(grp.dataRegion().config()) || + isEnoughSpace(grp.dataRegion(), supplyMsg.messageSize() * 2)) preloadEntriesBatched(topVer, node, p, infos); else preloadEntries(topVer, node, p, infos); @@ -1236,10 +1237,10 @@ private String demandRoutineInfo(int topicId, UUID supplier, GridDhtPartitionSup private GridCacheContext resolveCacheContext(GridCacheEntryInfo info) { GridCacheContext cctx = grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); - if (cctx != null && cctx.isNear()) - cctx = cctx.dhtCache().context(); + if (cctx == null) + return null; - return cctx; + return cctx.isNear() ? cctx.dhtCache().context() : cctx; } /** @@ -1258,31 +1259,32 @@ private void updateCacheMetrics() { /** * Calculates whether there is enough free space in the current memory region for the specified amount of data. * + * @param dataRegion Data region. * @param size Data size in bytes. * @return {@code True} if a specified amount of data can be stored in the memory region without evictions. */ - private boolean isEnoughSpaceForData(long size) { + private boolean isEnoughSpace(DataRegion dataRegion, long size) { assert size >= 0 : size; - DataRegionConfiguration plc = grp.dataRegion().config(); + DataRegionConfiguration cfg = dataRegion.config(); - PageMemory pageMem = grp.dataRegion().pageMemory(); + PageMemory pageMem = dataRegion.pageMemory(); int sysPageSize = pageMem.systemPageSize(); long pagesRequired = (long)Math.ceil(size / (double)sysPageSize); - long maxPages = plc.getMaxSize() / sysPageSize; + long maxPages = cfg.getMaxSize() / sysPageSize; // There are enough pages left. if (pagesRequired < maxPages - pageMem.loadedPages()) return true; // Empty pages pool size restricted. - if (pagesRequired > plc.getEmptyPagesPoolSize()) + if (pagesRequired > cfg.getEmptyPagesPoolSize()) return false; - return pagesRequired < Math.round(maxPages * (1.0d - plc.getEvictionThreshold())); + return pagesRequired < Math.round(maxPages * (1.0d - cfg.getEvictionThreshold())); } /** {@inheritDoc} */ From bf99dbdb062e2c789dc3286017df5b3e2c3686ce Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 2 Jul 2019 16:08:45 +0300 Subject: [PATCH 15/21] IGNITE-11584 Filter nulls in RowStore.addRows(). --- .../cache/IgniteCacheOffheapManagerImpl.java | 37 ++++++++++--------- .../preloader/GridDhtPartitionDemander.java | 1 - .../cache/persistence/RowStore.java | 2 + 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index 72d2186699e02..ad6d6ae0f05a4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; @@ -41,6 +42,7 @@ import org.apache.ignite.failure.FailureContext; import org.apache.ignite.failure.FailureType; import org.apache.ignite.internal.NodeStoppingException; +import org.apache.ignite.internal.metric.IoStatisticsHolder; import org.apache.ignite.internal.pagemem.FullPageId; import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccMarkUpdatedRecord; import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccUpdateNewTxStateHintRecord; @@ -94,7 +96,6 @@ import org.apache.ignite.internal.processors.cache.tree.mvcc.search.MvccTreeClosure; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner; -import org.apache.ignite.internal.metric.IoStatisticsHolder; import org.apache.ignite.internal.transactions.IgniteTxUnexpectedStateCheckedException; import org.apache.ignite.internal.util.GridAtomicLong; import org.apache.ignite.internal.util.GridCloseableIteratorAdapter; @@ -1731,28 +1732,30 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo ) throws IgniteCheckedException { Collection rows = new ArrayList<>(infos.size()); - // Skipping infos with null values. - Collection nonNullInfos = F.view(infos, info -> info.value() != null); - - for (GridCacheEntryInfo info : nonNullInfos) { - rows.add(makeDataRow(info.key(), - info.value(), - info.version(), - info.expireTime(), - grp.storeCacheIdInDataPage() ? info.cacheId() : CU.UNDEFINED_CACHE_ID)); + for (GridCacheEntryInfo info : infos) { + rows.add(info.value() == null ? null : + makeDataRow(info.key(), + info.value(), + info.version(), + info.expireTime(), + grp.storeCacheIdInDataPage() ? info.cacheId() : CU.UNDEFINED_CACHE_ID)); } if (!busyLock.enterBusy()) throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); try { - rowStore.addRows(rows, grp.statisticsHolderData()); + rowStore.addRows(F.view(rows, Objects::nonNull), grp.statisticsHolderData()); - Iterator iter = nonNullInfos.iterator(); + Iterator iter = infos.iterator(); if (grp.sharedGroup() && !grp.storeCacheIdInDataPage()) { - for (DataRow row : rows) - row.cacheId(iter.next().cacheId()); + for (DataRow row : rows) { + GridCacheEntryInfo info = iter.next(); + + if (row != null) + row.cacheId(info.cacheId()); + } } } finally { @@ -1764,13 +1767,11 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo private final Iterator infosIter = infos.iterator(); @Override protected IgniteBiTuple onNext() { - GridCacheEntryInfo info = infosIter.next(); - - return new IgniteBiTuple<>(info, info.value() == null ? null : rowsIter.next()); + return new IgniteBiTuple<>(infosIter.next(), rowsIter.next()); } @Override protected boolean onHasNext() { - return infosIter.hasNext(); + return rowsIter.hasNext() && infosIter.hasNext(); } @Override protected void onClose() throws IgniteCheckedException { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 549247996aa4a..2bc6175c84a33 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -1038,7 +1038,6 @@ private void preloadEntriesBatched( // Create data rows on data pages before getting locks on cache entries. try (GridCloseableIterator> iter = part.dataStore().allocateRows(batch)) { - while (iter.hasNext()) { IgniteBiTuple tup = iter.next(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index cc23d9389febc..d11f883e0851b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.cache.persistence; import java.util.Collection; +import java.util.Objects; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.pagemem.PageIdUtils; import org.apache.ignite.internal.pagemem.PageMemory; @@ -28,6 +29,7 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler; import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner; import org.apache.ignite.internal.metric.IoStatisticsHolder; +import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; /** From 5908956856b0bdf011dd62f2da3e125ceff4ac86 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 2 Jul 2019 16:39:10 +0300 Subject: [PATCH 16/21] IGNITE-11584 Removed redundant size estimation. --- .../apache/ignite/internal/processors/cache/GridCacheUtils.java | 2 +- .../distributed/dht/preloader/GridDhtPartitionDemander.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java index 3db958d140e3a..3652f01b17ac4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java @@ -2003,7 +2003,7 @@ public static boolean isCacheTemplateName(String cacheName) { * Check whether in-memory evictions disabled. * * @param memPlcCfg Data region configuration. - * @return {@code True} if eviction policy disabled and native persistence is enabled. + * @return {@code True} if eviction policy disabled or native persistence is enabled. */ public static boolean isEvictionsDisabled(DataRegionConfiguration memPlcCfg) { return memPlcCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED || memPlcCfg.isPersistenceEnabled(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 2bc6175c84a33..d5e03222ea42b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -795,7 +795,7 @@ public void handleSupplyMessage( if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); else if (CU.isEvictionsDisabled(grp.dataRegion().config()) || - isEnoughSpace(grp.dataRegion(), supplyMsg.messageSize() * 2)) + isEnoughSpace(grp.dataRegion(), supplyMsg.messageSize())) preloadEntriesBatched(topVer, node, p, infos); else preloadEntries(topVer, node, p, infos); From 615b16d7b391920edb71fba94a40740cde1dc738 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 3 Jul 2019 00:44:04 +0300 Subject: [PATCH 17/21] IGNITE-11584 Unused imports. --- .../internal/processors/cache/persistence/RowStore.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index d11f883e0851b..6607d4bc17f3e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -18,8 +18,8 @@ package org.apache.ignite.internal.processors.cache.persistence; import java.util.Collection; -import java.util.Objects; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.metric.IoStatisticsHolder; import org.apache.ignite.internal.pagemem.PageIdUtils; import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.cache.CacheGroupContext; @@ -28,8 +28,6 @@ import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler; import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner; -import org.apache.ignite.internal.metric.IoStatisticsHolder; -import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; /** From df68693c8cbd68f31a1f604a5cf8dacf74adfc8c Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 3 Jul 2019 15:02:08 +0300 Subject: [PATCH 18/21] IGNITE-11584 Minor code cleanup: method naming. --- .../ignite/internal/processors/cache/GridCacheUtils.java | 4 ++-- .../distributed/dht/preloader/GridDhtPartitionDemander.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java index 3652f01b17ac4..f45641bc71020 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java @@ -2000,12 +2000,12 @@ public static boolean isCacheTemplateName(String cacheName) { } /** - * Check whether in-memory evictions disabled. + * Check whether in-memory eviction disabled. * * @param memPlcCfg Data region configuration. * @return {@code True} if eviction policy disabled or native persistence is enabled. */ - public static boolean isEvictionsDisabled(DataRegionConfiguration memPlcCfg) { + public static boolean isEvictionDisabled(DataRegionConfiguration memPlcCfg) { return memPlcCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED || memPlcCfg.isPersistenceEnabled(); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index d5e03222ea42b..97a2d82bf6340 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -794,7 +794,7 @@ public void handleSupplyMessage( try { if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); - else if (CU.isEvictionsDisabled(grp.dataRegion().config()) || + else if (CU.isEvictionDisabled(grp.dataRegion().config()) || isEnoughSpace(grp.dataRegion(), supplyMsg.messageSize())) preloadEntriesBatched(topVer, node, p, infos); else From 3c42ce86fe3c5b40ba657a0a5066a0f61b22b0f7 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 8 Jul 2019 18:01:45 +0300 Subject: [PATCH 19/21] wip --- .../pagemem/JmhCacheFreelistBenchmark.java | 9 ++- .../cache/IgniteCacheOffheapManager.java | 19 +------ .../cache/IgniteCacheOffheapManagerImpl.java | 56 +++++++------------ .../preloader/GridDhtPartitionDemander.java | 34 +++-------- .../persistence/GridCacheOffheapManager.java | 17 ++---- .../cache/persistence/RowStore.java | 5 +- .../freelist/AbstractFreeList.java | 6 +- .../cache/persistence/freelist/FreeList.java | 5 +- .../database/CacheFreeListSelfTest.java | 13 ++++- 9 files changed, 66 insertions(+), 98 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java index 381547317ad70..8302f6bd36726 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhCacheFreelistBenchmark.java @@ -43,6 +43,7 @@ import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeList; import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; +import org.apache.ignite.internal.util.typedef.CAX; import org.apache.ignite.logger.java.JavaLogger; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -135,11 +136,15 @@ public void insertRow(FreeListProvider provider, Data rows) throws IgniteChecked } /** - * Check {@link FreeList#insertDataRows(Collection, IoStatisticsHolder)} performance. + * Check {@link FreeList#insertDataRows(Collection, IoStatisticsHolder, org.apache.ignite.internal.util.typedef.CAX)} performance. */ @Benchmark public void insertRows(FreeListProvider provider, Data rows) throws IgniteCheckedException { - provider.freeList.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE); + provider.freeList.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE, new CAX() { + @Override public void applyx() { + // No-op. + } + }); } /** */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index 33394f871c772..45a14aa94242d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -48,6 +48,7 @@ import org.apache.ignite.internal.util.lang.GridCursor; import org.apache.ignite.internal.util.lang.GridIterator; import org.apache.ignite.internal.util.lang.IgniteInClosure2X; +import org.apache.ignite.internal.util.lang.IgnitePredicate2X; import org.apache.ignite.lang.IgniteBiTuple; import org.jetbrains.annotations.Nullable; @@ -695,22 +696,8 @@ CacheDataRow createRow( long expireTime, @Nullable CacheDataRow oldRow) throws IgniteCheckedException; - /** - * @param row Row removed from cache. - */ - public void removeRow(CacheDataRow row) throws IgniteCheckedException; - - /** - * Create a batch of data rows in page memory which is not associated with the cache entries. - * - * @param infos Entry infos. - * @return {@link AutoCloseable} iterator with the mapping of source entry info to created cache data row (row - * can be {@code null} if the source value is {@code null}). Iterator automatically removes unprocessed data - * rows from page memory on close. - * @throws IgniteCheckedException If failed. - */ - public GridCloseableIterator> allocateRows( - Collection infos + /** */ + public void createRows(Collection infos, IgnitePredicate2X pred ) throws IgniteCheckedException; /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index ad6d6ae0f05a4..a8a363b846f09 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -108,6 +108,8 @@ import org.apache.ignite.internal.util.lang.GridCursor; import org.apache.ignite.internal.util.lang.GridIterator; import org.apache.ignite.internal.util.lang.IgniteInClosure2X; +import org.apache.ignite.internal.util.lang.IgnitePredicate2X; +import org.apache.ignite.internal.util.typedef.CAX; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.CU; @@ -1727,9 +1729,8 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo } /** {@inheritDoc} */ - @Override public GridCloseableIterator> allocateRows( - Collection infos - ) throws IgniteCheckedException { + @Override public void createRows(Collection infos, IgnitePredicate2X pred) throws IgniteCheckedException { Collection rows = new ArrayList<>(infos.size()); for (GridCacheEntryInfo info : infos) { @@ -1745,48 +1746,33 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); try { - rowStore.addRows(F.view(rows, Objects::nonNull), grp.statisticsHolderData()); - - Iterator iter = infos.iterator(); - - if (grp.sharedGroup() && !grp.storeCacheIdInDataPage()) { - for (DataRow row : rows) { - GridCacheEntryInfo info = iter.next(); - - if (row != null) - row.cacheId(info.cacheId()); + rowStore.addRows(F.view(rows, Objects::nonNull), grp.statisticsHolderData(), new CAX() { + @Override public void applyx() throws IgniteCheckedException { + grp.shared().database().ensureFreeSpace(grp.dataRegion()); } - } + }); } finally { busyLock.leaveBusy(); } - return new GridCloseableIteratorAdapter>() { - private final Iterator rowsIter = rows.iterator(); - private final Iterator infosIter = infos.iterator(); + Iterator iter = rows.iterator(); - @Override protected IgniteBiTuple onNext() { - return new IgniteBiTuple<>(infosIter.next(), rowsIter.next()); - } + try { + for (GridCacheEntryInfo info : infos) { + DataRow row = iter.next(); - @Override protected boolean onHasNext() { - return rowsIter.hasNext() && infosIter.hasNext(); - } + if (row != null && grp.sharedGroup() && row.cacheId() == CU.UNDEFINED_CACHE_ID) + row.cacheId(info.cacheId()); - @Override protected void onClose() throws IgniteCheckedException { - while (rowsIter.hasNext()) - removeRow(rowsIter.next()); + if (!pred.apply(info, row) && row != null) + rowStore.removeRow(row.link(), grp.statisticsHolderData()); } - }; - } - - /** {@inheritDoc} */ - @Override public void removeRow(CacheDataRow row) throws IgniteCheckedException { - if (row == null) - return; - - rowStore.removeRow(row.link(), grp.statisticsHolderData()); + } + finally { + while (iter.hasNext()) + rowStore.removeRow(iter.next().link(), grp.statisticsHolderData()); + } } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 97a2d82bf6340..1395f699a5c7b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -68,8 +68,8 @@ import org.apache.ignite.internal.util.future.GridCompoundFuture; import org.apache.ignite.internal.util.future.GridFinishedFuture; import org.apache.ignite.internal.util.future.GridFutureAdapter; -import org.apache.ignite.internal.util.lang.GridCloseableIterator; import org.apache.ignite.internal.util.lang.IgniteInClosureX; +import org.apache.ignite.internal.util.lang.IgnitePredicate2X; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.CI1; @@ -77,7 +77,6 @@ import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.lang.IgniteBiTuple; import org.apache.ignite.lang.IgniteInClosure; import org.apache.ignite.lang.IgnitePredicate; import org.apache.ignite.spi.IgniteSpiException; @@ -794,11 +793,8 @@ public void handleSupplyMessage( try { if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); - else if (CU.isEvictionDisabled(grp.dataRegion().config()) || - isEnoughSpace(grp.dataRegion(), supplyMsg.messageSize())) - preloadEntriesBatched(topVer, node, p, infos); else - preloadEntries(topVer, node, p, infos); + preloadEntriesBatched(topVer, node, p, infos); } catch (GridDhtInvalidPartitionException ignored) { if (log.isDebugEnabled()) @@ -1001,8 +997,6 @@ else if (cctx.isNear()) } preloadEntry(node, p, entry, topVer, cctx, null); - - updateCacheMetrics(); } } finally { @@ -1035,27 +1029,13 @@ private void preloadEntriesBatched( try { GridDhtLocalPartition part = grp.topology().localPartition(p); - // Create data rows on data pages before getting locks on cache entries. - try (GridCloseableIterator> iter = - part.dataStore().allocateRows(batch)) { - while (iter.hasNext()) { - IgniteBiTuple tup = iter.next(); - - GridCacheEntryInfo info = tup.get1(); - CacheDataRow row = tup.get2(); - + part.dataStore().createRows(batch, new IgnitePredicate2X() { + @Override public boolean applyx(GridCacheEntryInfo info, CacheDataRow row) throws IgniteCheckedException { GridCacheContext cctx = resolveCacheContext(info); - if (cctx == null) - part.dataStore().removeRow(row); - else { - if (!preloadEntry(from, p, info, topVer, cctx, row)) - part.dataStore().removeRow(row); - - updateCacheMetrics(); - } + return cctx != null && preloadEntry(from, p, info, topVer, cctx, row); } - } + }); } finally { ctx.database().checkpointReadUnlock(); @@ -1128,6 +1108,8 @@ private boolean preloadEntry( log.trace("Rebalancing entry is already in cache (will ignore) [key=" + cached.key() + ", part=" + p + ']'); } + + updateCacheMetrics(); } } catch (GridCacheEntryRemovedException ignored) { 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 5c0903bf67a4b..880157248fbd4 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 @@ -100,9 +100,9 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner; import org.apache.ignite.internal.util.GridLongList; -import org.apache.ignite.internal.util.lang.GridCloseableIterator; import org.apache.ignite.internal.util.lang.GridCursor; import org.apache.ignite.internal.util.lang.IgniteInClosure2X; +import org.apache.ignite.internal.util.lang.IgnitePredicate2X; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.S; @@ -2377,15 +2377,6 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { return delegate.createRow(cctx, key, val, ver, expireTime, oldRow); } - /** {@inheritDoc} */ - @Override public void removeRow(CacheDataRow row) throws IgniteCheckedException { - assert ctx.database().checkpointLockIsHeldByThread(); - - CacheDataStore delegate = init0(false); - - delegate.removeRow(row); - } - /** {@inheritDoc} */ @Override public int cleanup(GridCacheContext cctx, @Nullable List cleanupRows) throws IgniteCheckedException { @@ -2412,14 +2403,14 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { } /** {@inheritDoc} */ - @Override public GridCloseableIterator> allocateRows( - Collection infos + @Override public void createRows( + Collection infos, IgnitePredicate2X pred ) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); CacheDataStore delegate = init0(false); - return delegate.allocateRows(infos); + delegate.createRows(infos, pred); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index 6607d4bc17f3e..de21e2af61248 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -28,6 +28,7 @@ import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList; import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler; import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner; +import org.apache.ignite.internal.util.typedef.CAX; import org.apache.ignite.internal.util.typedef.internal.U; /** @@ -123,10 +124,10 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit * @param statHolder Statistics holder to track IO operations. * @throws IgniteCheckedException If failed. */ - public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { + public void addRows(Collection rows, IoStatisticsHolder statHolder, CAX clo) throws IgniteCheckedException { assert ctx.database().checkpointLockIsHeldByThread(); - freeList.insertDataRows(rows, statHolder); + freeList.insertDataRows(rows, statHolder, clo); } /** 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 b38f8ec362886..3f7ce9684ee15 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 @@ -44,6 +44,7 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageLockListener; import org.apache.ignite.internal.metric.IoStatisticsHolder; import org.apache.ignite.internal.metric.IoStatisticsHolderNoOp; +import org.apache.ignite.internal.util.typedef.CAX; import org.apache.ignite.internal.util.typedef.internal.U; /** @@ -553,10 +554,11 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken * * @param rows Rows. * @param statHolder Statistics holder to track IO operations. + * @param clo * @throws IgniteCheckedException If failed. */ @Override public void insertDataRows(Collection rows, - IoStatisticsHolder statHolder) throws IgniteCheckedException { + IoStatisticsHolder statHolder, CAX clo) throws IgniteCheckedException { try { Iterator iter = rows.iterator(); @@ -565,6 +567,8 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken int written = COMPLETE; while (iter.hasNext() || written != COMPLETE) { + clo.applyx(); + if (written == COMPLETE) { row = iter.next(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java index 0753fd7dd9f0e..25a784831062c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java @@ -23,6 +23,7 @@ import org.apache.ignite.internal.processors.cache.persistence.Storable; import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler; import org.apache.ignite.internal.metric.IoStatisticsHolder; +import org.apache.ignite.internal.util.typedef.CAX; /** */ @@ -37,9 +38,11 @@ public interface FreeList { /** * @param rows Rows. * @param statHolder Statistics holder to track IO operations. + * @param clo * @throws IgniteCheckedException If failed. */ - public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException; + public void insertDataRows(Collection rows, IoStatisticsHolder statHolder, + CAX clo) throws IgniteCheckedException; /** * @param link Row link. 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 3848594935558..bc6d708599745 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 @@ -50,6 +50,7 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.CacheVersionIO; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.metric.IoStatisticsHolderNoOp; +import org.apache.ignite.internal.util.typedef.CAX; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageWriter; import org.apache.ignite.testframework.GridTestUtils; @@ -327,7 +328,11 @@ protected void checkInsertDeleteMultiThreaded(final int pageSize, final boolean } if (rows.size() == BATCH_SIZE) { - list.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE); + list.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE, new CAX() { + @Override public void applyx() { + // No-op. + } + }); for (TestDataRow row0 : rows) { assertTrue(row0.link() != 0L); @@ -440,7 +445,11 @@ protected void checkInsertDeleteSingleThreaded(int pageSize, boolean batched) th } if (rows.size() == BATCH_SIZE) { - list.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE); + list.insertDataRows(rows, IoStatisticsHolderNoOp.INSTANCE, new CAX() { + @Override public void applyx() throws IgniteCheckedException { + // No-op. + } + }); for (TestDataRow row0 : rows) { assertTrue(row0.link() != 0L); From df141f7c8fbb5202a6789408d2a489435868edc7 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 8 Jul 2019 18:03:38 +0300 Subject: [PATCH 20/21] wip2 --- .../processors/cache/GridCacheUtils.java | 11 --- .../preloader/GridDhtPartitionDemander.java | 82 +------------------ 2 files changed, 2 insertions(+), 91 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java index f45641bc71020..cc5dd42ef6cc9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java @@ -54,7 +54,6 @@ import org.apache.ignite.cache.store.CacheStoreSessionListener; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; -import org.apache.ignite.configuration.DataPageEvictionMode; import org.apache.ignite.configuration.DataRegionConfiguration; import org.apache.ignite.configuration.DataStorageConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; @@ -1999,16 +1998,6 @@ public static boolean isCacheTemplateName(String cacheName) { return cacheName.endsWith("*"); } - /** - * Check whether in-memory eviction disabled. - * - * @param memPlcCfg Data region configuration. - * @return {@code True} if eviction policy disabled or native persistence is enabled. - */ - public static boolean isEvictionDisabled(DataRegionConfiguration memPlcCfg) { - return memPlcCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED || memPlcCfg.isPersistenceEnabled(); - } - /** * */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 1395f699a5c7b..d22f1858ac685 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -35,14 +35,12 @@ import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; -import org.apache.ignite.configuration.DataRegionConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.IgniteInterruptedCheckedException; import org.apache.ignite.internal.IgniteNodeAttributes; import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; -import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection; @@ -62,7 +60,6 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccVersionAware; import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState; import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; -import org.apache.ignite.internal.processors.cache.persistence.DataRegion; import org.apache.ignite.internal.processors.timeout.GridTimeoutObject; import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter; import org.apache.ignite.internal.util.future.GridCompoundFuture; @@ -794,7 +791,7 @@ public void handleSupplyMessage( if (grp.mvccEnabled()) mvccPreloadEntries(topVer, node, p, infos); else - preloadEntriesBatched(topVer, node, p, infos); + preloadEntries(topVer, node, p, infos); } catch (GridDhtInvalidPartitionException ignored) { if (log.isDebugEnabled()) @@ -961,50 +958,6 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node } } - /** - * Adds entries with theirs history to partition p. - * - * @param node Node which sent entry. - * @param p Partition id. - * @param infos Entries info for preload. - * @param topVer Topology version. - * @throws IgniteInterruptedCheckedException If interrupted. - */ - private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - List infos) throws IgniteCheckedException { - GridCacheContext cctx = null; - - Iterator iter = infos.iterator(); - - // Loop through all received entries and try to preload them. - while (iter.hasNext()) { - ctx.database().checkpointReadLock(); - - try { - for (int i = 0; i < CHECKPOINT_THRESHOLD; i++) { - if (!iter.hasNext()) - break; - - GridCacheEntryInfo entry = iter.next(); - - if (cctx == null || (grp.sharedGroup() && entry.cacheId() != cctx.cacheId())) { - cctx = grp.sharedGroup() ? grp.shared().cacheContext(entry.cacheId()) : grp.singleCacheContext(); - - if (cctx == null) - continue; - else if (cctx.isNear()) - cctx = cctx.dhtCache().context(); - } - - preloadEntry(node, p, entry, topVer, cctx, null); - } - } - finally { - ctx.database().checkpointReadUnlock(); - } - } - } - /** * @param topVer Topology version. * @param from Node which sent entry. @@ -1012,7 +965,7 @@ else if (cctx.isNear()) * @param infos Preloaded entries. * @throws IgniteCheckedException If failed. */ - private void preloadEntriesBatched( + private void preloadEntries( AffinityTopologyVersion topVer, ClusterNode from, int p, @@ -1237,37 +1190,6 @@ private void updateCacheMetrics() { } } - /** - * Calculates whether there is enough free space in the current memory region for the specified amount of data. - * - * @param dataRegion Data region. - * @param size Data size in bytes. - * @return {@code True} if a specified amount of data can be stored in the memory region without evictions. - */ - private boolean isEnoughSpace(DataRegion dataRegion, long size) { - assert size >= 0 : size; - - DataRegionConfiguration cfg = dataRegion.config(); - - PageMemory pageMem = dataRegion.pageMemory(); - - int sysPageSize = pageMem.systemPageSize(); - - long pagesRequired = (long)Math.ceil(size / (double)sysPageSize); - - long maxPages = cfg.getMaxSize() / sysPageSize; - - // There are enough pages left. - if (pagesRequired < maxPages - pageMem.loadedPages()) - return true; - - // Empty pages pool size restricted. - if (pagesRequired > cfg.getEmptyPagesPoolSize()) - return false; - - return pagesRequired < Math.round(maxPages * (1.0d - cfg.getEvictionThreshold())); - } - /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridDhtPartitionDemander.class, this); From 8fc06f41d653a0b63113a0de499dd9130f4d0c88 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 8 Jul 2019 20:22:58 +0300 Subject: [PATCH 21/21] wip3 --- .../processors/cache/IgniteCacheOffheapManagerImpl.java | 1 + .../internal/processors/cache/persistence/RowStore.java | 1 + .../cache/persistence/freelist/AbstractFreeList.java | 6 +++--- .../processors/cache/persistence/freelist/FreeList.java | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index a8a363b846f09..7b40b38730675 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -1770,6 +1770,7 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo } } finally { + // Clean up unprocessed rows. while (iter.hasNext()) rowStore.removeRow(iter.next().link(), grp.statisticsHolderData()); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index de21e2af61248..aa3f98e388730 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -122,6 +122,7 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit /** * @param rows Rows. * @param statHolder Statistics holder to track IO operations. + * @param clo * @throws IgniteCheckedException If failed. */ public void addRows(Collection rows, IoStatisticsHolder statHolder, CAX clo) throws IgniteCheckedException { 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 3f7ce9684ee15..65403b46cadd0 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 @@ -554,11 +554,11 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken * * @param rows Rows. * @param statHolder Statistics holder to track IO operations. - * @param clo + * @param checkFreeSpace * @throws IgniteCheckedException If failed. */ @Override public void insertDataRows(Collection rows, - IoStatisticsHolder statHolder, CAX clo) throws IgniteCheckedException { + IoStatisticsHolder statHolder, CAX checkFreeSpace) throws IgniteCheckedException { try { Iterator iter = rows.iterator(); @@ -567,7 +567,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken int written = COMPLETE; while (iter.hasNext() || written != COMPLETE) { - clo.applyx(); + checkFreeSpace.applyx(); if (written == COMPLETE) { row = iter.next(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java index 25a784831062c..5fd28a7ae235d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java @@ -38,11 +38,11 @@ public interface FreeList { /** * @param rows Rows. * @param statHolder Statistics holder to track IO operations. - * @param clo + * @param checkFreeSpace * @throws IgniteCheckedException If failed. */ public void insertDataRows(Collection rows, IoStatisticsHolder statHolder, - CAX clo) throws IgniteCheckedException; + CAX checkFreeSpace) throws IgniteCheckedException; /** * @param link Row link.