You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2021/02/19 06:38:42 UTC
[ignite] branch master updated: IGNITE-13761 Clock and
Segmented-LRU page replacement - Fixes #8513.
This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 693cc44 IGNITE-13761 Clock and Segmented-LRU page replacement - Fixes #8513.
693cc44 is described below
commit 693cc44cddf186c0eaf76fb24ce7d2d2010276ce
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Fri Feb 19 09:29:57 2021 +0300
IGNITE-13761 Clock and Segmented-LRU page replacement - Fixes #8513.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../jmh/misc/JmhSegmentedLruListBenchmark.java | 122 +++++
.../configuration/DataRegionConfiguration.java | 36 +-
.../ignite/configuration/PageReplacementMode.java | 87 ++++
.../pagemem/ClockPageReplacementFlags.java | 154 ++++++
.../pagemem/ClockPageReplacementPolicy.java | 96 ++++
.../pagemem/ClockPageReplacementPolicyFactory.java | 33 ++
.../cache/persistence/pagemem/PageMemoryImpl.java | 532 ++++++++-------------
.../cache/persistence/pagemem/PagePool.java | 8 +
.../persistence/pagemem/PageReplacementPolicy.java | 69 +++
.../pagemem/PageReplacementPolicyFactory.java | 39 ++
.../pagemem/RandomLruPageReplacementPolicy.java | 247 ++++++++++
.../RandomLruPageReplacementPolicyFactory.java | 33 ++
.../persistence/pagemem/SegmentedLruPageList.java | 364 ++++++++++++++
.../pagemem/SegmentedLruPageReplacementPolicy.java | 102 ++++
.../SegmentedLruPageReplacementPolicyFactory.java | 33 ++
.../pagemem/BPlusTreePageMemoryImplTest.java | 2 +
.../BPlusTreeReuseListPageMemoryImplTest.java | 2 +
.../pagemem/ClockPageReplacementFlagsTest.java | 119 +++++
.../pagemem/IndexStoragePageMemoryImplTest.java | 2 +
.../pagemem/PageMemoryImplNoLoadTest.java | 2 +
.../pagemem/SegmentedLruPageListTest.java | 366 ++++++++++++++
.../ignite/testsuites/IgniteBasicTestSuite.java | 4 +
.../benchmark-cache-pagereplacements.properties | 124 +++++
.../benchmark-cache-pegereplacements.properties | 83 ----
.../ignite-localhost-pagereplacement-config.xml | 79 +++
.../ignite/yardstick/IgniteBenchmarkArguments.java | 23 +
.../org/apache/ignite/yardstick/IgniteNode.java | 8 +-
...=> IgniteAbstractPageReplacementBenchmark.java} | 89 +++-
.../IgniteGetWithPageReplacementBenchmark.java | 41 ++
.../IgnitePutWithPageReplacementBenchmark.java | 43 ++
30 files changed, 2506 insertions(+), 436 deletions(-)
diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/misc/JmhSegmentedLruListBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/misc/JmhSegmentedLruListBenchmark.java
new file mode 100644
index 0000000..fa418dd
--- /dev/null
+++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/misc/JmhSegmentedLruListBenchmark.java
@@ -0,0 +1,122 @@
+/*
+ * 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.misc;
+
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.internal.mem.DirectMemoryProvider;
+import org.apache.ignite.internal.mem.DirectMemoryRegion;
+import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
+import org.apache.ignite.internal.processors.cache.persistence.pagemem.SegmentedLruPageList;
+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.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.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/**
+ * Benchmarks {@link SegmentedLruPageList} class.
+ */
+@State(Scope.Benchmark)
+@Fork(1)
+@Threads(2)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 2)
+@Measurement(iterations = 10, time = 3)
+public class JmhSegmentedLruListBenchmark {
+ /** Pages count. */
+ private static final int PAGES_CNT = 1000;
+
+ /** Random numbers generator. */
+ private Random rnd;
+
+ /** Direct memory provider. */
+ DirectMemoryProvider provider;
+
+ /** LRU list. */
+ private SegmentedLruPageList lruList;
+
+ /**
+ * Setup.
+ */
+ @Setup(Level.Iteration)
+ public void setup() {
+ rnd = new Random(0);
+
+ provider = new UnsafeMemoryProvider(null);
+ provider.initialize(new long[] {SegmentedLruPageList.requiredMemory(PAGES_CNT)});
+
+ DirectMemoryRegion region = provider.nextRegion();
+
+ lruList = new SegmentedLruPageList(PAGES_CNT, region.address());
+
+ for (int i = 0; i < PAGES_CNT; i++)
+ lruList.addToTail(i, false);
+ }
+
+ /**
+ * Tear down.
+ */
+ @TearDown(Level.Iteration)
+ public void tearDown() {
+ provider.shutdown(true);
+ }
+
+ /**
+ * Benchmark {@link SegmentedLruPageList#moveToTail(int)} method.
+ */
+ @Benchmark
+ public void moveToTail() {
+ int nextIdx = rnd.nextInt(PAGES_CNT);
+
+ lruList.moveToTail(nextIdx);
+ }
+
+ /**
+ * Benchmark {@link SegmentedLruPageList#poll()} and {@link SegmentedLruPageList#addToTail(int, boolean)} methods.
+ */
+ @Benchmark
+ public void pollAndAdd() {
+ lruList.addToTail(lruList.poll(), rnd.nextBoolean());
+ }
+
+ /**
+ *
+ * @param args Args.
+ * @throws Exception Exception.
+ */
+ public static void main(String[] args) throws Exception {
+ final Options options = new OptionsBuilder()
+ .include(JmhSegmentedLruListBenchmark.class.getSimpleName())
+ .build();
+
+ new Runner(options).run();
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
index 39c4876..3f3b09a 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
@@ -80,6 +80,9 @@ public final class DataRegionConfiguration implements Serializable {
/** Default length of interval over which {@link DataRegionMetrics#getAllocationRate()} metric is calculated. */
public static final int DFLT_RATE_TIME_INTERVAL_MILLIS = 60_000;
+ /** Default page replacement mode. */
+ public static final PageReplacementMode DFLT_PAGE_REPLACEMENT_MODE = PageReplacementMode.CLOCK;
+
/** Data region name. */
private String name = DFLT_DATA_REG_DEFAULT_NAME;
@@ -93,9 +96,12 @@ public final class DataRegionConfiguration implements Serializable {
/** An optional path to a memory mapped files directory for this data region. */
private String swapPath;
- /** An algorithm for memory pages eviction. */
+ /** An algorithm for memory pages eviction (persistence is disabled). */
private DataPageEvictionMode pageEvictionMode = DataPageEvictionMode.DISABLED;
+ /** An algorithm for memory pages replacement (persistence is enabled). */
+ private PageReplacementMode pageReplacementMode = DFLT_PAGE_REPLACEMENT_MODE;
+
/**
* A threshold for memory pages eviction initiation. For instance, if the threshold is 0.9 it means that the page
* memory will start the eviction only after 90% data region is occupied.
@@ -243,6 +249,9 @@ public final class DataRegionConfiguration implements Serializable {
* memory exception will be thrown if the memory region usage, defined by this data region, goes beyond its
* capacity which is {@link #getMaxSize()}.
*
+ * Note: Page eviction is used only when persistence is disabled for data region. For persistent data regions see
+ * page replacement mode ({@link #getPageReplacementMode()}).
+ *
* @return Memory pages eviction algorithm. {@link DataPageEvictionMode#DISABLED} used by default.
*/
public DataPageEvictionMode getPageEvictionMode() {
@@ -262,6 +271,31 @@ public final class DataRegionConfiguration implements Serializable {
}
/**
+ * Gets memory pages replacement mode. If persistence is enabled and Ignite store on disk more data then available
+ * data region memory ({@link #getMaxSize()}) page replacement can be started to rotate memory pages with the disk.
+ * This parameter defines the algorithm to find pages to replace.
+ *
+ * Note: For not persistent data regions see page eviction mode ({@link #getPageEvictionMode()}).
+ *
+ * @return Memory pages replacement algorithm. {@link PageReplacementMode#CLOCK} used by default.
+ */
+ public PageReplacementMode getPageReplacementMode() {
+ return pageReplacementMode;
+ }
+
+ /**
+ * Sets memory pages replacement mode.
+ *
+ * @param replacementMode Page replacement mode.
+ * @return {@code this} for chaining.
+ */
+ public DataRegionConfiguration setPageReplacementMode(PageReplacementMode replacementMode) {
+ pageReplacementMode = replacementMode;
+
+ return this;
+ }
+
+ /**
* Gets a threshold for memory pages eviction initiation. For instance, if the threshold is 0.9 it means that the
* page memory will start the eviction only after 90% of the data region is occupied.
*
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/PageReplacementMode.java b/modules/core/src/main/java/org/apache/ignite/configuration/PageReplacementMode.java
new file mode 100644
index 0000000..c7a0ac2
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/PageReplacementMode.java
@@ -0,0 +1,87 @@
+/*
+* 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.configuration;
+
+import org.apache.ignite.DataRegionMetrics;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Defines memory page replacement algorithm. A mode is set for a specific {@link DataRegionConfiguration}.
+ */
+public enum PageReplacementMode {
+ /**
+ * Random-LRU algorithm.
+ *
+ * Every time page is accessed, its timestamp gets updated. When a page fault occurs and it's required to replace
+ * some pages, the algorithm randomly chooses 5 pages from the page memory and evicts a page with the latest
+ * timestamp.
+ *
+ * This algorithm has zero maintenance cost, but not very effective in terms of finding the next page to replace.
+ * Recommended to use in environments, where there are no page replacements (large enough data region to store all
+ * amount of data) or page replacements are rare (See {@link DataRegionMetrics#getPagesReplaceRate()} metric, which
+ * can be helpful here).
+ */
+ RANDOM_LRU,
+
+ /**
+ * Segmented-LRU algorithm.
+ *
+ * Segmented-LRU algorithm is a scan-resistant variation of the Least Recently Used (LRU) algorithm.
+ * Segmented-LRU pages list is divided into two segments, a probationary segment, and a protected segment. Pages in
+ * each segment are ordered from the least to the most recently accessed. New pages are added to the most recently
+ * accessed end (tail) of the probationary segment. Existing pages are removed from wherever they currently reside
+ * and added to the most recently accessed end of the protected segment. Pages in the protected segment have thus
+ * been accessed at least twice. The protected segment is finite, so migration of a page from the probationary
+ * segment to the protected segment may force the migration of the LRU page in the protected segment to the most
+ * recently used end of the probationary segment, giving this page another chance to be accessed before being
+ * replaced. Page to replace is polled from the least recently accessed end (head) of the probationary segment.
+ *
+ * This algorithm requires additional memory to store pages list and need to update this list on each page access,
+ * but have near to optimal page to replace selection policy. So, there can be a little performance drop for
+ * environments without page replacement (compared to random-LRU and CLOCK), but for environments with a high rate
+ * of page replacement and a large amount of one-time scans segmented-LRU can outperform random-LRU and CLOCK.
+ */
+ SEGMENTED_LRU,
+
+ /**
+ * CLOCK algorithm.
+ *
+ * The clock algorithm keeps a circular list of pages in memory, with the "hand" pointing to the last examined page
+ * frame in the list. When a page fault occurs and no empty frames exist, then the hit flag of the page is inspected
+ * at the hand's location. If the hit flag is 0, the new page is put in place of the page the "hand" points to, and
+ * the hand is advanced one position. Otherwise, the hit flag is cleared, then the clock hand is incremented and the
+ * process is repeated until a page is replaced.
+ *
+ * This algorithm has near to zero maintenance cost and replacement policy efficiency between random-LRU and
+ * segmented-LRU.
+ */
+ CLOCK;
+
+ /** Enumerated values. */
+ private static final PageReplacementMode[] VALS = values();
+
+ /**
+ * Efficiently gets enumerated value from its ordinal.
+ *
+ * @param ord Ordinal value.
+ * @return Enumerated value or {@code null} if ordinal out of range.
+ */
+ @Nullable public static PageReplacementMode fromOrdinal(int ord) {
+ return ord >= 0 && ord < VALS.length ? VALS[ord] : null;
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementFlags.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementFlags.java
new file mode 100644
index 0000000..84b06d2
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementFlags.java
@@ -0,0 +1,154 @@
+/*
+ * 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.function.LongUnaryOperator;
+import org.apache.ignite.configuration.PageReplacementMode;
+import org.apache.ignite.internal.util.GridUnsafe;
+
+/**
+ * Clock page replacement algorithm implementation.
+ *
+ * @see PageReplacementMode#CLOCK
+ */
+public class ClockPageReplacementFlags {
+ /** Total pages count. */
+ private final int pagesCnt;
+
+ /** Index of the next candidate ("hand"). */
+ private int curIdx;
+
+ /** Pointer to memory region to store page hit flags. */
+ private final long flagsPtr;
+
+ /**
+ * @param totalPagesCnt Total pages count.
+ * @param memPtr Pointer to memory region.
+ */
+ ClockPageReplacementFlags(int totalPagesCnt, long memPtr) {
+ pagesCnt = totalPagesCnt;
+ flagsPtr = memPtr;
+
+ GridUnsafe.setMemory(flagsPtr, (totalPagesCnt + 7) >> 3, (byte)0);
+ }
+
+ /**
+ * Find page to replace.
+ *
+ * @return Page index to replace.
+ */
+ public int poll() {
+ // This method is always executed under exclusive lock, no other synchronization or CAS required.
+ while (true) {
+ if (curIdx >= pagesCnt)
+ curIdx = 0;
+
+ long ptr = flagsPtr + ((curIdx >> 3) & (~7L));
+
+ long flags = GridUnsafe.getLong(ptr);
+
+ if (((curIdx & 63) == 0) && (flags == ~0L)) {
+ GridUnsafe.putLong(ptr, 0L);
+
+ curIdx += 64;
+
+ continue;
+ }
+
+ long mask = ~0L << curIdx;
+
+ int bitIdx = Long.numberOfTrailingZeros(~flags & mask);
+
+ if (bitIdx == 64) {
+ GridUnsafe.putLong(ptr, flags & ~mask);
+
+ curIdx = (curIdx & ~63) + 64;
+ }
+ else {
+ mask &= ~(~0L << bitIdx);
+
+ GridUnsafe.putLong(ptr, flags & ~mask);
+
+ curIdx = (curIdx & ~63) + bitIdx + 1;
+
+ if (curIdx <= pagesCnt)
+ return curIdx - 1;
+ }
+ }
+ }
+
+ /**
+ * Get page hit flag.
+ *
+ * @param pageIdx Page index.
+ */
+ boolean getFlag(int pageIdx) {
+ long flags = GridUnsafe.getLong(flagsPtr + ((pageIdx >> 3) & (~7L)));
+
+ return (flags & (1L << pageIdx)) != 0L;
+ }
+
+ /**
+ * Clear page hit flag.
+ *
+ * @param pageIdx Page index.
+ */
+ public void clearFlag(int pageIdx) {
+ compareAndSwapFlag(pageIdx, flags -> flags & ~(1L << pageIdx));
+ }
+
+ /**
+ * Set page hit flag.
+ *
+ * @param pageIdx Page index.
+ */
+ public void setFlag(int pageIdx) {
+ compareAndSwapFlag(pageIdx, flags -> flags | (1L << pageIdx));
+ }
+
+ /**
+ * CAS page hit flag value.
+ *
+ * @param pageIdx Page index.
+ * @param func Function to apply to flags.
+ */
+ private void compareAndSwapFlag(int pageIdx, LongUnaryOperator func) {
+ long ptr = flagsPtr + ((pageIdx >> 3) & (~7L));
+
+ long oldFlags;
+ long newFlags;
+
+ do {
+ oldFlags = GridUnsafe.getLong(ptr);
+ newFlags = func.applyAsLong(oldFlags);
+
+ if (oldFlags == newFlags)
+ return;
+ }
+ while (!GridUnsafe.compareAndSwapLong(null, ptr, oldFlags, newFlags));
+ }
+
+ /**
+ * Memory required to service {@code pagesCnt} pages.
+ *
+ * @param pagesCnt Pages count.
+ */
+ public static long requiredMemory(int pagesCnt) {
+ return ((pagesCnt + 63) / 8) & (~7L) /* 1 bit per page + 8 byte align */;
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementPolicy.java
new file mode 100644
index 0000000..85f7b31
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementPolicy.java
@@ -0,0 +1,96 @@
+/*
+ * 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 org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.PageReplacementMode;
+import org.apache.ignite.internal.pagemem.FullPageId;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+
+import static org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl.INVALID_REL_PTR;
+import static org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl.OUTDATED_REL_PTR;
+
+/**
+ * CLOCK page replacement policy implementation.
+ *
+ * @see PageReplacementMode#CLOCK
+ */
+public class ClockPageReplacementPolicy extends PageReplacementPolicy {
+ /** Pages hit-flags store. */
+ private final ClockPageReplacementFlags flags;
+
+ /**
+ * @param seg Page memory segment.
+ * @param ptr Pointer to memory region.
+ * @param pagesCnt Pages count.
+ */
+ protected ClockPageReplacementPolicy(PageMemoryImpl.Segment seg, long ptr, int pagesCnt) {
+ super(seg);
+
+ flags = new ClockPageReplacementFlags(pagesCnt, ptr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onHit(long relPtr) {
+ int pageIdx = (int)seg.pageIndex(relPtr);
+
+ flags.setFlag(pageIdx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onRemove(long relPtr) {
+ int pageIdx = (int)seg.pageIndex(relPtr);
+
+ flags.clearFlag(pageIdx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long replace() throws IgniteCheckedException {
+ LoadedPagesMap loadedPages = seg.loadedPages();
+
+ for (int i = 0; i < loadedPages.size(); i++) {
+ int pageIdx = flags.poll();
+
+ long relPtr = seg.relative(pageIdx);
+ long absPtr = seg.absolute(relPtr);
+
+ FullPageId fullId = PageHeader.fullPageId(absPtr);
+
+ // Check loaded pages map for outdated page.
+ relPtr = loadedPages.get(
+ fullId.groupId(),
+ fullId.effectivePageId(),
+ seg.partGeneration(fullId.groupId(), PageIdUtils.partId(fullId.pageId())),
+ INVALID_REL_PTR,
+ OUTDATED_REL_PTR
+ );
+
+ assert relPtr != INVALID_REL_PTR;
+
+ if (relPtr == OUTDATED_REL_PTR)
+ return seg.refreshOutdatedPage(fullId.groupId(), fullId.pageId(), true);
+
+ if (seg.tryToRemovePage(fullId, absPtr))
+ return relPtr;
+
+ flags.setFlag(pageIdx);
+ }
+
+ throw seg.oomException("no pages to replace");
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementPolicyFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementPolicyFactory.java
new file mode 100644
index 0000000..7dc9e50
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementPolicyFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * {@link ClockPageReplacementPolicy} factory.
+ */
+public class ClockPageReplacementPolicyFactory implements PageReplacementPolicyFactory {
+ /** {@inheritDoc} */
+ @Override public long requiredMemory(int pagesCnt) {
+ return ClockPageReplacementFlags.requiredMemory(pagesCnt);
+ }
+
+ /** {@inheritDoc} */
+ @Override public PageReplacementPolicy create(PageMemoryImpl.Segment seg, long ptr, int pagesCnt) {
+ return new ClockPageReplacementPolicy(seg, ptr, pagesCnt);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java
index 67405cf..1024219 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImpl.java
@@ -25,7 +25,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
@@ -42,6 +41,7 @@ import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.PageReplacementMode;
import org.apache.ignite.events.EventType;
import org.apache.ignite.events.PageReplacementStartedEvent;
import org.apache.ignite.failure.FailureContext;
@@ -70,12 +70,9 @@ import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetrics
import org.apache.ignite.internal.processors.cache.persistence.PageStoreWriter;
import org.apache.ignite.internal.processors.cache.persistence.StorageException;
import org.apache.ignite.internal.processors.cache.persistence.checkpoint.CheckpointProgress;
-import org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListMetaIO;
import org.apache.ignite.internal.processors.cache.persistence.partstate.GroupPartitionId;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionCountersIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.TrackingPageIO;
import org.apache.ignite.internal.processors.cache.persistence.wal.WALPointer;
import org.apache.ignite.internal.processors.cache.persistence.wal.crc.IgniteDataIntegrityViolationException;
@@ -138,10 +135,10 @@ public class PageMemoryImpl implements PageMemoryEx {
public static final long RELATIVE_PTR_MASK = 0xFFFFFFFFFFFFFFL;
/** Invalid relative pointer value. */
- public static final long INVALID_REL_PTR = RELATIVE_PTR_MASK;
+ static final long INVALID_REL_PTR = RELATIVE_PTR_MASK;
/** Pointer which means that this page is outdated (for example, cache was destroyed, partition eviction'd happened */
- private static final long OUTDATED_REL_PTR = INVALID_REL_PTR + 1;
+ static final long OUTDATED_REL_PTR = INVALID_REL_PTR + 1;
/** Page lock offset. */
public static final int PAGE_LOCK_OFFSET = 32;
@@ -157,9 +154,6 @@ public class PageMemoryImpl implements PageMemoryEx {
*/
public static final int PAGE_OVERHEAD = 48;
- /** Number of random pages that will be picked for eviction. */
- public static final int RANDOM_PAGES_EVICT_NUM = 5;
-
/** Try again tag. */
public static final int TRY_AGAIN_TAG = -1;
@@ -192,6 +186,9 @@ public class PageMemoryImpl implements PageMemoryEx {
private final boolean useBackwardShiftMap =
IgniteSystemProperties.getBoolean(IGNITE_LOADED_PAGES_BACKWARD_SHIFT_MAP, DFLT_LOADED_PAGES_BACKWARD_SHIFT_MAP);
+ /** Page replacement policy factory. */
+ private final PageReplacementPolicyFactory pageReplacementPolicyFactory;
+
/** */
private final ExecutorService asyncRunner;
@@ -340,6 +337,28 @@ public class PageMemoryImpl implements PageMemoryEx {
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(Runtime.getRuntime().availableProcessors()),
new IgniteThreadFactory(ctx.igniteInstanceName(), "page-mem-op"));
+
+ DataRegionConfiguration memCfg = getDataRegionConfiguration();
+
+ PageReplacementMode pageReplacementMode = memCfg == null ? DataRegionConfiguration.DFLT_PAGE_REPLACEMENT_MODE :
+ memCfg.getPageReplacementMode();
+
+ switch (pageReplacementMode) {
+ case RANDOM_LRU:
+ pageReplacementPolicyFactory = new RandomLruPageReplacementPolicyFactory();
+
+ break;
+ case SEGMENTED_LRU:
+ pageReplacementPolicyFactory = new SegmentedLruPageReplacementPolicyFactory();
+
+ break;
+ case CLOCK:
+ pageReplacementPolicyFactory = new ClockPageReplacementPolicyFactory();
+
+ break;
+ default:
+ throw new IgniteException("Unexpected page replacement mode: " + pageReplacementMode);
+ }
}
/** {@inheritDoc} */
@@ -376,6 +395,7 @@ public class PageMemoryImpl implements PageMemoryEx {
long totalAllocated = 0;
int pages = 0;
long totalTblSize = 0;
+ long totalReplSize = 0;
for (int i = 0; i < regs - 1; i++) {
assert i < segments.length;
@@ -388,19 +408,21 @@ public class PageMemoryImpl implements PageMemoryEx {
pages += segments[i].pages();
totalTblSize += segments[i].tableSize();
+ totalReplSize += segments[i].replacementSize();
}
initWriteThrottle();
this.segments = segments;
- if (log.isInfoEnabled())
+ if (log.isInfoEnabled()) {
log.info("Started page memory [memoryAllocated=" + U.readableSize(totalAllocated, false) +
", pages=" + pages +
", tableSize=" + U.readableSize(totalTblSize, false) +
+ ", replacementSize=" + U.readableSize(totalReplSize, false) +
", checkpointBuffer=" + U.readableSize(checkpointBuf, false) +
']');
-
+ }
}
}
@@ -533,9 +555,6 @@ public class PageMemoryImpl implements PageMemoryEx {
// because there is no crc inside them.
Segment seg = segment(grpId, pageId);
- DelayedDirtyPageStoreWrite delayedWriter = delayedPageReplacementTracker != null
- ? delayedPageReplacementTracker.delayedPageWrite() : null;
-
seg.writeLock().lock();
boolean isTrackingPage = changeTracker != null &&
@@ -555,14 +574,17 @@ public class PageMemoryImpl implements PageMemoryEx {
OUTDATED_REL_PTR
);
- if (relPtr == OUTDATED_REL_PTR)
- relPtr = refreshOutdatedPage(seg, grpId, pageId, false);
+ if (relPtr == OUTDATED_REL_PTR) {
+ relPtr = seg.refreshOutdatedPage(grpId, pageId, false);
+
+ seg.pageReplacementPolicy.onRemove(relPtr);
+ }
if (relPtr == INVALID_REL_PTR)
relPtr = seg.borrowOrAllocateFreePage(pageId);
if (relPtr == INVALID_REL_PTR)
- relPtr = seg.removePageForReplacement(delayedWriter == null ? flushDirtyPage : delayedWriter);
+ relPtr = seg.removePageForReplacement();
long absPtr = seg.absolute(relPtr);
@@ -606,6 +628,8 @@ public class PageMemoryImpl implements PageMemoryEx {
}
}
+ seg.pageReplacementPolicy.onMiss(relPtr);
+
seg.loadedPages.put(grpId, PageIdUtils.effectivePageId(pageId), relPtr, seg.partGeneration(grpId, partId));
}
catch (IgniteOutOfMemoryException oom) {
@@ -617,7 +641,6 @@ public class PageMemoryImpl implements PageMemoryEx {
", maxSize=" + U.readableSize(dataRegionCfg.getMaxSize(), false) +
", persistenceEnabled=" + dataRegionCfg.isPersistenceEnabled() + "] Try the following:" + U.nl() +
" ^-- Increase maximum off-heap memory size (DataRegionConfiguration.maxSize)" + U.nl() +
- " ^-- Enable Ignite persistence (DataRegionConfiguration.persistenceEnabled)" + U.nl() +
" ^-- Enable eviction or expiration policies"
);
@@ -631,9 +654,9 @@ public class PageMemoryImpl implements PageMemoryEx {
seg.writeLock().unlock();
}
- //Finish replacement only when an exception wasn't thrown otherwise it possible to corrupt B+Tree.
- if (delayedWriter != null)
- delayedWriter.finishReplacement();
+ // Finish replacement only when an exception wasn't thrown otherwise it possible to corrupt B+Tree.
+ if (delayedPageReplacementTracker != null)
+ delayedPageReplacementTracker.delayedPageWrite().finishReplacement();
//we have allocated 'tracking' page, we need to allocate regular one
return isTrackingPage ? allocatePage(grpId, partId, flags) : pageId;
@@ -739,6 +762,8 @@ public class PageMemoryImpl implements PageMemoryEx {
seg.acquirePage(absPtr);
+ seg.pageReplacementPolicy.onHit(relPtr);
+
statHolder.trackLogicalRead(absPtr + PAGE_OVERHEAD);
return absPtr;
@@ -748,9 +773,6 @@ public class PageMemoryImpl implements PageMemoryEx {
seg.readLock().unlock();
}
- DelayedDirtyPageStoreWrite delayedWriter = delayedPageReplacementTracker != null
- ? delayedPageReplacementTracker.delayedPageWrite() : null;
-
FullPageId fullId = new FullPageId(pageId, grpId);
seg.writeLock().lock();
@@ -777,7 +799,7 @@ public class PageMemoryImpl implements PageMemoryEx {
pageAllocated.set(true);
if (relPtr == INVALID_REL_PTR)
- relPtr = seg.removePageForReplacement(delayedWriter == null ? flushDirtyPage : delayedWriter);
+ relPtr = seg.removePageForReplacement();
absPtr = seg.absolute(relPtr);
@@ -791,6 +813,8 @@ public class PageMemoryImpl implements PageMemoryEx {
// We can clear dirty flag after the page has been allocated.
setDirty(fullId, absPtr, false, false);
+ seg.pageReplacementPolicy.onMiss(relPtr);
+
seg.loadedPages.put(
grpId,
fullId.effectivePageId(),
@@ -826,7 +850,7 @@ public class PageMemoryImpl implements PageMemoryEx {
else if (relPtr == OUTDATED_REL_PTR) {
assert PageIdUtils.pageIndex(pageId) == 0 : fullId;
- relPtr = refreshOutdatedPage(seg, grpId, pageId, false);
+ relPtr = seg.refreshOutdatedPage(grpId, pageId, false);
absPtr = seg.absolute(relPtr);
@@ -843,10 +867,16 @@ public class PageMemoryImpl implements PageMemoryEx {
", absPtr=" + U.hexLong(absPtr) + ']';
rwLock.init(absPtr + PAGE_LOCK_OFFSET, PageIdUtils.tag(pageId));
+
+ seg.pageReplacementPolicy.onRemove(relPtr);
+ seg.pageReplacementPolicy.onMiss(relPtr);
}
- else
+ else {
absPtr = seg.absolute(relPtr);
+ seg.pageReplacementPolicy.onHit(relPtr);
+ }
+
seg.acquirePage(absPtr);
if (!readPageFromStore)
@@ -862,8 +892,8 @@ public class PageMemoryImpl implements PageMemoryEx {
finally {
seg.writeLock().unlock();
- if (delayedWriter != null)
- delayedWriter.finishReplacement();
+ if (delayedPageReplacementTracker != null)
+ delayedPageReplacementTracker.delayedPageWrite().finishReplacement();
if (readPageFromStore) {
assert lockedPageAbsPtr != -1 : "Page is expected to have a valid address [pageId=" + fullId +
@@ -906,57 +936,6 @@ public class PageMemoryImpl implements PageMemoryEx {
}
}
- /**
- * @param seg Segment.
- * @param grpId Cache group ID.
- * @param pageId Page ID.
- * @param rmv {@code True} if page should be removed.
- * @return Relative pointer to refreshed page.
- */
- private long refreshOutdatedPage(Segment seg, int grpId, long pageId, boolean rmv) {
- assert seg.writeLock().isHeldByCurrentThread();
-
- int tag = seg.partGeneration(grpId, PageIdUtils.partId(pageId));
-
- long relPtr = seg.loadedPages.refresh(grpId, PageIdUtils.effectivePageId(pageId), tag);
-
- long absPtr = seg.absolute(relPtr);
-
- GridUnsafe.setMemory(absPtr + PAGE_OVERHEAD, pageSize(), (byte)0);
-
- PageHeader.dirty(absPtr, false);
-
- long tmpBufPtr = PageHeader.tempBufferPointer(absPtr);
-
- if (tmpBufPtr != INVALID_REL_PTR) {
- GridUnsafe.setMemory(checkpointPool.absolute(tmpBufPtr) + PAGE_OVERHEAD, pageSize(), (byte)0);
-
- PageHeader.tempBufferPointer(absPtr, INVALID_REL_PTR);
-
- // We pinned the page when allocated the temp buffer, release it now.
- PageHeader.releasePage(absPtr);
-
- releaseCheckpointBufferPage(tmpBufPtr);
- }
-
- if (rmv)
- seg.loadedPages.remove(grpId, PageIdUtils.effectivePageId(pageId));
-
- CheckpointPages cpPages = seg.checkpointPages;
-
- if (cpPages != null)
- cpPages.markAsSaved(new FullPageId(pageId, grpId));
-
- Collection<FullPageId> dirtyPages = seg.dirtyPages;
-
- if (dirtyPages != null) {
- if (dirtyPages.remove(new FullPageId(pageId, grpId)))
- seg.dirtyPagesCntr.decrementAndGet();
- }
-
- return relPtr;
- }
-
/** */
private void releaseCheckpointBufferPage(long tmpBufPtr) {
int resCntr = checkpointPool.releaseFreePage(tmpBufPtr);
@@ -1245,13 +1224,14 @@ public class PageMemoryImpl implements PageMemoryEx {
return;
if (relPtr == OUTDATED_REL_PTR) {
- relPtr = refreshOutdatedPage(
- seg,
+ relPtr = seg.refreshOutdatedPage(
fullId.groupId(),
fullId.effectivePageId(),
true
);
+ seg.pageReplacementPolicy.onRemove(relPtr);
+
seg.pool.releaseFreePage(relPtr);
}
@@ -1973,13 +1953,10 @@ public class PageMemoryImpl implements PageMemoryEx {
/**
*
*/
- private class Segment extends ReentrantReadWriteLock {
+ class Segment extends ReentrantReadWriteLock {
/** */
private static final long serialVersionUID = 0L;
- /** */
- private static final double FULL_SCAN_THRESHOLD = 0.4;
-
/** Pointer to acquired pages integer counter. */
private static final int ACQUIRED_PAGES_SIZEOF = 4;
@@ -1995,9 +1972,15 @@ public class PageMemoryImpl implements PageMemoryEx {
/** */
private PagePool pool;
+ /** */
+ private final PageReplacementPolicy pageReplacementPolicy;
+
/** Bytes required to store {@link #loadedPages}. */
private long memPerTbl;
+ /** Bytes required to store {@link #pageReplacementPolicy} service data. */
+ private long memPerRepl;
+
/** Pages marked as dirty since the last checkpoint. */
private volatile Collection<FullPageId> dirtyPages = new GridConcurrentHashSet<>();
@@ -2038,16 +2021,23 @@ public class PageMemoryImpl implements PageMemoryEx {
memPerTbl = useBackwardShiftMap
? RobinHoodBackwardShiftHashMap.requiredMemory(pages)
- : requiredSegmentTableMemory(pages);
+ : FullPageIdTable.requiredMemory(pages);
loadedPages = useBackwardShiftMap
? new RobinHoodBackwardShiftHashMap(ldPagesAddr, memPerTbl)
: new FullPageIdTable(ldPagesAddr, memPerTbl, true);
- DirectMemoryRegion poolRegion = region.slice(memPerTbl + ldPagesMapOffInRegion);
+ pages = (int)((totalMemory - memPerTbl - ldPagesMapOffInRegion) / sysPageSize);
+
+ memPerRepl = pageReplacementPolicyFactory.requiredMemory(pages);
+
+ DirectMemoryRegion poolRegion = region.slice(memPerTbl + memPerRepl + ldPagesMapOffInRegion);
pool = new PagePool(idx, poolRegion, sysPageSize, rwLock);
+ pageReplacementPolicy = pageReplacementPolicyFactory.create(this,
+ region.address() + memPerTbl + ldPagesMapOffInRegion, pool.pages());
+
maxDirtyPages = throttlingPlc != ThrottlingPolicy.DISABLED
? pool.pages() * 3L / 4
: Math.min(pool.pages() * 2L / 3, cpPoolPages);
@@ -2096,6 +2086,13 @@ public class PageMemoryImpl implements PageMemoryEx {
}
/**
+ * @return Memory allocated for page replacement service data.
+ */
+ private long replacementSize() {
+ return memPerRepl;
+ }
+
+ /**
* @param absPtr Page absolute address to acquire.
*/
private void acquirePage(long absPtr) {
@@ -2142,11 +2139,10 @@ public class PageMemoryImpl implements PageMemoryEx {
*
* @param fullPageId Candidate page full ID.
* @param absPtr Absolute pointer of the page to evict.
- * @param saveDirtyPage implementation to save dirty page to persistent storage.
* @return {@code True} if it is ok to replace this page, {@code false} if another page should be selected.
* @throws IgniteCheckedException If failed to write page to the underlying store during eviction.
*/
- private boolean preparePageRemoval(FullPageId fullPageId, long absPtr, PageStoreWriter saveDirtyPage) throws IgniteCheckedException {
+ public boolean tryToRemovePage(FullPageId fullPageId, long absPtr) throws IgniteCheckedException {
assert writeLock().isHeldByCurrentThread();
// Do not evict cache meta pages.
@@ -2167,6 +2163,9 @@ public class PageMemoryImpl implements PageMemoryEx {
memMetrics.updatePageReplaceRate(U.currentTimeMillis() - PageHeader.readTimestamp(absPtr));
+ PageStoreWriter saveDirtyPage = delayedPageReplacementTracker != null
+ ? delayedPageReplacementTracker.delayedPageWrite() : flushDirtyPage;
+
saveDirtyPage.writePage(
fullPageId,
wrapPointer(absPtr + PAGE_OVERHEAD, pageSize()),
@@ -2180,6 +2179,8 @@ public class PageMemoryImpl implements PageMemoryEx {
checkpointPages.markAsSaved(fullPageId);
+ loadedPages.remove(fullPageId.groupId(), fullPageId.effectivePageId());
+
return true;
}
@@ -2188,6 +2189,8 @@ public class PageMemoryImpl implements PageMemoryEx {
else {
memMetrics.updatePageReplaceRate(U.currentTimeMillis() - PageHeader.readTimestamp(absPtr));
+ loadedPages.remove(fullPageId.groupId(), fullPageId.effectivePageId());
+
// Page was not modified, ok to evict.
return true;
}
@@ -2232,13 +2235,62 @@ public class PageMemoryImpl implements PageMemoryEx {
}
/**
+ * @param grpId Cache group ID.
+ * @param pageId Page ID.
+ * @param rmv {@code True} if page should be removed.
+ * @return Relative pointer to refreshed page.
+ */
+ public long refreshOutdatedPage(int grpId, long pageId, boolean rmv) {
+ assert writeLock().isHeldByCurrentThread();
+
+ int tag = partGeneration(grpId, PageIdUtils.partId(pageId));
+
+ long relPtr = loadedPages.refresh(grpId, PageIdUtils.effectivePageId(pageId), tag);
+
+ long absPtr = absolute(relPtr);
+
+ GridUnsafe.setMemory(absPtr + PAGE_OVERHEAD, pageSize(), (byte)0);
+
+ PageHeader.dirty(absPtr, false);
+
+ long tmpBufPtr = PageHeader.tempBufferPointer(absPtr);
+
+ if (tmpBufPtr != INVALID_REL_PTR) {
+ GridUnsafe.setMemory(checkpointPool.absolute(tmpBufPtr) + PAGE_OVERHEAD, pageSize(), (byte)0);
+
+ PageHeader.tempBufferPointer(absPtr, INVALID_REL_PTR);
+
+ // We pinned the page when allocated the temp buffer, release it now.
+ PageHeader.releasePage(absPtr);
+
+ releaseCheckpointBufferPage(tmpBufPtr);
+ }
+
+ if (rmv)
+ loadedPages.remove(grpId, PageIdUtils.effectivePageId(pageId));
+
+ CheckpointPages cpPages = checkpointPages;
+
+ if (cpPages != null)
+ cpPages.markAsSaved(new FullPageId(pageId, grpId));
+
+ Collection<FullPageId> dirtyPages = this.dirtyPages;
+
+ if (dirtyPages != null) {
+ if (dirtyPages.remove(new FullPageId(pageId, grpId)))
+ dirtyPagesCntr.decrementAndGet();
+ }
+
+ return relPtr;
+ }
+
+ /**
* Removes random oldest page for page replacement from memory to storage.
*
* @return Relative address for removed page, now it can be replaced by allocated or reloaded page.
* @throws IgniteCheckedException If failed to evict page.
- * @param saveDirtyPage Replaced page writer, implementation to save dirty page to persistent storage.
*/
- private long removePageForReplacement(PageStoreWriter saveDirtyPage) throws IgniteCheckedException {
+ private long removePageForReplacement() throws IgniteCheckedException {
assert getWriteHoldCount() > 0;
if (pageReplacementWarned == 0) {
@@ -2262,243 +2314,33 @@ public class PageMemoryImpl implements PageMemoryEx {
}
}
- final ThreadLocalRandom rnd = ThreadLocalRandom.current();
-
- final int cap = loadedPages.capacity();
-
- if (acquiredPages() >= loadedPages.size()) {
- DataRegionConfiguration dataRegionCfg = getDataRegionConfiguration();
-
- throw new IgniteOutOfMemoryException("Failed to evict page from segment (all pages are acquired)."
- + U.nl() + "Out of memory in data region [" +
- "name=" + dataRegionCfg.getName() +
- ", initSize=" + U.readableSize(dataRegionCfg.getInitialSize(), false) +
- ", maxSize=" + U.readableSize(dataRegionCfg.getMaxSize(), false) +
- ", persistenceEnabled=" + dataRegionCfg.isPersistenceEnabled() + "] Try the following:" + U.nl() +
- " ^-- Increase maximum off-heap memory size (DataRegionConfiguration.maxSize)" + U.nl() +
- " ^-- Enable Ignite persistence (DataRegionConfiguration.persistenceEnabled)" + U.nl() +
- " ^-- Enable eviction or expiration policies"
- );
- }
-
- // With big number of random picked pages we may fall into infinite loop, because
- // every time the same page may be found.
- Set<Long> ignored = null;
-
- long relRmvAddr = INVALID_REL_PTR;
-
- int iterations = 0;
-
- while (true) {
- long cleanAddr = INVALID_REL_PTR;
- long cleanTs = Long.MAX_VALUE;
- long dirtyAddr = INVALID_REL_PTR;
- long dirtyTs = Long.MAX_VALUE;
- long metaAddr = INVALID_REL_PTR;
- long metaTs = Long.MAX_VALUE;
-
- for (int i = 0; i < RANDOM_PAGES_EVICT_NUM; i++) {
- ++iterations;
-
- if (iterations > pool.pages() * FULL_SCAN_THRESHOLD)
- break;
-
- // We need to lookup for pages only in current segment for thread safety,
- // so peeking random memory will lead to checking for found page segment.
- // It's much faster to check available pages for segment right away.
- ReplaceCandidate nearest = loadedPages.getNearestAt(rnd.nextInt(cap));
-
- assert nearest != null && nearest.relativePointer() != INVALID_REL_PTR;
-
- long rndAddr = nearest.relativePointer();
-
- int partGen = nearest.generation();
-
- final long absPageAddr = absolute(rndAddr);
-
- FullPageId fullId = PageHeader.fullPageId(absPageAddr);
-
- // Check page mapping consistency.
- assert fullId.equals(nearest.fullId()) : "Invalid page mapping [tableId=" + nearest.fullId() +
- ", actual=" + fullId + ", nearest=" + nearest;
-
- boolean outdated = partGen < partGeneration(fullId.groupId(), PageIdUtils.partId(fullId.pageId()));
-
- if (outdated)
- return refreshOutdatedPage(this, fullId.groupId(), fullId.pageId(), true);
-
- boolean pinned = PageHeader.isAcquired(absPageAddr);
-
- boolean skip = ignored != null && ignored.contains(rndAddr);
-
- final boolean dirty = isDirty(absPageAddr);
-
- CheckpointPages checkpointPages = this.checkpointPages;
-
- if (relRmvAddr == rndAddr || pinned || skip ||
- fullId.pageId() == META_PAGE_ID ||
- (dirty && (checkpointPages == null || !checkpointPages.contains(fullId)))
- ) {
- i--;
-
- continue;
- }
-
- final long pageTs = PageHeader.readTimestamp(absPageAddr);
-
- final boolean storMeta = isStoreMetadataPage(absPageAddr);
-
- if (pageTs < cleanTs && !dirty && !storMeta) {
- cleanAddr = rndAddr;
-
- cleanTs = pageTs;
- }
- else if (pageTs < dirtyTs && dirty && !storMeta) {
- dirtyAddr = rndAddr;
-
- dirtyTs = pageTs;
- }
- else if (pageTs < metaTs && storMeta) {
- metaAddr = rndAddr;
-
- metaTs = pageTs;
- }
-
- if (cleanAddr != INVALID_REL_PTR)
- relRmvAddr = cleanAddr;
- else if (dirtyAddr != INVALID_REL_PTR)
- relRmvAddr = dirtyAddr;
- else
- relRmvAddr = metaAddr;
- }
-
- if (relRmvAddr == INVALID_REL_PTR)
- return tryToFindSequentially(cap, saveDirtyPage);
-
- final long absRmvAddr = absolute(relRmvAddr);
-
- final FullPageId fullPageId = PageHeader.fullPageId(absRmvAddr);
-
- if (!preparePageRemoval(fullPageId, absRmvAddr, saveDirtyPage)) {
- if (iterations > 10) {
- if (ignored == null)
- ignored = new HashSet<>();
-
- ignored.add(relRmvAddr);
- }
-
- if (iterations > pool.pages() * FULL_SCAN_THRESHOLD)
- return tryToFindSequentially(cap, saveDirtyPage);
-
- continue;
- }
-
- loadedPages.remove(
- fullPageId.groupId(),
- fullPageId.effectivePageId()
- );
-
- return relRmvAddr;
- }
- }
-
- /**
- * @param absPageAddr Absolute page address
- * @return {@code True} if page is related to partition metadata, which is loaded in saveStoreMetadata().
- */
- private boolean isStoreMetadataPage(long absPageAddr) {
- try {
- long dataAddr = absPageAddr + PAGE_OVERHEAD;
+ if (acquiredPages() >= loadedPages.size())
+ throw oomException("all pages are acquired");
- int type = PageIO.getType(dataAddr);
- int ver = PageIO.getVersion(dataAddr);
-
- PageIO io = PageIO.getPageIO(type, ver);
-
- return io instanceof PagePartitionMetaIO
- || io instanceof PagesListMetaIO
- || io instanceof PagePartitionCountersIO;
- }
- catch (IgniteCheckedException ignored) {
- return false;
- }
+ return pageReplacementPolicy.replace();
}
/**
- * Will scan all segment pages to find one to evict it
+ * Creates out of memory exception with additional information.
*
- * @param cap Capacity.
- * @param saveDirtyPage Evicted page writer.
+ * @param reason Reason.
*/
- private long tryToFindSequentially(int cap, PageStoreWriter saveDirtyPage) throws IgniteCheckedException {
- assert getWriteHoldCount() > 0;
-
- long prevAddr = INVALID_REL_PTR;
- int pinnedCnt = 0;
- int failToPrepare = 0;
-
- for (int i = 0; i < cap; i++) {
- final ReplaceCandidate nearest = loadedPages.getNearestAt(i);
-
- assert nearest != null && nearest.relativePointer() != INVALID_REL_PTR;
-
- final long addr = nearest.relativePointer();
-
- int partGen = nearest.generation();
-
- final long absPageAddr = absolute(addr);
-
- FullPageId fullId = PageHeader.fullPageId(absPageAddr);
-
- if (partGen < partGeneration(fullId.groupId(), PageIdUtils.partId(fullId.pageId())))
- return refreshOutdatedPage(this, fullId.groupId(), fullId.pageId(), true);
-
- boolean pinned = PageHeader.isAcquired(absPageAddr);
-
- if (pinned)
- pinnedCnt++;
-
- if (addr == prevAddr || pinned)
- continue;
-
- final long absEvictAddr = absolute(addr);
-
- final FullPageId fullPageId = PageHeader.fullPageId(absEvictAddr);
-
- if (preparePageRemoval(fullPageId, absEvictAddr, saveDirtyPage)) {
- loadedPages.remove(
- fullPageId.groupId(),
- fullPageId.effectivePageId()
- );
-
- return addr;
- }
- else
- failToPrepare++;
-
- prevAddr = addr;
- }
-
+ public IgniteOutOfMemoryException oomException(String reason) {
DataRegionConfiguration dataRegionCfg = getDataRegionConfiguration();
- throw new IgniteOutOfMemoryException("Failed to find a page for eviction [segmentCapacity=" + cap +
+ return new IgniteOutOfMemoryException("Failed to find a page for eviction (" + reason + ") [" +
+ "segmentCapacity=" + loadedPages.capacity() +
", loaded=" + loadedPages.size() +
", maxDirtyPages=" + maxDirtyPages +
", dirtyPages=" + dirtyPagesCntr +
- ", cpPages=" + (checkpointPages == null ? 0 : checkpointPages.size()) +
- ", pinnedInSegment=" + pinnedCnt +
- ", failedToPrepare=" + failToPrepare +
+ ", cpPages=" + (checkpointPages() == null ? 0 : checkpointPages().size()) +
+ ", pinned=" + acquiredPages() +
']' + U.nl() + "Out of memory in data region [" +
- (dataRegionCfg == null ? "NULL" : (
- "name=" + dataRegionCfg.getName() +
- ", initSize=" + U.readableSize(dataRegionCfg.getInitialSize(), false) +
- ", maxSize=" + U.readableSize(dataRegionCfg.getMaxSize(), false) +
- ", persistenceEnabled=" + dataRegionCfg.isPersistenceEnabled()
- )) +
- "]" +
- " Try the following:" + U.nl() +
+ "name=" + dataRegionCfg.getName() +
+ ", initSize=" + U.readableSize(dataRegionCfg.getInitialSize(), false) +
+ ", maxSize=" + U.readableSize(dataRegionCfg.getMaxSize(), false) +
+ ", persistenceEnabled=" + dataRegionCfg.isPersistenceEnabled() + "] Try the following:" + U.nl() +
" ^-- Increase maximum off-heap memory size (DataRegionConfiguration.maxSize)" + U.nl() +
- " ^-- Enable Ignite persistence (DataRegionConfiguration.persistenceEnabled)" + U.nl() +
" ^-- Enable eviction or expiration policies"
);
}
@@ -2509,16 +2351,36 @@ public class PageMemoryImpl implements PageMemoryEx {
* @param relPtr Relative pointer.
* @return Absolute pointer.
*/
- private long absolute(long relPtr) {
+ public long absolute(long relPtr) {
return pool.absolute(relPtr);
}
/**
+ * Delegate to the corresponding page pool.
+ *
+ * @param pageIdx Page index.
+ * @return Relative pointer.
+ */
+ public long relative(long pageIdx) {
+ return pool.relative(pageIdx);
+ }
+
+ /**
+ * Delegate to the corresponding page pool.
+ *
+ * @param relPtr Relative pointer.
+ * @return Page index in the pool.
+ */
+ public long pageIndex(long relPtr) {
+ return pool.pageIndex(relPtr);
+ }
+
+ /**
* @param grpId Cache group ID.
* @param partId Partition ID.
* @return Partition generation. Growing, 1-based partition version. Changed
*/
- private int partGeneration(int grpId, int partId) {
+ public int partGeneration(int grpId, int partId) {
assert getReadHoldCount() > 0 || getWriteHoldCount() > 0;
Integer tag = partGenerationMap.get(new GroupPartitionId(grpId, partId));
@@ -2529,6 +2391,27 @@ public class PageMemoryImpl implements PageMemoryEx {
}
/**
+ * Gets loaded pages map.
+ */
+ public LoadedPagesMap loadedPages() {
+ return loadedPages;
+ }
+
+ /**
+ * Gets checkpoint pages.
+ */
+ public CheckpointPages checkpointPages() {
+ return checkpointPages;
+ }
+
+ /**
+ * Gets page pool.
+ */
+ public PagePool pool() {
+ return pool;
+ }
+
+ /**
* Increments partition generation due to partition invalidation (e.g. partition was rebalanced to other node
* and evicted).
*
@@ -2571,17 +2454,6 @@ public class PageMemoryImpl implements PageMemoryEx {
}
/**
- * Gets an estimate for the amount of memory required to store the given number of page IDs
- * in a segment table.
- *
- * @param pages Number of pages to store.
- * @return Memory size estimate.
- */
- private static long requiredSegmentTableMemory(int pages) {
- return FullPageIdTable.requiredMemory(pages) + 8;
- }
-
- /**
* @param ptr Pointer to update.
* @param delta Delta.
*/
@@ -2687,6 +2559,8 @@ public class PageMemoryImpl implements PageMemoryEx {
GridUnsafe.setMemory(absPtr + PAGE_OVERHEAD, pageSize, (byte)0);
+ seg.pageReplacementPolicy.onRemove(relPtr);
+
seg.pool.releaseFreePage(relPtr);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PagePool.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PagePool.java
index 5e4c80b..e399c66 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PagePool.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PagePool.java
@@ -232,6 +232,14 @@ public class PagePool {
}
/**
+ * @param relPtr Relative pointer.
+ * @return Page index in the pool.
+ */
+ long pageIndex(long relPtr) {
+ return relPtr & ~SEGMENT_INDEX_MASK;
+ }
+
+ /**
* @return Max number of pages in the pool.
*/
public int pages() {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageReplacementPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageReplacementPolicy.java
new file mode 100644
index 0000000..0d9881b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageReplacementPolicy.java
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.ignite.IgniteCheckedException;
+
+/**
+ * Abstract page replacement policy.
+ */
+public abstract class PageReplacementPolicy {
+ /** Page memory segment. */
+ protected final PageMemoryImpl.Segment seg;
+
+ /**
+ * @param seg Page memory segment.
+ */
+ protected PageReplacementPolicy(PageMemoryImpl.Segment seg) {
+ this.seg = seg;
+ }
+
+ /**
+ * Existing page touched.
+ *
+ * Note: This method can be invoked under segment write lock or segment read lock.
+ */
+ public void onHit(long relPtr) {
+ // No-op.
+ }
+
+ /**
+ * New page added.
+ *
+ * Note: This method always invoked under segment write lock.
+ */
+ public void onMiss(long relPtr) {
+ // No-op.
+ }
+
+ /**
+ * Page removed from the page memory.
+ */
+ public void onRemove(long relPtr) {
+ // No-op.
+ }
+
+ /**
+ * Finds page to replace.
+ *
+ * Note: This method always invoked under segment write lock.
+ *
+ * @return Relative pointer to page.
+ */
+ public abstract long replace() throws IgniteCheckedException;
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageReplacementPolicyFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageReplacementPolicyFactory.java
new file mode 100644
index 0000000..5808a5a
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageReplacementPolicyFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * Page replacement policy factory.
+ */
+public interface PageReplacementPolicyFactory {
+ /**
+ * Calculaete amount of memory required to service {@code pagesCnt} pages.
+ *
+ * @param pagesCnt Pages count.
+ */
+ public long requiredMemory(int pagesCnt);
+
+ /**
+ * Create page replacement policy.
+ *
+ * @param seg Page memory segment.
+ * @param ptr Pointer to memory region.
+ * @param pagesCnt Pages count.
+ */
+ public PageReplacementPolicy create(PageMemoryImpl.Segment seg, long ptr, int pagesCnt);
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/RandomLruPageReplacementPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/RandomLruPageReplacementPolicy.java
new file mode 100644
index 0000000..3ffc260
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/RandomLruPageReplacementPolicy.java
@@ -0,0 +1,247 @@
+/*
+ * 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.HashSet;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.PageReplacementMode;
+import org.apache.ignite.internal.pagemem.FullPageId;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListMetaIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionCountersIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIO;
+
+import static org.apache.ignite.internal.pagemem.PageIdAllocator.META_PAGE_ID;
+import static org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl.INVALID_REL_PTR;
+import static org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl.PAGE_OVERHEAD;
+
+/**
+ * Random-LRU page replacement policy implementation.
+ *
+ * @see PageReplacementMode#RANDOM_LRU
+ */
+public class RandomLruPageReplacementPolicy extends PageReplacementPolicy {
+ /** Number of random pages that will be picked for eviction. */
+ public static final int RANDOM_PAGES_EVICT_NUM = 5;
+
+ /** */
+ private static final double FULL_SCAN_THRESHOLD = 0.4;
+
+ /**
+ * @param seg Page memory segment.
+ */
+ protected RandomLruPageReplacementPolicy(PageMemoryImpl.Segment seg) {
+ super(seg);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long replace() throws IgniteCheckedException {
+ final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+ LoadedPagesMap loadedPages = seg.loadedPages();
+ PagePool pool = seg.pool();
+
+ final int cap = loadedPages.capacity();
+
+ // With big number of random picked pages we may fall into infinite loop, because
+ // every time the same page may be found.
+ Set<Long> ignored = null;
+
+ long relRmvAddr = INVALID_REL_PTR;
+
+ int iterations = 0;
+
+ while (true) {
+ long cleanAddr = INVALID_REL_PTR;
+ long cleanTs = Long.MAX_VALUE;
+ long dirtyAddr = INVALID_REL_PTR;
+ long dirtyTs = Long.MAX_VALUE;
+ long metaAddr = INVALID_REL_PTR;
+ long metaTs = Long.MAX_VALUE;
+
+ for (int i = 0; i < RANDOM_PAGES_EVICT_NUM; i++) {
+ ++iterations;
+
+ if (iterations > pool.pages() * FULL_SCAN_THRESHOLD)
+ break;
+
+ // We need to lookup for pages only in current segment for thread safety,
+ // so peeking random memory will lead to checking for found page segment.
+ // It's much faster to check available pages for segment right away.
+ ReplaceCandidate nearest = loadedPages.getNearestAt(rnd.nextInt(cap));
+
+ assert nearest != null && nearest.relativePointer() != INVALID_REL_PTR;
+
+ long rndAddr = nearest.relativePointer();
+
+ int partGen = nearest.generation();
+
+ final long absPageAddr = seg.absolute(rndAddr);
+
+ FullPageId fullId = PageHeader.fullPageId(absPageAddr);
+
+ // Check page mapping consistency.
+ assert fullId.equals(nearest.fullId()) : "Invalid page mapping [tableId=" + nearest.fullId() +
+ ", actual=" + fullId + ", nearest=" + nearest;
+
+ boolean outdated = partGen < seg.partGeneration(fullId.groupId(), PageIdUtils.partId(fullId.pageId()));
+
+ if (outdated)
+ return seg.refreshOutdatedPage(fullId.groupId(), fullId.pageId(), true);
+
+ boolean pinned = PageHeader.isAcquired(absPageAddr);
+
+ boolean skip = ignored != null && ignored.contains(rndAddr);
+
+ final boolean dirty = PageHeader.dirty(absPageAddr);
+
+ CheckpointPages checkpointPages = seg.checkpointPages();
+
+ if (relRmvAddr == rndAddr || pinned || skip ||
+ fullId.pageId() == META_PAGE_ID ||
+ (dirty && (checkpointPages == null || !checkpointPages.contains(fullId)))
+ ) {
+ i--;
+
+ continue;
+ }
+
+ final long pageTs = PageHeader.readTimestamp(absPageAddr);
+
+ final boolean storMeta = isStoreMetadataPage(absPageAddr);
+
+ if (pageTs < cleanTs && !dirty && !storMeta) {
+ cleanAddr = rndAddr;
+
+ cleanTs = pageTs;
+ }
+ else if (pageTs < dirtyTs && dirty && !storMeta) {
+ dirtyAddr = rndAddr;
+
+ dirtyTs = pageTs;
+ }
+ else if (pageTs < metaTs && storMeta) {
+ metaAddr = rndAddr;
+
+ metaTs = pageTs;
+ }
+
+ if (cleanAddr != INVALID_REL_PTR)
+ relRmvAddr = cleanAddr;
+ else if (dirtyAddr != INVALID_REL_PTR)
+ relRmvAddr = dirtyAddr;
+ else
+ relRmvAddr = metaAddr;
+ }
+
+ if (relRmvAddr == INVALID_REL_PTR)
+ return tryToFindSequentially(cap);
+
+ final long absRmvAddr = seg.absolute(relRmvAddr);
+
+ final FullPageId fullPageId = PageHeader.fullPageId(absRmvAddr);
+
+ if (!seg.tryToRemovePage(fullPageId, absRmvAddr)) {
+ if (iterations > 10) {
+ if (ignored == null)
+ ignored = new HashSet<>();
+
+ ignored.add(relRmvAddr);
+ }
+
+ if (iterations > seg.pool().pages() * FULL_SCAN_THRESHOLD)
+ return tryToFindSequentially(cap);
+
+ continue;
+ }
+
+ return relRmvAddr;
+ }
+ }
+
+ /**
+ * @param absPageAddr Absolute page address
+ * @return {@code True} if page is related to partition metadata, which is loaded in saveStoreMetadata().
+ */
+ private static boolean isStoreMetadataPage(long absPageAddr) {
+ try {
+ long dataAddr = absPageAddr + PAGE_OVERHEAD;
+
+ int type = PageIO.getType(dataAddr);
+ int ver = PageIO.getVersion(dataAddr);
+
+ PageIO io = PageIO.getPageIO(type, ver);
+
+ return io instanceof PagePartitionMetaIO
+ || io instanceof PagesListMetaIO
+ || io instanceof PagePartitionCountersIO;
+ }
+ catch (IgniteCheckedException ignored) {
+ return false;
+ }
+ }
+
+ /**
+ * Will scan all segment pages to find one to evict it.
+ *
+ * @param cap Capacity.
+ */
+ private long tryToFindSequentially(int cap) throws IgniteCheckedException {
+ assert seg.getWriteHoldCount() > 0;
+
+ long prevAddr = INVALID_REL_PTR;
+
+ LoadedPagesMap loadedPages = seg.loadedPages();
+
+ for (int i = 0; i < cap; i++) {
+ final ReplaceCandidate nearest = loadedPages.getNearestAt(i);
+
+ assert nearest != null && nearest.relativePointer() != INVALID_REL_PTR;
+
+ final long addr = nearest.relativePointer();
+
+ int partGen = nearest.generation();
+
+ final long absPageAddr = seg.absolute(addr);
+
+ FullPageId fullId = PageHeader.fullPageId(absPageAddr);
+
+ if (partGen < seg.partGeneration(fullId.groupId(), PageIdUtils.partId(fullId.pageId())))
+ return seg.refreshOutdatedPage(fullId.groupId(), fullId.pageId(), true);
+
+ boolean pinned = PageHeader.isAcquired(absPageAddr);
+
+ if (addr == prevAddr || pinned)
+ continue;
+
+ final long absEvictAddr = seg.absolute(addr);
+
+ final FullPageId fullPageId = PageHeader.fullPageId(absEvictAddr);
+
+ if (seg.tryToRemovePage(fullPageId, absEvictAddr))
+ return addr;
+
+ prevAddr = addr;
+ }
+
+ throw seg.oomException("no pages to replace");
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/RandomLruPageReplacementPolicyFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/RandomLruPageReplacementPolicyFactory.java
new file mode 100644
index 0000000..b2a9b3d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/RandomLruPageReplacementPolicyFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * {@link RandomLruPageReplacementPolicy} factory.
+ */
+public class RandomLruPageReplacementPolicyFactory implements PageReplacementPolicyFactory {
+ /** {@inheritDoc} */
+ @Override public long requiredMemory(int pagesCnt) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public PageReplacementPolicy create(PageMemoryImpl.Segment seg, long ptr, int pagesCnt) {
+ return new RandomLruPageReplacementPolicy(seg);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageList.java
new file mode 100644
index 0000000..725fd39
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageList.java
@@ -0,0 +1,364 @@
+/*
+ * 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 org.apache.ignite.configuration.PageReplacementMode;
+import org.apache.ignite.internal.util.GridUnsafe;
+
+/**
+ * Pages Segmented-LRU (SLRU) list implementation.
+ *
+ * @see PageReplacementMode#SEGMENTED_LRU
+ */
+public class SegmentedLruPageList {
+ /** Ratio to limit count of protected pages. */
+ private static final double PROTECTED_TO_TOTAL_PAGES_RATIO = 0.5;
+
+ /** Null page index. */
+ static final int NULL_IDX = -1;
+
+ /** Index of the head page of LRU list. */
+ private int headIdx = NULL_IDX;
+
+ /** Index of the tail page of LRU list. */
+ private int tailIdx = NULL_IDX;
+
+ /** Index of the tail page of probationary segment. */
+ private int probTailIdx = NULL_IDX;
+
+ /** Count of protected pages in the list. */
+ private int protectedPagesCnt;
+
+ /** Protected pages segment limit. */
+ private final int protectedPagesLimit;
+
+ /** Pointer to memory region to store links. */
+ private final long linksPtr;
+
+ /** Pointer to memory region to store protected flags. */
+ private final long flagsPtr;
+
+ /**
+ * @param totalPagesCnt Total pages count.
+ * @param memPtr Pointer to memory region.
+ */
+ public SegmentedLruPageList(int totalPagesCnt, long memPtr) {
+ linksPtr = memPtr;
+ flagsPtr = memPtr + (((long)totalPagesCnt) << 3);
+
+ GridUnsafe.setMemory(linksPtr, ((long)totalPagesCnt) << 3, (byte)0xFF);
+ GridUnsafe.setMemory(flagsPtr, (totalPagesCnt + 7) >> 3, (byte)0);
+
+ protectedPagesLimit = (int)(totalPagesCnt * PROTECTED_TO_TOTAL_PAGES_RATIO);
+ }
+
+ /**
+ * Remove page from the head of LRU list.
+ *
+ * @return Page index or {@code -1} if list is empty.
+ */
+ public synchronized int poll() {
+ int idx = headIdx;
+
+ if (idx != NULL_IDX)
+ remove(idx);
+
+ return idx;
+ }
+
+ /**
+ * Remove page from LRU list by page index.
+ *
+ * @param pageIdx Page index.
+ */
+ public synchronized void remove(int pageIdx) {
+ remove0(pageIdx, protectedPage(pageIdx));
+ }
+
+ /**
+ * @param pageIdx Page index.
+ * @param clearProtectedFlag Clear protected page flag.
+ */
+ private void remove0(int pageIdx, boolean clearProtectedFlag) {
+ assert pageIdx != NULL_IDX;
+
+ int prevIdx = prev(pageIdx);
+ int nextIdx = next(pageIdx);
+
+ if (pageIdx == probTailIdx)
+ probTailIdx = prevIdx;
+
+ if (prevIdx == NULL_IDX) {
+ assert headIdx == pageIdx : "Unexpected LRU page index [headIdx=" + headIdx + ", pageIdx=" + pageIdx + ']';
+
+ headIdx = nextIdx;
+ }
+ else
+ next(prevIdx, nextIdx);
+
+ if (nextIdx == NULL_IDX) {
+ assert tailIdx == pageIdx : "Unexpected LRU page index [tailIdx=" + tailIdx + ", pageIdx=" + pageIdx + ']';
+
+ tailIdx = prevIdx;
+ }
+ else
+ prev(nextIdx, prevIdx);
+
+ clearLinks(pageIdx);
+
+ if (clearProtectedFlag) {
+ protectedPagesCnt--;
+
+ protectedPage(pageIdx, false);
+ }
+ }
+
+ /**
+ * Add page to the tail of protected or probationary LRU list.
+ *
+ * @param pageIdx Page index.
+ * @param protectedPage Protected page flag.
+ */
+ public synchronized void addToTail(int pageIdx, boolean protectedPage) {
+ assert prev(pageIdx) == NULL_IDX : prev(pageIdx);
+ assert next(pageIdx) == NULL_IDX : next(pageIdx);
+
+ if (headIdx == NULL_IDX || tailIdx == NULL_IDX) {
+ // In case of empty list.
+ assert headIdx == NULL_IDX : headIdx;
+ assert tailIdx == NULL_IDX : tailIdx;
+ assert probTailIdx == NULL_IDX : probTailIdx;
+ assert protectedPagesCnt == 0 : protectedPagesCnt;
+
+ headIdx = pageIdx;
+ tailIdx = pageIdx;
+
+ if (protectedPage) {
+ protectedPagesCnt = 1;
+
+ protectedPage(pageIdx, true);
+ }
+ else
+ probTailIdx = pageIdx;
+
+ return;
+ }
+
+ if (protectedPage) {
+ // Protected page - insert to the list tail.
+ assert next(tailIdx) == NULL_IDX : "Unexpected LRU page index [pageIdx=" + pageIdx +
+ ", tailIdx=" + tailIdx + ", nextLruIdx=" + next(tailIdx) + ']';
+
+ link(tailIdx, pageIdx);
+
+ tailIdx = pageIdx;
+
+ protectedPage(pageIdx, true);
+
+ // Move one page from protected segment to probationary segment if there are too many protected pages.
+ if (protectedPagesCnt >= protectedPagesLimit) {
+ probTailIdx = probTailIdx != NULL_IDX ? next(probTailIdx) : headIdx;
+
+ assert probTailIdx != NULL_IDX;
+
+ protectedPage(probTailIdx, false);
+ }
+ else
+ protectedPagesCnt++;
+ }
+ else {
+ if (probTailIdx == NULL_IDX) {
+ // First page in the probationary list - insert to the head.
+ assert prev(headIdx) == NULL_IDX : "Unexpected LRU page index [pageIdx=" + pageIdx +
+ ", headIdx=" + headIdx + ", prevLruIdx=" + prev(headIdx) + ']';
+
+ link(pageIdx, headIdx);
+
+ headIdx = pageIdx;
+ }
+ else {
+ int protectedIdx = next(probTailIdx);
+
+ link(probTailIdx, pageIdx);
+
+ if (protectedIdx == NULL_IDX) {
+ // There are no protected pages in the list.
+ assert probTailIdx == tailIdx :
+ "Unexpected LRU page index [probTailIdx=" + probTailIdx + ", tailIdx=" + tailIdx + ']';
+
+ tailIdx = pageIdx;
+ }
+ else {
+ // Link with last protected page.
+ link(pageIdx, protectedIdx);
+ }
+ }
+
+ probTailIdx = pageIdx;
+ }
+ }
+
+ /**
+ * Move page to the tail of protected LRU list.
+ *
+ * @param pageIdx Page index.
+ */
+ public synchronized void moveToTail(int pageIdx) {
+ if (tailIdx == pageIdx)
+ return;
+
+ remove0(pageIdx, false);
+
+ if (protectedPage(pageIdx)) {
+ link(tailIdx, pageIdx);
+
+ tailIdx = pageIdx;
+ }
+ else
+ addToTail(pageIdx, true);
+ }
+
+ /**
+ * Link two pages.
+ *
+ * @param prevIdx Previous page index.
+ * @param nextIdx Next page index.
+ */
+ private void link(int prevIdx, int nextIdx) {
+ prev(nextIdx, prevIdx);
+ next(prevIdx, nextIdx);
+ }
+
+ /**
+ * Clear page links.
+ *
+ * @param pageIdx Page index.
+ */
+ private void clearLinks(int pageIdx) {
+ GridUnsafe.putLong(linksPtr + (((long)pageIdx) << 3), -1L);
+ }
+
+ /**
+ * Gets link to the previous page in the list.
+ *
+ * @param pageIdx Page index.
+ */
+ int prev(int pageIdx) {
+ return GridUnsafe.getInt(linksPtr + (((long)pageIdx) << 3));
+ }
+
+ /**
+ * Gets link to the next page in the list.
+ *
+ * @param pageIdx Page index.
+ */
+ int next(int pageIdx) {
+ return GridUnsafe.getInt(linksPtr + (((long)pageIdx) << 3) + 4);
+ }
+
+ /**
+ * Gets protected page flag.
+ *
+ * @param pageIdx Page index.
+ */
+ boolean protectedPage(int pageIdx) {
+ long flags = GridUnsafe.getLong(flagsPtr + ((pageIdx >> 3) & (~7)));
+
+ return (flags & (1L << pageIdx)) != 0L;
+ }
+
+ /**
+ * Sets link to the previous page in the list.
+ *
+ * @param pageIdx Page index.
+ * @param prevIdx Previous page index.
+ */
+ private void prev(int pageIdx, int prevIdx) {
+ GridUnsafe.putInt(linksPtr + (((long)pageIdx) << 3), prevIdx);
+ }
+
+ /**
+ * Sets link to the next page in the list.
+ *
+ * @param pageIdx Page index.
+ * @param nextIdx Next page index.
+ */
+ private void next(int pageIdx, int nextIdx) {
+ GridUnsafe.putInt(linksPtr + (((long)pageIdx) << 3) + 4, nextIdx);
+ }
+
+ /**
+ * Sets protected page flag.
+ *
+ * @param pageIdx Page index.
+ * @param protectedPage Protected page flag.
+ */
+ private void protectedPage(int pageIdx, boolean protectedPage) {
+ long ptr = flagsPtr + ((pageIdx >> 3) & (~7));
+
+ if (protectedPage)
+ GridUnsafe.putLong(ptr, GridUnsafe.getLong(ptr) | (1L << pageIdx));
+ else
+ GridUnsafe.putLong(ptr, GridUnsafe.getLong(ptr) & ~(1L << pageIdx));
+ }
+
+ /**
+ * Gets the index of the head page of LRU list.
+ */
+ synchronized int headIdx() {
+ return headIdx;
+ }
+
+ /**
+ * Gets the indexof the tail page of probationary segment.
+ */
+ synchronized int probTailIdx() {
+ return probTailIdx;
+ }
+
+ /**
+ * Gets the index of the tail page of LRU list.
+ */
+ synchronized int tailIdx() {
+ return tailIdx;
+ }
+
+ /**
+ * Gets protected pages count.
+ */
+ synchronized int protectedPagesCount() {
+ return protectedPagesCnt;
+ }
+
+ /**
+ * Gets protected pages limit.
+ */
+ int protectedPagesLimit() {
+ return protectedPagesLimit;
+ }
+
+ /**
+ * Memory required to service {@code pagesCnt} pages.
+ *
+ * @param pagesCnt Pages count.
+ */
+ public static long requiredMemory(int pagesCnt) {
+ return pagesCnt * 8 /* links = 2 ints per page */ +
+ ((pagesCnt + 63) / 8) & (~7L) /* protected flags = 1 bit per page + 8 byte align */;
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageReplacementPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageReplacementPolicy.java
new file mode 100644
index 0000000..f03fa0e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageReplacementPolicy.java
@@ -0,0 +1,102 @@
+/*
+ * 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 org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.PageReplacementMode;
+import org.apache.ignite.internal.pagemem.FullPageId;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+
+import static org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl.INVALID_REL_PTR;
+import static org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl.OUTDATED_REL_PTR;
+
+/**
+ * Segmented-LRU page replacement policy implementation.
+ *
+ * @see PageReplacementMode#SEGMENTED_LRU
+ */
+public class SegmentedLruPageReplacementPolicy extends PageReplacementPolicy {
+ /** LRU list. */
+ private final SegmentedLruPageList lruList;
+
+ /**
+ * @param seg Page memory segment.
+ */
+ protected SegmentedLruPageReplacementPolicy(PageMemoryImpl.Segment seg, long ptr, int pagesCnt) {
+ super(seg);
+
+ lruList = new SegmentedLruPageList(pagesCnt, ptr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onHit(long relPtr) {
+ int pageIdx = (int)seg.pageIndex(relPtr);
+
+ lruList.moveToTail(pageIdx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onMiss(long relPtr) {
+ int pageIdx = (int)seg.pageIndex(relPtr);
+
+ lruList.addToTail(pageIdx, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onRemove(long relPtr) {
+ int pageIdx = (int)seg.pageIndex(relPtr);
+
+ lruList.remove(pageIdx);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long replace() throws IgniteCheckedException {
+ LoadedPagesMap loadedPages = seg.loadedPages();
+
+ for (int i = 0; i < loadedPages.size(); i++) {
+ int pageIdx = lruList.poll();
+
+ long relPtr = seg.relative(pageIdx);
+ long absPtr = seg.absolute(relPtr);
+
+ FullPageId fullId = PageHeader.fullPageId(absPtr);
+
+ // Check loaded pages map for outdated page.
+ relPtr = loadedPages.get(
+ fullId.groupId(),
+ fullId.effectivePageId(),
+ seg.partGeneration(fullId.groupId(), PageIdUtils.partId(fullId.pageId())),
+ INVALID_REL_PTR,
+ OUTDATED_REL_PTR
+ );
+
+ assert relPtr != INVALID_REL_PTR;
+
+ if (relPtr == OUTDATED_REL_PTR)
+ return seg.refreshOutdatedPage(fullId.groupId(), fullId.pageId(), true);
+
+ if (seg.tryToRemovePage(fullId, absPtr))
+ return relPtr;
+
+ // Return page to the LRU list.
+ lruList.addToTail(pageIdx, true);
+ }
+
+ throw seg.oomException("no pages to replace");
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageReplacementPolicyFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageReplacementPolicyFactory.java
new file mode 100644
index 0000000..090c1eb
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageReplacementPolicyFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * {@link SegmentedLruPageReplacementPolicy} factory.
+ */
+public class SegmentedLruPageReplacementPolicyFactory implements PageReplacementPolicyFactory {
+ /** {@inheritDoc} */
+ @Override public long requiredMemory(int pagesCnt) {
+ return SegmentedLruPageList.requiredMemory(pagesCnt);
+ }
+
+ /** {@inheritDoc} */
+ @Override public PageReplacementPolicy create(PageMemoryImpl.Segment seg, long ptr, int pagesCnt) {
+ return new SegmentedLruPageReplacementPolicy(seg, ptr, pagesCnt);
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreePageMemoryImplTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreePageMemoryImplTest.java
index 3e73281..33ae0cf 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreePageMemoryImplTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreePageMemoryImplTest.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.cache.persistence.pagemem;
import java.util.Collections;
import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.managers.encryption.GridEncryptionManager;
import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
@@ -66,6 +67,7 @@ public class BPlusTreePageMemoryImplTest extends BPlusTreeSelfTest {
cfg.setEncryptionSpi(new NoopEncryptionSpi());
cfg.setMetricExporterSpi(new NoopMetricExporterSpi());
cfg.setSystemViewExporterSpi(new JmxSystemViewExporterSpi());
+ cfg.setDataStorageConfiguration(new DataStorageConfiguration());
GridTestKernalContext cctx = new GridTestKernalContext(log, cfg);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreeReuseListPageMemoryImplTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreeReuseListPageMemoryImplTest.java
index 456c9d6..3c37874 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreeReuseListPageMemoryImplTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/BPlusTreeReuseListPageMemoryImplTest.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.cache.persistence.pagemem;
import java.util.Collections;
import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.managers.encryption.GridEncryptionManager;
import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
@@ -65,6 +66,7 @@ public class BPlusTreeReuseListPageMemoryImplTest extends BPlusTreeReuseSelfTest
cfg.setEncryptionSpi(new NoopEncryptionSpi());
cfg.setMetricExporterSpi(new NoopMetricExporterSpi());
cfg.setSystemViewExporterSpi(new JmxSystemViewExporterSpi());
+ cfg.setDataStorageConfiguration(new DataStorageConfiguration());
GridTestKernalContext cctx = new GridTestKernalContext(log, cfg);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementFlagsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementFlagsTest.java
new file mode 100644
index 0000000..91c7671
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/ClockPageReplacementFlagsTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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 org.apache.ignite.internal.mem.DirectMemoryProvider;
+import org.apache.ignite.internal.mem.DirectMemoryRegion;
+import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ClockPageReplacementFlagsTest extends GridCommonAbstractTest {
+ /** Max pages count. */
+ private static final int MAX_PAGES_CNT = 1000;
+
+ /** Memory provider. */
+ private static DirectMemoryProvider provider;
+
+ /** Memory region. */
+ private static DirectMemoryRegion region;
+
+ /** Clock replacement algorithm implementation. */
+ ClockPageReplacementFlags clockFlags;
+
+ /** */
+ @BeforeClass
+ public static void setUp() {
+ provider = new UnsafeMemoryProvider(log);
+ provider.initialize(new long[] {ClockPageReplacementFlags.requiredMemory(MAX_PAGES_CNT)});
+
+ region = provider.nextRegion();
+ }
+
+ /** */
+ @AfterClass
+ public static void tearDown() {
+ provider.shutdown(true);
+ }
+
+ /**
+ * Test setFlag() and getFlag() methods.
+ */
+ @Test
+ public void testSetGet() {
+ clockFlags = new ClockPageReplacementFlags(MAX_PAGES_CNT, region.address());
+
+ setRange(50, 100);
+
+ for (int i = 0; i < MAX_PAGES_CNT; i++)
+ assertEquals("Unexpectd value of " + i + " item", i >= 50 && i <= 100, clockFlags.getFlag(i));
+ }
+
+
+ /**
+ * Test poll() method.
+ */
+ @Test
+ public void testPoll() {
+ clockFlags = new ClockPageReplacementFlags(MAX_PAGES_CNT, region.address());
+
+ setRange(2, 2);
+ setRange(4, 7);
+ setRange(9, 254);
+ setRange(256, 320);
+ setRange(322, 499);
+ setRange(501, MAX_PAGES_CNT - 1);
+
+ assertEquals(0, clockFlags.poll());
+ assertEquals(1, clockFlags.poll());
+ assertEquals(3, clockFlags.poll());
+ assertEquals(8, clockFlags.poll());
+ assertEquals(255, clockFlags.poll());
+ assertEquals(321, clockFlags.poll());
+ assertEquals(500, clockFlags.poll());
+
+ setRange(0, 1);
+
+ assertEquals(2, clockFlags.poll());
+ assertEquals(3, clockFlags.poll());
+
+ setRange(4, MAX_PAGES_CNT - 2);
+
+ assertEquals(MAX_PAGES_CNT - 1, clockFlags.poll());
+ assertEquals(0, clockFlags.poll());
+
+ for (int i = 0; i < MAX_PAGES_CNT; i++)
+ assertEquals("Unexpectd value of " + i + " item", false, clockFlags.getFlag(i));
+ }
+
+ /**
+ * Set flag for range of page indexes.
+ *
+ * @param fromIdx From index.
+ * @param toIdx To index.
+ */
+ private void setRange(int fromIdx, int toIdx) {
+ for (int i = fromIdx; i <= toIdx; i++)
+ clockFlags.setFlag(i);
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/IndexStoragePageMemoryImplTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/IndexStoragePageMemoryImplTest.java
index 19e67b4..fa89f05 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/IndexStoragePageMemoryImplTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/IndexStoragePageMemoryImplTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.cache.persistence.pagemem;
import java.io.File;
import java.util.Collections;
import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.managers.encryption.GridEncryptionManager;
import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
@@ -81,6 +82,7 @@ public class IndexStoragePageMemoryImplTest extends IndexStorageSelfTest {
cfg.setEncryptionSpi(new NoopEncryptionSpi());
cfg.setMetricExporterSpi(new NoopMetricExporterSpi());
cfg.setSystemViewExporterSpi(new JmxSystemViewExporterSpi());
+ cfg.setDataStorageConfiguration(new DataStorageConfiguration());
GridTestKernalContext cctx = new GridTestKernalContext(log, cfg);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImplNoLoadTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImplNoLoadTest.java
index 51e29ff..149086b 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImplNoLoadTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/PageMemoryImplNoLoadTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.cache.persistence.pagemem;
import java.io.File;
import java.util.Collections;
import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.managers.encryption.GridEncryptionManager;
import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
@@ -71,6 +72,7 @@ public class PageMemoryImplNoLoadTest extends PageMemoryNoLoadSelfTest {
cfg.setEncryptionSpi(new NoopEncryptionSpi());
cfg.setMetricExporterSpi(new NoopMetricExporterSpi());
cfg.setSystemViewExporterSpi(new JmxSystemViewExporterSpi());
+ cfg.setDataStorageConfiguration(new DataStorageConfiguration());
GridTestKernalContext cctx = new GridTestKernalContext(log, cfg);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageListTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageListTest.java
new file mode 100644
index 0000000..ac78088
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/SegmentedLruPageListTest.java
@@ -0,0 +1,366 @@
+/*
+ * 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 org.apache.ignite.internal.mem.DirectMemoryProvider;
+import org.apache.ignite.internal.mem.DirectMemoryRegion;
+import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import static org.apache.ignite.internal.processors.cache.persistence.pagemem.SegmentedLruPageList.NULL_IDX;
+
+/**
+ * Test Segmented-LRU list implementation.
+ */
+public class SegmentedLruPageListTest extends GridCommonAbstractTest {
+ /** Max pages count. */
+ private static final int MAX_PAGES_CNT = 20;
+
+ /** Memory provider. */
+ private static DirectMemoryProvider provider;
+
+ /** Memory region. */
+ private static DirectMemoryRegion region;
+
+ /** LRU list. */
+ SegmentedLruPageList lru;
+
+ /** Test watcher. */
+ @Rule public TestRule testWatcher = new TestWatcher() {
+ @Override protected void failed(Throwable e, Description description) {
+ dump();
+ }
+ };
+
+ /** */
+ @BeforeClass
+ public static void setUp() {
+ provider = new UnsafeMemoryProvider(log);
+ provider.initialize(new long[] {SegmentedLruPageList.requiredMemory(MAX_PAGES_CNT)});
+
+ region = provider.nextRegion();
+ }
+
+ /** */
+ @AfterClass
+ public static void tearDown() {
+ provider.shutdown(true);
+ }
+
+ /** */
+ @Test
+ public void testAdd() {
+ // Check start with probationary page.
+ lru = new SegmentedLruPageList(MAX_PAGES_CNT, region.address());
+
+ addToTail(0, false);
+ assertProbationarySegment(0);
+ assertProtectedSegment();
+
+ addToTail(1, true);
+ assertProbationarySegment(0);
+ assertProtectedSegment(1);
+
+ addToTail(2, false);
+ assertProbationarySegment(0, 2);
+ assertProtectedSegment(1);
+
+ addToTail(3, true);
+ assertProbationarySegment(0, 2);
+ assertProtectedSegment(1, 3);
+
+ // Check start with protected page.
+ lru = new SegmentedLruPageList(MAX_PAGES_CNT, region.address());
+
+ addToTail(0, true);
+ assertProbationarySegment();
+ assertProtectedSegment(0);
+
+ addToTail(1, false);
+ assertProbationarySegment(1);
+ assertProtectedSegment(0);
+
+ addToTail(2, true);
+ assertProbationarySegment(1);
+ assertProtectedSegment(0, 2);
+
+ addToTail(3, false);
+ assertProbationarySegment(1, 3);
+ assertProtectedSegment(0, 2);
+ }
+
+ /** */
+ @Test
+ public void testRemove() {
+ lru = new SegmentedLruPageList(MAX_PAGES_CNT, region.address());
+
+ addToTail(0, false);
+ addToTail(1, true);
+ addToTail(2, false);
+ addToTail(3, true);
+ addToTail(4, false);
+ addToTail(5, true);
+
+ remove(0); // Head.
+ assertProbationarySegment(2, 4);
+ assertProtectedSegment(1, 3, 5);
+
+ remove(5); // Tail.
+ assertProbationarySegment(2, 4);
+ assertProtectedSegment(1, 3);
+
+ remove(1); // Protected segment head.
+ assertProbationarySegment(2, 4);
+ assertProtectedSegment(3);
+
+ remove(4); // Probationary segment tail.
+ assertProbationarySegment(2);
+ assertProtectedSegment(3);
+
+ remove(2); // Last probationary segment item.
+ assertProbationarySegment();
+ assertProtectedSegment(3);
+
+ remove(3); // Last potected segment and LRU item.
+ assertProbationarySegment();
+ assertProtectedSegment();
+
+ addToTail(2, false);
+ addToTail(3, true);
+
+ remove(3); // Last protected segment item.
+ assertProbationarySegment(2);
+ assertProtectedSegment();
+
+ remove(2); // Last probationary segment and LRU item.
+ assertProbationarySegment();
+ assertProtectedSegment();
+ }
+
+ /** */
+ @Test
+ public void testPoll() {
+ lru = new SegmentedLruPageList(MAX_PAGES_CNT, region.address());
+
+ assertEquals(NULL_IDX, poll());
+
+ addToTail(0, false);
+ addToTail(1, true);
+ addToTail(2, false);
+ addToTail(3, true);
+
+ assertEquals(0, poll());
+ assertEquals(2, poll());
+ assertEquals(1, poll());
+ assertEquals(3, poll());
+ assertEquals(NULL_IDX, poll());
+ }
+
+ /** */
+ @Test
+ public void testMoveToTail() {
+ lru = new SegmentedLruPageList(MAX_PAGES_CNT, region.address());
+
+ addToTail(0, false);
+ addToTail(1, true);
+ addToTail(2, false);
+ addToTail(3, true);
+
+ moveToTail(3);
+ assertProbationarySegment(0, 2);
+ assertProtectedSegment(1, 3);
+
+ moveToTail(2);
+ assertProbationarySegment(0);
+ assertProtectedSegment(1, 3, 2);
+
+ moveToTail(1);
+ assertProbationarySegment(0);
+ assertProtectedSegment(3, 2, 1);
+
+ moveToTail(0);
+ assertProbationarySegment();
+ assertProtectedSegment(3, 2, 1, 0);
+ }
+
+ /** */
+ @Test
+ public void testProtectedToProbationaryMigration() {
+ lru = new SegmentedLruPageList(6, region.address());
+
+ assertEquals(3, lru.protectedPagesLimit());
+
+ addToTail(0, true);
+ addToTail(1, true);
+ addToTail(2, true);
+
+ assertProbationarySegment();
+ assertProtectedSegment(0, 1, 2);
+
+ addToTail(3, true);
+ assertProbationarySegment(0);
+ assertProtectedSegment(1, 2, 3);
+
+ addToTail(4, true);
+ assertProbationarySegment(0, 1);
+ assertProtectedSegment(2, 3, 4);
+ }
+
+ /** */
+ private void addToTail(int pageIdx, boolean protectedPage) {
+ lru.addToTail(pageIdx, protectedPage);
+
+ checkInvariants();
+ }
+
+ /** */
+ private void remove(int pageIdx) {
+ lru.remove(pageIdx);
+
+ checkInvariants();
+ }
+
+ /** */
+ private int poll() {
+ int idx = lru.poll();
+
+ checkInvariants();
+
+ return idx;
+ }
+
+ /** */
+ private void moveToTail(int pageIdx) {
+ lru.moveToTail(pageIdx);
+
+ checkInvariants();
+ }
+
+ /** */
+ private void assertProbationarySegment(int... pageIdxs) {
+ assertTrue((lru.probTailIdx() != NULL_IDX) ^ F.isEmpty(pageIdxs));
+
+ int curIdx = lru.headIdx();
+
+ for (int pageIdx : pageIdxs) {
+ assertEquals(pageIdx, curIdx);
+
+ curIdx = (curIdx == lru.probTailIdx()) ? NULL_IDX : lru.next(curIdx);
+ }
+
+ if (!F.isEmpty(pageIdxs))
+ assertTrue(curIdx == NULL_IDX);
+ }
+
+ /** */
+ private void assertProtectedSegment(int... pageIdxs) {
+ int curIdx = lru.headIdx();
+
+ if (lru.probTailIdx() != NULL_IDX)
+ curIdx = lru.next(lru.probTailIdx());
+
+ assertTrue((curIdx != NULL_IDX) ^ F.isEmpty(pageIdxs));
+
+ for (int pageIdx : pageIdxs) {
+ assertEquals(pageIdx, curIdx);
+
+ curIdx = lru.next(curIdx);
+ }
+
+ assertEquals(pageIdxs.length, lru.protectedPagesCount());
+ }
+
+ /**
+ * Check LRU list invariants.
+ */
+ private void checkInvariants() {
+ int limit = MAX_PAGES_CNT + 1;
+
+ int curIdx = lru.headIdx();
+ int protectedCnt = 0;
+ boolean protectedPage = (lru.probTailIdx() == NULL_IDX);
+
+ if (lru.headIdx() == NULL_IDX || lru.tailIdx() == NULL_IDX) {
+ assertEquals(NULL_IDX, lru.headIdx());
+ assertEquals(NULL_IDX, lru.tailIdx());
+ assertEquals(NULL_IDX, lru.probTailIdx());
+ assertEquals(0, lru.protectedPagesCount());
+ }
+
+ while (curIdx != NULL_IDX && limit-- > 0) {
+ int prev = lru.prev(curIdx);
+ int next = lru.next(curIdx);
+
+ if (prev == NULL_IDX)
+ assertEquals(lru.headIdx(), curIdx);
+ else
+ assertEquals(curIdx, lru.next(prev));
+
+ if (next == NULL_IDX)
+ assertEquals(lru.tailIdx(), curIdx);
+ else
+ assertEquals(curIdx, lru.prev(next));
+
+ assertEquals(protectedPage, lru.protectedPage(curIdx));
+
+ if (protectedPage)
+ protectedCnt++;
+
+ if (curIdx == lru.probTailIdx())
+ protectedPage = true;
+
+ curIdx = next;
+ }
+
+ assertTrue(limit > 0);
+
+ assertEquals(protectedCnt, lru.protectedPagesCount());
+ assertTrue(protectedCnt <= lru.protectedPagesLimit());
+ }
+
+ /**
+ * Dump LRU list content.
+ */
+ private void dump() {
+ int limit = MAX_PAGES_CNT;
+
+ log.info(String.format("LRU list dump [headPtr=%d, probTailPtr=%d, tailPtr=%d, protectedCnt=%d]",
+ lru.headIdx(), lru.probTailIdx(), lru.tailIdx(), lru.protectedPagesCount()));
+
+ int curIdx = lru.headIdx();
+
+ while (curIdx != NULL_IDX && limit-- > 0) {
+ log.info(String.format(" Page %d [prev=%d, next=%d, protected=%b]%s", curIdx,
+ lru.prev(curIdx), lru.next(curIdx),
+ lru.protectedPage(curIdx), curIdx == lru.probTailIdx() ? " <- probationary list tail" : ""));
+
+ curIdx = lru.next(curIdx);
+ }
+
+ if (limit == 0)
+ log.info("...");
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
index 2fcfbe0..6331cae 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
@@ -80,7 +80,9 @@ import org.apache.ignite.internal.processors.cache.SetTxTimeoutOnPartitionMapExc
import org.apache.ignite.internal.processors.cache.distributed.IgniteRejectConnectOnNodeStopTest;
import org.apache.ignite.internal.processors.cache.distributed.dht.topology.EvictPartitionInLogTest;
import org.apache.ignite.internal.processors.cache.persistence.defragmentation.LinkMapTest;
+import org.apache.ignite.internal.processors.cache.persistence.pagemem.ClockPageReplacementFlagsTest;
import org.apache.ignite.internal.processors.cache.persistence.pagemem.PagePoolTest;
+import org.apache.ignite.internal.processors.cache.persistence.pagemem.SegmentedLruPageListTest;
import org.apache.ignite.internal.processors.cache.query.continuous.DiscoveryDataDeserializationFailureHanderTest;
import org.apache.ignite.internal.processors.cache.transactions.AtomicOperationsInTxTest;
import org.apache.ignite.internal.processors.cache.transactions.TransactionIntegrityWithSystemWorkerDeathTest;
@@ -222,6 +224,8 @@ import org.junit.runners.Suite;
// Basic DB data structures.
PagePoolTest.class,
+ SegmentedLruPageListTest.class,
+ ClockPageReplacementFlagsTest.class,
BPlusTreeSelfTest.class,
BPlusTreeFakeReuseSelfTest.class,
BPlusTreeReuseSelfTest.class,
diff --git a/modules/yardstick/config/benchmark-cache-pagereplacements.properties b/modules/yardstick/config/benchmark-cache-pagereplacements.properties
new file mode 100644
index 0000000..47f7497
--- /dev/null
+++ b/modules/yardstick/config/benchmark-cache-pagereplacements.properties
@@ -0,0 +1,124 @@
+
+# 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.
+
+now0=`date +'%H%M%S'`
+
+# JVM options.
+JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
+
+# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
+JVM_OPTS=${JVM_OPTS}" \
+-Xloggc:./gc${now0}.log \
+-XX:+PrintGCDetails \
+-verbose:gc \
+-XX:+UseG1GC \
+-XX:+PrintGCDateStamps \
+-DIGNITE_CHECKPOINT_TRIGGER_ARCHIVE_SIZE_PERCENTAGE=100 \
+"
+
+DRIVER_JVM_OPTS=${DRIVER_JVM_OPTS}"-Xmx1024m"
+
+SERVER_JVM_OPTS=${SERVER_JVM_OPTS}"-Xms8g -Xmx8g"
+
+#Ignite version
+ver="RELEASE-"
+
+# List of default probes.
+# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
+BENCHMARK_DEFAULT_PROBES=ThroughputLatencyProbe,PercentileProbe,DStatProbe
+
+# Packages where the specified benchmark is searched by reflection mechanism.
+BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
+
+# Flag which indicates to restart the servers before every benchmark execution.
+RESTART_SERVERS=true
+
+# Probe point writer class name.
+# BENCHMARK_WRITER=
+
+# The benchmark is applicable only for 1 server and 1 driver
+SERVER_HOSTS=127.0.0.1
+DRIVER_HOSTS=127.0.0.1
+
+# Remote username.
+# REMOTE_USER=
+
+# Number of nodes, used to wait for the specified number of nodes to start.
+nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
+
+# Backups count.
+b=0
+
+# Warmup.
+w=30
+
+# Duration.
+d=200
+
+# Threads count.
+t=4
+
+# Sync mode.
+sm=PRIMARY_SYNC
+
+# Jobs.
+j=10
+
+# Parameters that should be the same across all the benchmarks launches.
+commonParams="\
+-cfg ${SCRIPT_DIR}/../config/ignite-localhost-pagereplacement-config.xml -nn ${nodesNum} -sn IgniteNode \
+-b ${b} -w ${w} -d ${d} -t ${t} -sm ${sm} -cwd -cl\
+"
+
+# With page replacement.
+repl="-DREPLACE_RATIO=2.0"
+
+# Without page replacement.
+norepl="-DREPLACE_RATIO=0.5"
+
+# Page replacement modes.
+rlru="-SIGNITE_PAGE_REPLACEMENT_MODE=RANDOM_LRU"
+slru="-SIGNITE_PAGE_REPLACEMENT_MODE=SEGMENTED_LRU"
+clck="-SIGNITE_PAGE_REPLACEMENT_MODE=CLOCK"
+
+# Benchmark operations.
+putBenchmark="-dn IgnitePutWithPageReplacementBenchmark"
+getBenchmark="-dn IgniteGetWithPageReplacementBenchmark"
+
+# Benchmark with background scans.
+bgScan="-DBACKGROUND_SCAN_INTERVAL=10000"
+
+# Run configuration which contains all benchmarks.
+CONFIGS="\
+${commonParams} ${norepl} ${rlru} ${putBenchmark} -ds ${ver}cache-put-no-pagereplacement-RLRU,\
+${commonParams} ${norepl} ${slru} ${putBenchmark} -ds ${ver}cache-put-no-pagereplacement-SLRU,\
+${commonParams} ${norepl} ${clck} ${putBenchmark} -ds ${ver}cache-put-no-pagereplacement-CLCK,\
+${commonParams} ${repl} ${rlru} ${putBenchmark} -ds ${ver}cache-put-pagereplacement-RLRU,\
+${commonParams} ${repl} ${slru} ${putBenchmark} -ds ${ver}cache-put-pagereplacement-SLRU,\
+${commonParams} ${repl} ${clck} ${putBenchmark} -ds ${ver}cache-put-pagereplacement-CLCK,\
+${commonParams} ${repl} ${rlru} ${putBenchmark} ${bgScan} -ds ${ver}cache-put-pagereplacement-RLRU-BG,\
+${commonParams} ${repl} ${slru} ${putBenchmark} ${bgScan} -ds ${ver}cache-put-pagereplacement-SLRU-BG,\
+${commonParams} ${repl} ${clck} ${putBenchmark} ${bgScan} -ds ${ver}cache-put-pagereplacement-CLCK-BG,\
+${commonParams} ${norepl} ${rlru} ${getBenchmark} -ds ${ver}cache-get-no-pagereplacement-RLRU,\
+${commonParams} ${norepl} ${slru} ${getBenchmark} -ds ${ver}cache-get-no-pagereplacement-SLRU,\
+${commonParams} ${norepl} ${clck} ${getBenchmark} -ds ${ver}cache-get-no-pagereplacement-CLCK,\
+${commonParams} ${repl} ${rlru} ${getBenchmark} -ds ${ver}cache-get-pagereplacement-RLRU,\
+${commonParams} ${repl} ${slru} ${getBenchmark} -ds ${ver}cache-get-pagereplacement-SLRU,\
+${commonParams} ${repl} ${clck} ${getBenchmark} -ds ${ver}cache-get-pagereplacement-CLCK,\
+${commonParams} ${repl} ${rlru} ${getBenchmark} ${bgScan} -ds ${ver}cache-get-pagereplacement-RLRU-BG,\
+${commonParams} ${repl} ${slru} ${getBenchmark} ${bgScan} -ds ${ver}cache-get-pagereplacement-SLRU-BG,\
+${commonParams} ${repl} ${clck} ${getBenchmark} ${bgScan} -ds ${ver}cache-get-pagereplacement-CLCK-BG,\
+"
diff --git a/modules/yardstick/config/benchmark-cache-pegereplacements.properties b/modules/yardstick/config/benchmark-cache-pegereplacements.properties
deleted file mode 100644
index 1da456c..0000000
--- a/modules/yardstick/config/benchmark-cache-pegereplacements.properties
+++ /dev/null
@@ -1,83 +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.
-
-now0=`date +'%H%M%S'`
-
-# JVM options.
-JVM_OPTS=${JVM_OPTS}" -DIGNITE_QUIET=false"
-
-# Uncomment to enable concurrent garbage collection (GC) if you encounter long GC pauses.
-JVM_OPTS=${JVM_OPTS}" \
--Xloggc:./gc${now0}.log \
--XX:+PrintGCDetails \
--verbose:gc \
--XX:+UseG1GC \
--XX:+PrintGCDateStamps \
-"
-
-DRIVER_JVM_OPTS=${DRIVER_JVM_OPTS}"-Xmx1024m"
-
-SERVER_JVM_OPTS=${SERVER_JVM_OPTS}"-Xms8g -Xmx8g"
-
-#Ignite version
-ver="RELEASE-"
-
-# List of default probes.
-# Add DStatProbe or VmStatProbe if your OS supports it (e.g. if running on Linux).
-BENCHMARK_DEFAULT_PROBES=ThroughputLatencyProbe,PercentileProbe,DStatProbe
-
-# Packages where the specified benchmark is searched by reflection mechanism.
-BENCHMARK_PACKAGES=org.yardstickframework,org.apache.ignite.yardstick
-
-# Flag which indicates to restart the servers before every benchmark execution.
-RESTART_SERVERS=true
-
-# Probe point writer class name.
-# BENCHMARK_WRITER=
-
-# The benchmark is applicable only for 1 server and 1 driver
-SERVER_HOSTS=127.0.0.1
-DRIVER_HOSTS=127.0.0.1
-
-# Remote username.
-# REMOTE_USER=
-
-# Number of nodes, used to wait for the specified number of nodes to start.
-nodesNum=$((`echo ${SERVER_HOSTS} | tr ',' '\n' | wc -l` + `echo ${DRIVER_HOSTS} | tr ',' '\n' | wc -l`))
-
-# Backups count.
-b=0
-
-# Warmup.
-w=30
-
-# Duration.
-d=200
-
-# Threads count.
-t=4
-
-# Sync mode.
-sm=PRIMARY_SYNC
-
-# Jobs.
-j=10
-
-# Run configuration which contains all benchmarks.
-# Note that each benchmark is set to run for 300 seconds (5 min) with warm-up set to 60 seconds (1 minute).
-CONFIGS="\
--cfg ${SCRIPT_DIR}/../config/ignite-localhost-persistence-config.xml -nn ${nodesNum} -b ${b} -w ${w} -d ${d} -t ${t} -sm ${sm} -dn IgnitePutGetWithPageReplacements -sn IgniteNode -ds ${ver}cache-pagereplacement-r1-${b}-backup -cl,\
-"
diff --git a/modules/yardstick/config/ignite-localhost-pagereplacement-config.xml b/modules/yardstick/config/ignite-localhost-pagereplacement-config.xml
new file mode 100644
index 0000000..b83b859
--- /dev/null
+++ b/modules/yardstick/config/ignite-localhost-pagereplacement-config.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<!--
+ Ignite Spring configuration file to startup grid.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation=" http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd">
+ <import resource="ignite-base-config.xml"/>
+ <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration" parent="base-ignite.cfg">
+ <property name="localHost" value="127.0.0.1"/>
+
+ <property name="includeEventTypes">
+ <util:constant static-field="org.apache.ignite.events.EventType.EVT_PAGE_REPLACEMENT_STARTED"/>
+ </property>
+
+ <property name="discoverySpi">
+ <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+ <property name="ipFinder">
+ <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+ <property name="addresses">
+ <list>
+ <value>127.0.0.1:47500</value>
+ <value>127.0.0.1:47501</value>
+ <value>127.0.0.1:47502</value>
+ <value>127.0.0.1:47503</value>
+ <value>127.0.0.1:47504</value>
+ <value>127.0.0.1:47505</value>
+ <value>127.0.0.1:47506</value>
+ <value>127.0.0.1:47507</value>
+ <value>127.0.0.1:47508</value>
+ <value>127.0.0.1:47509</value>
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+ </property>
+
+ <property name="dataStorageConfiguration" >
+ <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
+ <property name="defaultDataRegionConfiguration">
+ <bean class="org.apache.ignite.configuration.DataRegionConfiguration">
+ <property name="maxSize" value="#{300 * 1024 * 1024}"/>
+ <property name="persistenceEnabled" value="true"/>
+ <property name="pageReplacementMode" value="#{systemProperties['IGNITE_PAGE_REPLACEMENT_MODE']}"/>
+ </bean>
+ </property>
+ </bean>
+ </property>
+
+ <property name="communicationSpi">
+ <bean class="org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi">
+ <property name="sharedMemoryPort" value="-1"/>
+ </bean>
+ </property>
+ </bean>
+</beans>
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
index 8a2e947..854800a 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
@@ -306,6 +306,11 @@ public class IgniteBenchmarkArguments {
description = "Allow add any dynamic parameters specific for some benchmarks")
private Map<String, String> params = new HashMap<>();
+ /** Additional system prorperties. */
+ @DynamicParameter(names = {"-S", "--sysProp"},
+ description = "Allow add additinal dynamic system properties to benchmarks")
+ private Map<String, String> sysProps = new HashMap<>();
+
/**
* @return {@code True} if need set {@link DataStorageConfiguration}.
*/
@@ -789,6 +794,24 @@ public class IgniteBenchmarkArguments {
return val != null ? Long.parseLong(val) : dflt;
}
+ /**
+ * @param name Parameter name.
+ * @param dflt Default value.
+ * @return value.
+ */
+ public double getDoubleParameter(String name, double dflt) {
+ String val = params.get(name);
+
+ return val != null ? Double.parseDouble(val) : dflt;
+ }
+
+ /**
+ * @return Additional dynamic system properties.
+ */
+ public Map<String, String> systemProperties() {
+ return sysProps;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return GridToStringBuilder.toString(IgniteBenchmarkArguments.class, this);
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
index 28467fe..591b76c 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
@@ -40,6 +40,7 @@ import org.apache.ignite.configuration.NearCacheConfiguration;
import org.apache.ignite.configuration.TransactionConfiguration;
import org.apache.ignite.configuration.WALMode;
import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi;
@@ -98,6 +99,11 @@ public class IgniteNode implements BenchmarkServer {
BenchmarkUtils.jcommander(cfg.commandLineArguments(), args, "<ignite-node>");
+ if (!F.isEmpty(args.systemProperties())) {
+ for (Map.Entry<String, String> entry : args.systemProperties().entrySet())
+ System.setProperty(entry.getKey(), entry.getValue());
+ }
+
if (args.clientNodesAfterId() >= 0 && cfg.memberId() > args.clientNodesAfterId())
clientMode = true;
@@ -107,7 +113,7 @@ public class IgniteNode implements BenchmarkServer {
assert c != null;
- if (args.cleanWorkDirectory())
+ if (args.cleanWorkDirectory() && !clientMode)
FileUtils.cleanDirectory(U.workDirectory(c.getWorkDirectory(), c.getIgniteHome()));
ApplicationContext appCtx = tup.get2();
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetWithPageReplacements.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteAbstractPageReplacementBenchmark.java
similarity index 62%
rename from modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetWithPageReplacements.java
rename to modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteAbstractPageReplacementBenchmark.java
index 2c6c972..9acc2b5 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetWithPageReplacements.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteAbstractPageReplacementBenchmark.java
@@ -21,9 +21,9 @@ import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
-import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteDataStreamer;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.ScanQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
@@ -42,17 +42,26 @@ import static org.apache.ignite.events.EventType.EVT_PAGE_REPLACEMENT_STARTED;
* after proceed to fill two times more data.
* Execute full scan.
*
- * On test phase fill data belonging to only 1/2 of dataregion capacity, calculated on setUp phase.
+ * On test phase process data belonging to dataregion capacity (calculated on setUp phase) * REPLACE_RATIO parameter.
*
* NOTE: EVT_PAGE_REPLACEMENT_STARTED event need to be enabled on server side.
*/
-public class IgnitePutGetWithPageReplacements extends IgniteCacheAbstractBenchmark<Integer, Object> {
+public abstract class IgniteAbstractPageReplacementBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
/** Cache name. */
private static final String CACHE_NAME = "CacheWithReplacement";
+ /** Scan-cache name. */
+ private static final String SCAN_CACHE_NAME = "ScanCache";
+
/** In mem reg capacity. */
private volatile int replCntr = Integer.MAX_VALUE / 2;
+ /** Thread to perform periodical background scans. */
+ private volatile Thread backgroundScanThread;
+
+ /** Key range for given data region capacity and REPLACE_RATIO parameter. */
+ private volatile int range;
+
/** {@inheritDoc} */
@Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
super.setUp(cfg);
@@ -100,7 +109,45 @@ public class IgnitePutGetWithPageReplacements extends IgniteCacheAbstractBenchma
}
}
- BenchmarkUtils.println("DataRegion fullfill complete. progress=" + progress + " replCntr=" + replCntr + ".");
+ BenchmarkUtils.println("Benchmark cache fullfill complete. progress=" + progress + " replCntr=" + replCntr + ".");
+
+ long backgroundScanInterval = args.getLongParameter("BACKGROUND_SCAN_INTERVAL", 0L);
+
+ if (backgroundScanInterval != 0) {
+ IgniteCache<Integer, Object> scanCache = ignite().getOrCreateCache(SCAN_CACHE_NAME);
+
+ try (IgniteDataStreamer<Integer, Object> streamer = ignite().dataStreamer(SCAN_CACHE_NAME)) {
+ for (int i = 0; i < replCntr; i++)
+ streamer.addData(i, new TestValue(i));
+ }
+
+ BenchmarkUtils.println("Scan cache fullfill complete. Size=" + replCntr + ".");
+
+ backgroundScanThread = new Thread(() -> {
+ long iteration = 0;
+
+ while (ignite().cluster().state().active()) {
+ iteration++;
+ long size = 0;
+
+ try (QueryCursor cursor = scanCache.query(new ScanQuery())) {
+ for (Object o : cursor)
+ size++;
+ }
+
+ BenchmarkUtils.println("Background scan iteration " + iteration + " finished, size=" + size);
+
+ try {
+ Thread.sleep(backgroundScanInterval);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+
+ });
+
+ backgroundScanThread.start();
+ }
int cacheSize = 0;
@@ -110,36 +157,34 @@ public class IgnitePutGetWithPageReplacements extends IgniteCacheAbstractBenchma
}
BenchmarkUtils.println("cache size=" + cacheSize);
- }
- /** */
- @Override protected IgniteCache<Integer, Object> cache() {
- return ignite().getOrCreateCache(CACHE_NAME);
+ range = (int)(args.getDoubleParameter("REPLACE_RATIO", 1.0) * replCntr);
}
/** {@inheritDoc} */
- @Override public boolean test(Map<Object, Object> map) throws Exception {
- int portion = 100;
-
- Map<Integer, TestValue> putMap = new HashMap<>(portion, 1.f);
+ @Override public void tearDown() throws Exception {
+ super.tearDown();
- ThreadLocalRandom rnd = ThreadLocalRandom.current();
-
- for (int i = 0; i < portion; i++) {
- int val = rnd.nextInt(replCntr / 2);
-
- putMap.put(val, new TestValue(val));
+ if (backgroundScanThread != null) {
+ backgroundScanThread.interrupt();
+ backgroundScanThread.join();
}
+ }
- cache().putAll(putMap);
+ /** */
+ @Override protected IgniteCache<Integer, Object> cache() {
+ return ignite().getOrCreateCache(CACHE_NAME);
+ }
- return true;
+ /** */
+ protected int nextKey() {
+ return nextRandom(range);
}
/**
* Class for test purpose.
*/
- private static class TestValue implements Serializable {
+ protected static class TestValue implements Serializable {
/** */
private int id;
@@ -150,7 +195,7 @@ public class IgnitePutGetWithPageReplacements extends IgniteCacheAbstractBenchma
/**
* @param id ID.
*/
- private TestValue(int id) {
+ protected TestValue(int id) {
this.id = id;
}
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetWithPageReplacementBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetWithPageReplacementBenchmark.java
new file mode 100644
index 0000000..d5adeb9
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetWithPageReplacementBenchmark.java
@@ -0,0 +1,41 @@
+/*
+ * 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.yardstick.cache;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Benchmark get operation with page replacement.
+ */
+public class IgniteGetWithPageReplacementBenchmark extends IgniteAbstractPageReplacementBenchmark {
+ /** {@inheritDoc} */
+ @Override public boolean test(Map<Object, Object> map) throws Exception {
+ int portion = 100;
+
+ Set<Integer> getSet = new HashSet<>(portion, 1.f);
+
+ for (int i = 0; i < portion; i++)
+ getSet.add(nextKey());
+
+ cache().getAll(getSet);
+
+ return true;
+ }
+}
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutWithPageReplacementBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutWithPageReplacementBenchmark.java
new file mode 100644
index 0000000..e891200
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutWithPageReplacementBenchmark.java
@@ -0,0 +1,43 @@
+/*
+ * 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.yardstick.cache;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Benchmark put operation with page replacement.
+ */
+public class IgnitePutWithPageReplacementBenchmark extends IgniteAbstractPageReplacementBenchmark {
+ /** {@inheritDoc} */
+ @Override public boolean test(Map<Object, Object> map) throws Exception {
+ int portion = 100;
+
+ Map<Integer, TestValue> putMap = new HashMap<>(portion, 1.f);
+
+ for (int i = 0; i < portion; i++) {
+ int val = nextKey();
+
+ putMap.put(val, new TestValue(val));
+ }
+
+ cache().putAll(putMap);
+
+ return true;
+ }
+}