You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2017/04/10 16:34:19 UTC

[1/2] ignite git commit: IGNITE-4534 - Added offheap evictions

Repository: ignite
Updated Branches:
  refs/heads/ignite-3477-master baa3835ee -> ff5b3e168


http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
index 190349a..9546890 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/DataPageIO.java
@@ -18,9 +18,11 @@
 package org.apache.ignite.internal.processors.cache.database.tree.io;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.List;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
@@ -254,6 +256,29 @@ public class DataPageIO extends PageIO {
 
     /**
      * @param pageAddr Page address.
+     * @param c Closure.
+     * @param <T> Closure return type.
+     * @return Collection of closure results for all items in page.
+     * @throws IgniteCheckedException In case of error in closure body.
+     */
+    public <T> List<T> forAllItems(long pageAddr, CC<T> c) throws IgniteCheckedException {
+        long pageId = getPageId(pageAddr);
+
+        int cnt = getDirectCount(pageAddr);
+
+        List<T> res = new ArrayList<>(cnt);
+
+        for (int i = 0; i < cnt; i++) {
+            long link = PageIdUtils.link(pageId, i);
+
+            res.add(c.apply(link));
+        }
+
+        return res;
+    }
+
+    /**
+     * @param pageAddr Page address.
      * @param cnt Indirect count.
      */
     private void setIndirectCount(long pageAddr, int cnt) {
@@ -1007,7 +1032,8 @@ public class DataPageIO extends PageIO {
         final int keySize = row.key().valueBytesLength(null);
         final int valSize = row.value().valueBytesLength(null);
 
-        int written = writeFragment(row, buf, rowOff, payloadSize, EntryPart.KEY, keySize, valSize);
+        int written = writeFragment(row, buf, rowOff, payloadSize, EntryPart.CACHE_ID, keySize, valSize);
+        written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.KEY, keySize, valSize);
         written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.EXPIRE_TIME, keySize, valSize);
         written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.VALUE, keySize, valSize);
         written += writeFragment(row, buf, rowOff + written, payloadSize - written, EntryPart.VERSION, keySize, valSize);
@@ -1039,28 +1065,36 @@ public class DataPageIO extends PageIO {
         final int prevLen;
         final int curLen;
 
+        int cacheIdSize = row.cacheId() == 0 ? 0 : 4;
+
         switch (type) {
-            case KEY:
+            case CACHE_ID:
                 prevLen = 0;
-                curLen = keySize;
+                curLen = cacheIdSize;
+
+                break;
+
+            case KEY:
+                prevLen = cacheIdSize;
+                curLen = cacheIdSize + keySize;
 
                 break;
 
             case EXPIRE_TIME:
-                prevLen = keySize;
-                curLen = keySize + 8;
+                prevLen = cacheIdSize + keySize;
+                curLen = cacheIdSize + keySize + 8;
 
                 break;
 
             case VALUE:
-                prevLen = keySize + 8;
-                curLen = keySize + valSize + 8;
+                prevLen = cacheIdSize + keySize + 8;
+                curLen = cacheIdSize + keySize + valSize + 8;
 
                 break;
 
             case VERSION:
-                prevLen = keySize + valSize + 8;
-                curLen = keySize + valSize + CacheVersionIO.size(row.version(), false) + 8;
+                prevLen = cacheIdSize + keySize + valSize + 8;
+                curLen = cacheIdSize + keySize + valSize + CacheVersionIO.size(row.version(), false) + 8;
 
                 break;
 
@@ -1075,6 +1109,8 @@ public class DataPageIO extends PageIO {
 
         if (type == EntryPart.EXPIRE_TIME)
             writeExpireTimeFragment(buf, row.expireTime(), rowOff, len, prevLen);
+        else if (type == EntryPart.CACHE_ID)
+            writeCacheIdFragment(buf, row.cacheId(), rowOff, len, prevLen);
         else if (type != EntryPart.VERSION) {
             // Write key or value.
             final CacheObject co = type == EntryPart.KEY ? row.key() : row.value();
@@ -1139,6 +1175,32 @@ public class DataPageIO extends PageIO {
     }
 
     /**
+     * @param buf Buffer.
+     * @param cacheId Cache ID.
+     * @param rowOff Row offset.
+     * @param len Length.
+     * @param prevLen Prev length.
+     */
+    private void writeCacheIdFragment(ByteBuffer buf, int cacheId, int rowOff, int len, int prevLen) {
+        if (cacheId == 0)
+            return;
+
+        int size = 4;
+
+        if (size <= len)
+            buf.putInt(cacheId);
+        else {
+            ByteBuffer cacheIdBuf = ByteBuffer.allocate(size);
+
+            cacheIdBuf.order(buf.order());
+
+            cacheIdBuf.putInt(cacheId);
+
+            buf.put(cacheIdBuf.array(), rowOff - prevLen, len);
+        }
+    }
+
+    /**
      *
      */
     private enum EntryPart {
@@ -1152,7 +1214,10 @@ public class DataPageIO extends PageIO {
         VERSION,
 
         /** */
-        EXPIRE_TIME
+        EXPIRE_TIME,
+
+        /** */
+        CACHE_ID
     }
 
     /**
@@ -1326,14 +1391,21 @@ public class DataPageIO extends PageIO {
     ) throws IgniteCheckedException {
         long addr = pageAddr + dataOff;
 
+        int cacheIdSize = row.cacheId() != 0 ? 4 : 0;
+
         if (newRow) {
             PageUtils.putShort(addr, 0, (short)payloadSize);
             addr += 2;
 
+            if (cacheIdSize != 0)
+                PageUtils.putInt(addr, 0, row.cacheId());
+
+            addr += cacheIdSize;
+
             addr += row.key().putValue(addr);
         }
         else
-            addr += (2 + row.key().valueBytesLength(null));
+            addr += (2 + cacheIdSize + row.key().valueBytesLength(null));
 
         addr += row.value().putValue(addr);
 
@@ -1358,4 +1430,20 @@ public class DataPageIO extends PageIO {
 
         PageUtils.putBytes(pageAddr, dataOff, payload);
     }
+
+    /**
+     * Defines closure interface for applying computations to data page items.
+     *
+     * @param <T> Closure return type.
+     */
+    public interface CC<T> {
+        /**
+         * Closure body.
+         *
+         * @param link Link to item.
+         * @return Closure return value.
+         * @throws IgniteCheckedException In case of error in closure body.
+         */
+        public T apply(long link) throws IgniteCheckedException;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
index d652767..5a8dfa0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
@@ -1795,6 +1795,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                     // Do not check topology version if topology was locked on near node by
                     // external transaction or explicit lock.
                     if (req.topologyLocked() || !needRemap(req.topologyVersion(), top.topologyVersion())) {
+                        ctx.shared().database().ensureFreeSpace(ctx.memoryPolicy());
+
                         locked = lockEntries(req, req.topologyVersion());
 
                         boolean hasNear = ctx.discovery().cacheNearNode(node, name());

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
index c257154..64f6187 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
@@ -2673,6 +2673,8 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements AutoClosea
                                 GridCacheEntryEx entry = cacheCtx.cache().entryEx(key, topVer);
 
                                 try {
+                                    cacheCtx.shared().database().ensureFreeSpace(cacheCtx.memoryPolicy());
+
                                     EntryGetResult verVal = entry.versionedValue(cacheVal,
                                         ver,
                                         null,

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
index 767ce84..e5cd469 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxRemote.java
@@ -334,7 +334,7 @@ public class GridNearTxRemote extends GridDistributedTxRemoteAdapter {
 
                 CacheObject val = cached.peek(null);
 
-                if (val == null && cached.evictInternal(xidVer, null)) {
+                if (val == null && cached.evictInternal(xidVer, null, false)) {
                     evicted.add(entry.txKey());
 
                     return false;
@@ -395,7 +395,7 @@ public class GridNearTxRemote extends GridDistributedTxRemoteAdapter {
 
                 CacheObject peek = cached.peek(null);
 
-                if (peek == null && cached.evictInternal(xidVer, null)) {
+                if (peek == null && cached.evictInternal(xidVer, null, false)) {
                     cached.context().cache().removeIfObsolete(key.key());
 
                     evicted.add(key);

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
index da92692..b8c0e36 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
@@ -791,6 +791,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
 
         CacheEntryPredicate[] filters = CU.filterArray(filter);
 
+        ctx.shared().database().ensureFreeSpace(ctx.memoryPolicy());
+
         if (writeThrough && keys.size() > 1) {
             return updateWithBatch(op,
                 keys,
@@ -995,8 +997,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
                             null,
                             null,
                             /*read-through*/true,
-                            /**update-metrics*/true,
-                            /**event*/true,
+                            /*update-metrics*/true,
+                            /*event*/true,
                             subjId,
                             entryProcessor,
                             taskName,

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
index 4ec89e1..9beb296 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessor.java
@@ -180,13 +180,6 @@ public interface IgniteCacheObjectProcessor extends GridProcessor {
     public CacheObject toCacheObject(CacheObjectContext ctx, ByteBuffer buf);
 
     /**
-     * @param ctx Cache context.
-     * @param buf Buffer to read from.
-     * @return Cache object.
-     */
-    public KeyCacheObject toKeyCacheObject(CacheObjectContext ctx, ByteBuffer buf) throws IgniteCheckedException;
-
-    /**
      * @param ctx Cache object context.
      * @param buf Buffer.
      * @param incompleteObj Incomplete cache object or {@code null} if it's a first read.

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
index 4726d86..a8595fb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
@@ -188,22 +188,6 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
     }
 
     /** {@inheritDoc} */
-    @Override public KeyCacheObject toKeyCacheObject(CacheObjectContext ctx, ByteBuffer buf) throws IgniteCheckedException {
-        int len = buf.getInt();
-
-        if (len == 0)
-            return null;
-
-        byte type = buf.get();
-
-        byte[] data = new byte[len];
-
-        buf.get(data);
-
-        return toKeyCacheObject(ctx, type, data);
-    }
-
-    /** {@inheritDoc} */
     @Override public IncompleteCacheObject toCacheObject(
         final CacheObjectContext ctx,
         final ByteBuffer buf,

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
index 46dec44..adbd6f1 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTestEntryEx.java
@@ -369,7 +369,7 @@ public class GridCacheTestEntryEx extends GridMetadataAwareAdapter implements Gr
 
     /** @inheritDoc */
     @Override public boolean evictInternal(GridCacheVersion obsoleteVer,
-        @Nullable CacheEntryPredicate[] filter) {
+        @Nullable CacheEntryPredicate[] filter, boolean evictOffheap) {
         assert false;
 
         return false;

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCacheDhtEntrySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCacheDhtEntrySelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCacheDhtEntrySelfTest.java
index bc7fffe..58c3841 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCacheDhtEntrySelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCacheDhtEntrySelfTest.java
@@ -280,7 +280,7 @@ public class GridCacheDhtEntrySelfTest extends GridCommonAbstractTest {
         assert e0.readers().contains(other.id());
         assert e1 == null || e1.readers().isEmpty();
 
-        assert !e0.evictInternal(dht0.context().versions().next(), null);
+        assert !e0.evictInternal(dht0.context().versions().next(), null, false);
 
         assertEquals(1, near0.localSize(CachePeekMode.ALL));
         assertEquals(1, dht0.localSize(null));
@@ -288,7 +288,7 @@ public class GridCacheDhtEntrySelfTest extends GridCommonAbstractTest {
         assertEquals(1, near1.localSize(CachePeekMode.ALL));
         assertEquals(0, dht1.localSize(null));
 
-        assert !e0.evictInternal(dht0.context().versions().next(), null);
+        assert !e0.evictInternal(dht0.context().versions().next(), null, false);
 
         assertEquals(1, near0.localSize(CachePeekMode.ALL));
         assertEquals(1, dht0.localSize(null));

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionAbstractTest.java
new file mode 100644
index 0000000..bf05146
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionAbstractTest.java
@@ -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.
+*/
+package org.apache.ignite.internal.processors.cache.eviction.paged;
+
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.MemoryConfiguration;
+import org.apache.ignite.configuration.MemoryPolicyConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ *
+ */
+public class PageEvictionAbstractTest extends GridCommonAbstractTest {
+    /** */
+    protected static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** Offheap size for memory policy. */
+    private static final int SIZE = 256 * 1024 * 1024;
+
+    /** Page size. */
+    static final int PAGE_SIZE = 2048;
+
+    /** Number of entries. */
+    static final int ENTRIES = 400_000;
+
+    /** Empty pages pool size. */
+    private static final int EMPTY_PAGES_POOL_SIZE = 100;
+
+    /** Eviction threshold. */
+    private static final double EVICTION_THRESHOLD = 0.9;
+
+    /** Default policy name. */
+    private static final String DEFAULT_POLICY_NAME = "dfltPlc";
+
+    /**
+     * @param mode Eviction mode.
+     * @param configuration Configuration.
+     * @return Configuration with given eviction mode set.
+     */
+    static IgniteConfiguration setEvictionMode(DataPageEvictionMode mode, IgniteConfiguration configuration) {
+        MemoryPolicyConfiguration[] policies = configuration.getMemoryConfiguration().getMemoryPolicies();
+
+        for (MemoryPolicyConfiguration plcCfg : policies)
+            plcCfg.setPageEvictionMode(mode);
+
+        return configuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+        MemoryConfiguration dbCfg = new MemoryConfiguration();
+
+        MemoryPolicyConfiguration plc = new MemoryPolicyConfiguration();
+
+        plc.setSize(SIZE);
+        plc.setEmptyPagesPoolSize(EMPTY_PAGES_POOL_SIZE);
+        plc.setEvictionThreshold(EVICTION_THRESHOLD);
+        plc.setName(DEFAULT_POLICY_NAME);
+
+        dbCfg.setMemoryPolicies(plc);
+        dbCfg.setPageSize(PAGE_SIZE);
+        dbCfg.setDefaultMemoryPolicyName(DEFAULT_POLICY_NAME);
+
+        cfg.setMemoryConfiguration(dbCfg);
+
+        return cfg;
+    }
+
+    /**
+     * @param name Name.
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Atomicity mode.
+     * @param writeSynchronizationMode Write synchronization mode.
+     * @param memoryPlcName Memory policy name.
+     * @return Cache configuration.
+     */
+    protected static CacheConfiguration<Object, Object> cacheConfig(
+        String name,
+        String memoryPlcName,
+        CacheMode cacheMode,
+        CacheAtomicityMode atomicityMode,
+        CacheWriteSynchronizationMode writeSynchronizationMode
+    ) {
+        CacheConfiguration<Object, Object> cacheConfiguration = new CacheConfiguration<>()
+            .setName(name)
+            .setAffinity(new RendezvousAffinityFunction(false, 32))
+            .setCacheMode(cacheMode)
+            .setAtomicityMode(atomicityMode)
+            .setMemoryPolicyName(memoryPlcName)
+            .setWriteSynchronizationMode(writeSynchronizationMode);
+
+        if (cacheMode == CacheMode.PARTITIONED)
+            cacheConfiguration.setBackups(1);
+
+        return cacheConfiguration;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionMultinodeTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionMultinodeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionMultinodeTest.java
new file mode 100644
index 0000000..2302de1
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionMultinodeTest.java
@@ -0,0 +1,110 @@
+/*
+* 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.eviction.paged;
+
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public abstract class PageEvictionMultinodeTest extends PageEvictionAbstractTest {
+    /** Cache modes. */
+    private static final CacheMode[] CACHE_MODES = {CacheMode.PARTITIONED, CacheMode.REPLICATED};
+
+    /** Atomicity modes. */
+    private static final CacheAtomicityMode[] ATOMICITY_MODES = {
+        CacheAtomicityMode.ATOMIC, CacheAtomicityMode.TRANSACTIONAL};
+
+    /** Write modes. */
+    private static final CacheWriteSynchronizationMode[] WRITE_MODES = {CacheWriteSynchronizationMode.PRIMARY_SYNC,
+        CacheWriteSynchronizationMode.FULL_SYNC, CacheWriteSynchronizationMode.FULL_ASYNC};
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGridsMultiThreaded(4, false);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected long getTestTimeout() {
+        return 10 * 60 * 1000;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testPageEviction() throws Exception {
+        for (int i = 0; i < CACHE_MODES.length; i++) {
+            for (int j = 0; j < ATOMICITY_MODES.length; j++) {
+                for (int k = 0; k < WRITE_MODES.length; k++) {
+                    if (i + j + Math.min(k, 1) <= 1) {
+                        CacheConfiguration<Object, Object> cfg = cacheConfig(
+                            "evict" + i + j + k, null, CACHE_MODES[i], ATOMICITY_MODES[j], WRITE_MODES[k]);
+
+                        createCacheAndTestEvcition(cfg);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @param cfg Config.
+     * @throws Exception If failed.
+     */
+    private void createCacheAndTestEvcition(CacheConfiguration<Object, Object> cfg) throws Exception {
+        IgniteCache<Object, Object> cache = ignite(0).getOrCreateCache(cfg);
+
+        for (int i = 1; i <= ENTRIES; i++) {
+            ThreadLocalRandom r = ThreadLocalRandom.current();
+
+            if (r.nextInt() % 5 == 0)
+                cache.put(i, new TestObject(PAGE_SIZE / 4 - 50 + r.nextInt(5000))); // Fragmented object.
+            else
+                cache.put(i, new TestObject(r.nextInt(PAGE_SIZE / 4 - 50))); // Fits in one page.
+
+            if (r.nextInt() % 7 == 0)
+                cache.get(r.nextInt(i)); // Touch.
+            else if (r.nextInt() % 11 == 0)
+                cache.remove(r.nextInt(i)); // Remove.
+            else if (r.nextInt() % 13 == 0)
+                cache.put(r.nextInt(i), new TestObject(r.nextInt(PAGE_SIZE / 2))); // Update.
+
+            if (i % (ENTRIES / 10) == 0)
+                System.out.println(">>> Entries put: " + i);
+        }
+
+        int resultingSize = cache.size(CachePeekMode.PRIMARY);
+
+        System.out.println(">>> Resulting size: " + resultingSize);
+
+        // More than half of entries evicted, no OutOfMemory occurred, success.
+        assertTrue(resultingSize < ENTRIES / 2);
+
+        ignite(0).destroyCache(cfg.getName());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionReadThroughTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionReadThroughTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionReadThroughTest.java
new file mode 100644
index 0000000..c8cd7c9
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionReadThroughTest.java
@@ -0,0 +1,140 @@
+/*
+* 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.eviction.paged;
+
+import java.util.concurrent.ThreadLocalRandom;
+import javax.cache.Cache;
+import javax.cache.configuration.Factory;
+import javax.cache.integration.CacheLoaderException;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.store.CacheStoreAdapter;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ *
+ */
+public class PageEvictionReadThroughTest extends PageEvictionAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return setEvictionMode(DataPageEvictionMode.RANDOM_LRU, super.getConfiguration(gridName));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEvictionWithReadThroughAtomicReplicated() throws Exception {
+        testEvictionWithReadThrough(CacheAtomicityMode.ATOMIC, CacheMode.REPLICATED);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEvictionWithReadThroughAtomicLocal() throws Exception {
+        testEvictionWithReadThrough(CacheAtomicityMode.ATOMIC, CacheMode.LOCAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEvictionWithReadThroughTxReplicated() throws Exception {
+        testEvictionWithReadThrough(CacheAtomicityMode.TRANSACTIONAL, CacheMode.REPLICATED);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEvictionWithReadThroughTxLocal() throws Exception {
+        testEvictionWithReadThrough(CacheAtomicityMode.TRANSACTIONAL, CacheMode.LOCAL);
+    }
+
+    /**
+     * @param atomicityMode Atomicity mode.
+     * @param cacheMode Cache mode.
+     * @throws Exception If failed.
+     */
+    private void testEvictionWithReadThrough(CacheAtomicityMode atomicityMode, CacheMode cacheMode) throws Exception {
+        startGridsMultiThreaded(4);
+
+        CacheConfiguration<Object, Object> cfg = cacheConfig("evict-rebalance", null, cacheMode, atomicityMode,
+            CacheWriteSynchronizationMode.PRIMARY_SYNC);
+        cfg.setReadThrough(true);
+        cfg.setCacheStoreFactory(new TestStoreFactory());
+
+        IgniteCache<Object, Object> cache = ignite(0).getOrCreateCache(cfg);
+
+        for (int i = 1; i <= ENTRIES; i++) {
+            cache.get(i);
+
+            if (i % (ENTRIES / 10) == 0)
+                System.out.println(">>> Entries: " + i);
+        }
+
+        int size = cache.size(CachePeekMode.PRIMARY);
+
+        System.out.println(">>> Resulting size: " + size);
+
+        assertTrue(size > 0);
+
+        assertTrue(size < ENTRIES);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+    }
+
+    /**
+     *
+     */
+    private static class TestStoreFactory implements Factory<TestCacheStore> {
+        /** {@inheritDoc} */
+        @Override public TestCacheStore create() {
+            return new TestCacheStore();
+        }
+    }
+
+    /**
+     *
+     */
+    private static class TestCacheStore extends CacheStoreAdapter<Object, Object> {
+        /** {@inheritDoc} */
+        @Override public Object load(Object key) throws CacheLoaderException {
+            ThreadLocalRandom r = ThreadLocalRandom.current();
+
+            if (r.nextInt() % 5 == 0)
+                return new TestObject(PAGE_SIZE / 4 - 50 + r.nextInt(5000)); // Fragmented object.
+            else
+                return new TestObject(r.nextInt(PAGE_SIZE / 4 - 50)); // Fits in one page.
+        }
+
+        /** {@inheritDoc} */
+        @Override public void write(Cache.Entry<?, ?> entry) {
+            throw new UnsupportedOperationException();
+        }
+
+        /** {@inheritDoc} */
+        @Override public void delete(Object key) {
+            // No-op.
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionTouchOrderTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionTouchOrderTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionTouchOrderTest.java
new file mode 100644
index 0000000..4335649
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionTouchOrderTest.java
@@ -0,0 +1,109 @@
+/*
+* 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.eviction.paged;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ *
+ */
+public class PageEvictionTouchOrderTest extends PageEvictionAbstractTest {
+    /** Test entries number. */
+    private static final int SAFE_ENTRIES = 1000;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return setEvictionMode(/* Overriden by FairFifoPageEvictionTracker */DataPageEvictionMode.RANDOM_LRU,
+            super.getConfiguration(gridName));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        System.setProperty("override.fair.fifo.page.eviction.tracker", "true");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testTouchOrderWithFairFifoEvictionAtomicReplicated() throws Exception {
+        testTouchOrderWithFairFifoEviction(CacheAtomicityMode.ATOMIC, CacheMode.REPLICATED);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testTouchOrderWithFairFifoEvictionAtomicLocal() throws Exception {
+        testTouchOrderWithFairFifoEviction(CacheAtomicityMode.ATOMIC, CacheMode.LOCAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testTouchOrderWithFairFifoEvictionTxReplicated() throws Exception {
+        testTouchOrderWithFairFifoEviction(CacheAtomicityMode.TRANSACTIONAL, CacheMode.REPLICATED);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testTouchOrderWithFairFifoEvictionTxLocal() throws Exception {
+        testTouchOrderWithFairFifoEviction(CacheAtomicityMode.TRANSACTIONAL, CacheMode.LOCAL);
+    }
+
+    /**
+     * @param atomicityMode Atomicity mode.
+     * @param cacheMode Cache mode.
+     * @throws Exception If failed.
+     */
+    private void testTouchOrderWithFairFifoEviction(CacheAtomicityMode atomicityMode, CacheMode cacheMode)
+        throws Exception {
+        startGrid(0);
+
+        CacheConfiguration<Object, Object> cfg = cacheConfig("evict-fair", null, cacheMode, atomicityMode,
+            CacheWriteSynchronizationMode.PRIMARY_SYNC);
+
+        IgniteCache<Object, Object> cache = ignite(0).getOrCreateCache(cfg);
+
+        for (int i = 1; i <= ENTRIES; i++) {
+            cache.put(i, new TestObject(PAGE_SIZE / 6));
+            // Row size is between PAGE_SIZE / 2 and PAGE_SIZE. Enforces "one row - one page".
+
+            if (i % (ENTRIES / 10) == 0)
+                System.out.println(">>> Entries put: " + i);
+        }
+
+        for (int i = ENTRIES - SAFE_ENTRIES + 1; i <= ENTRIES; i++)
+            assertNotNull(cache.get(i));
+
+        for (int i = 1; i <= SAFE_ENTRIES; i++)
+            assertNull(cache.get(i));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+
+        System.setProperty("override.fair.fifo.page.eviction.tracker", "false");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionWithRebalanceTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionWithRebalanceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionWithRebalanceTest.java
new file mode 100644
index 0000000..fd80201
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionWithRebalanceTest.java
@@ -0,0 +1,81 @@
+/*
+* 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.eviction.paged;
+
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public abstract class PageEvictionWithRebalanceTest extends PageEvictionAbstractTest {
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEvictionWithRebalance() throws Exception {
+        startGridsMultiThreaded(4);
+
+        CacheConfiguration<Object, Object> cfg = cacheConfig("evict-rebalance", null, CacheMode.PARTITIONED,
+            CacheAtomicityMode.ATOMIC, CacheWriteSynchronizationMode.PRIMARY_SYNC);
+
+        IgniteCache<Object, Object> cache = ignite(0).getOrCreateCache(cfg);
+
+        for (int i = 1; i <= ENTRIES; i++) {
+            ThreadLocalRandom r = ThreadLocalRandom.current();
+
+            if (r.nextInt() % 5 == 0)
+                cache.put(i, new TestObject(PAGE_SIZE / 4 - 50 + r.nextInt(5000))); // Fragmented object.
+            else
+                cache.put(i, new TestObject(r.nextInt(PAGE_SIZE / 4 - 50))); // Fits in one page.
+
+            if (i % (ENTRIES / 10) == 0)
+                System.out.println(">>> Entries put: " + i);
+        }
+
+        int size = cache.size(CachePeekMode.PRIMARY);
+
+        System.out.println(">>> Resulting size: " + size);
+
+        assertTrue(size < ENTRIES);
+
+        for (int i = 3; i >= 1; i--) {
+            stopGrid(i);
+
+            cache.rebalance().get();
+
+            awaitPartitionMapExchange();
+
+            int rebalanceSize = cache.size(CachePeekMode.PRIMARY);
+
+            System.out.println(">>> Size after rebalance: " + rebalanceSize);
+
+            assertTrue(rebalanceSize < size);
+
+            size = rebalanceSize;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionMultinodeTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionMultinodeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionMultinodeTest.java
new file mode 100644
index 0000000..b05ec43
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionMultinodeTest.java
@@ -0,0 +1,30 @@
+/*
+* 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.eviction.paged;
+
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ *
+ */
+public class Random2LruPageEvictionMultinodeTest extends PageEvictionMultinodeTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return setEvictionMode(DataPageEvictionMode.RANDOM_2_LRU, super.getConfiguration(gridName));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionWithRebalanceTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionWithRebalanceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionWithRebalanceTest.java
new file mode 100644
index 0000000..56698fb
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/Random2LruPageEvictionWithRebalanceTest.java
@@ -0,0 +1,30 @@
+/*
+* 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.eviction.paged;
+
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ *
+ */
+public class Random2LruPageEvictionWithRebalanceTest extends PageEvictionWithRebalanceTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return setEvictionMode(DataPageEvictionMode.RANDOM_2_LRU, super.getConfiguration(gridName));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionMultinodeTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionMultinodeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionMultinodeTest.java
new file mode 100644
index 0000000..38ca2af
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionMultinodeTest.java
@@ -0,0 +1,30 @@
+/*
+* 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.eviction.paged;
+
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ *
+ */
+public class RandomLruPageEvictionMultinodeTest extends PageEvictionMultinodeTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return setEvictionMode(DataPageEvictionMode.RANDOM_LRU, super.getConfiguration(gridName));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionWithRebalanceTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionWithRebalanceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionWithRebalanceTest.java
new file mode 100644
index 0000000..d961360
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/RandomLruPageEvictionWithRebalanceTest.java
@@ -0,0 +1,30 @@
+/*
+* 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.eviction.paged;
+
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ *
+ */
+public class RandomLruPageEvictionWithRebalanceTest extends PageEvictionWithRebalanceTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        return setEvictionMode(DataPageEvictionMode.RANDOM_LRU, super.getConfiguration(gridName));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/TestObject.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/TestObject.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/TestObject.java
new file mode 100644
index 0000000..baf2414
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/TestObject.java
@@ -0,0 +1,78 @@
+/*
+* 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.eviction.paged;
+
+import java.util.Arrays;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ *
+ */
+class TestObject {
+    /** */
+    private int b;
+
+    /** */
+    private String c;
+
+    /** */
+    private int[] arr;
+
+    /**
+     * @param intArrSize Int array size.
+     */
+    public TestObject(int intArrSize) {
+        this.b = intArrSize;
+
+        this.c = String.valueOf(2 * intArrSize);
+
+        arr = new int[intArrSize];
+
+        for (int i = 0; i < intArrSize; i++)
+            arr[i] = ThreadLocalRandom.current().nextInt();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        TestObject testObj = (TestObject)o;
+
+        if (b != testObj.b)
+            return false;
+
+        if (c != null ? !c.equals(testObj.c) : testObj.c != null)
+            return false;
+
+        return Arrays.equals(arr, testObj.arr);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        int res = b;
+
+        res = 31 * res + (c != null ? c.hashCode() : 0);
+
+        res = 31 * res + Arrays.hashCode(arr);
+
+        return res;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImplSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImplSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImplSelfTest.java
index 7fccef1..d5011a8 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImplSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImplSelfTest.java
@@ -39,6 +39,8 @@ import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.database.MemoryMetricsImpl;
+import org.apache.ignite.internal.processors.cache.database.MemoryPolicy;
+import org.apache.ignite.internal.processors.cache.database.evict.NoOpPageEvictionTracker;
 import org.apache.ignite.internal.processors.cache.database.freelist.FreeList;
 import org.apache.ignite.internal.processors.cache.database.freelist.FreeListImpl;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -334,7 +336,11 @@ public class FreeListImplSelfTest extends GridCommonAbstractTest {
 
         long metaPageId = pageMem.allocatePage(1, 1, PageIdAllocator.FLAG_DATA);
 
-        return new FreeListImpl(1, "freelist", pageMem, new MemoryMetricsImpl(null), null, null, metaPageId, true);
+        MemoryMetricsImpl metrics = new MemoryMetricsImpl(null);
+
+        MemoryPolicy memPlc = new MemoryPolicy(pageMem, null, metrics, new NoOpPageEvictionTracker());
+
+        return new FreeListImpl(1, "freelist", metrics, memPlc, null, null, metaPageId, true);
     }
 
     /**
@@ -407,6 +413,11 @@ public class FreeListImplSelfTest extends GridCommonAbstractTest {
         @Override public int hash() {
             throw new UnsupportedOperationException();
         }
+
+        /** {@inheritDoc} */
+        @Override public int cacheId() {
+            return 0;
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheEvictionSelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheEvictionSelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheEvictionSelfTestSuite.java
index 94e1447..1bdfdd1 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheEvictionSelfTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheEvictionSelfTestSuite.java
@@ -33,6 +33,12 @@ import org.apache.ignite.internal.processors.cache.eviction.fifo.FifoEvictionPol
 import org.apache.ignite.internal.processors.cache.eviction.lru.LruEvictionPolicySelfTest;
 import org.apache.ignite.internal.processors.cache.eviction.lru.LruNearEvictionPolicySelfTest;
 import org.apache.ignite.internal.processors.cache.eviction.lru.LruNearOnlyNearEvictionPolicySelfTest;
+import org.apache.ignite.internal.processors.cache.eviction.paged.PageEvictionReadThroughTest;
+import org.apache.ignite.internal.processors.cache.eviction.paged.PageEvictionTouchOrderTest;
+import org.apache.ignite.internal.processors.cache.eviction.paged.Random2LruPageEvictionMultinodeTest;
+import org.apache.ignite.internal.processors.cache.eviction.paged.Random2LruPageEvictionWithRebalanceTest;
+import org.apache.ignite.internal.processors.cache.eviction.paged.RandomLruPageEvictionMultinodeTest;
+import org.apache.ignite.internal.processors.cache.eviction.paged.RandomLruPageEvictionWithRebalanceTest;
 import org.apache.ignite.internal.processors.cache.eviction.sorted.SortedEvictionPolicySelfTest;
 
 /**
@@ -63,6 +69,13 @@ public class IgniteCacheEvictionSelfTestSuite extends TestSuite {
         suite.addTest(new TestSuite(GridCacheEmptyEntriesLocalSelfTest.class));
         suite.addTest(new TestSuite(GridCacheEvictableEntryEqualsSelfTest.class));
 
+        suite.addTest(new TestSuite(RandomLruPageEvictionMultinodeTest.class));
+        suite.addTest(new TestSuite(Random2LruPageEvictionMultinodeTest.class));
+        suite.addTest(new TestSuite(RandomLruPageEvictionWithRebalanceTest.class));
+        suite.addTest(new TestSuite(Random2LruPageEvictionWithRebalanceTest.class));
+        suite.addTest(new TestSuite(PageEvictionTouchOrderTest.class));
+        suite.addTest(new TestSuite(PageEvictionReadThroughTest.class));
+
         return suite;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
index ce10cdb..042e163 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Row.java
@@ -174,4 +174,9 @@ public abstract class GridH2Row extends Row implements GridSearchRowPointer, Cac
     @Override public int hash() {
         throw new UnsupportedOperationException();
     }
+
+    /** {@inheritDoc} */
+    @Override public int cacheId() {
+        return 0;
+    }
 }
\ No newline at end of file


[2/2] ignite git commit: IGNITE-4534 - Added offheap evictions

Posted by ag...@apache.org.
IGNITE-4534 - Added offheap evictions


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/ff5b3e16
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/ff5b3e16
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/ff5b3e16

Branch: refs/heads/ignite-3477-master
Commit: ff5b3e16850e503b79a13c44b667140d23c1f080
Parents: baa3835
Author: Ivan Rakov <iv...@gmail.com>
Authored: Mon Apr 10 19:23:43 2017 +0300
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Mon Apr 10 19:23:43 2017 +0300

----------------------------------------------------------------------
 .../configuration/DataPageEvictionMode.java     |  32 +++
 .../MemoryPolicyConfiguration.java              |  70 ++++++
 .../ignite/internal/pagemem/PageSupport.java    |  10 +
 .../pagemem/impl/PageMemoryNoStoreImpl.java     |   8 +
 .../internal/pagemem/impl/PageNoStoreImpl.java  |   0
 .../cache/CacheOffheapEvictionManager.java      |   6 +-
 .../processors/cache/GridCacheAdapter.java      |   2 +
 .../processors/cache/GridCacheEntryEx.java      |   5 +-
 .../cache/GridCacheEvictionManager.java         |   2 +-
 .../processors/cache/GridCacheMapEntry.java     |  32 ++-
 .../cache/IgniteCacheOffheapManagerImpl.java    |  34 ++-
 .../processors/cache/database/CacheDataRow.java |   5 +
 .../cache/database/CacheDataRowAdapter.java     | 116 ++++++++-
 .../IgniteCacheDatabaseSharedManager.java       | 126 ++++++++--
 .../processors/cache/database/MemoryPolicy.java |  19 +-
 .../evict/FairFifoPageEvictionTracker.java      |  74 ++++++
 .../database/evict/NoOpPageEvictionTracker.java |  50 ++++
 .../evict/PageAbstractEvictionTracker.java      | 243 +++++++++++++++++++
 .../database/evict/PageEvictionTracker.java     |  52 ++++
 .../evict/Random2LruPageEvictionTracker.java    | 180 ++++++++++++++
 .../evict/RandomLruPageEvictionTracker.java     | 157 ++++++++++++
 .../cache/database/freelist/FreeListImpl.java   |  62 +++--
 .../cache/database/tree/io/DataPageIO.java      | 110 ++++++++-
 .../dht/atomic/GridDhtAtomicCache.java          |   2 +
 .../cache/distributed/near/GridNearTxLocal.java |   2 +
 .../distributed/near/GridNearTxRemote.java      |   4 +-
 .../local/atomic/GridLocalAtomicCache.java      |   6 +-
 .../cacheobject/IgniteCacheObjectProcessor.java |   7 -
 .../IgniteCacheObjectProcessorImpl.java         |  16 --
 .../processors/cache/GridCacheTestEntryEx.java  |   2 +-
 .../dht/GridCacheDhtEntrySelfTest.java          |   4 +-
 .../paged/PageEvictionAbstractTest.java         | 124 ++++++++++
 .../paged/PageEvictionMultinodeTest.java        | 110 +++++++++
 .../paged/PageEvictionReadThroughTest.java      | 140 +++++++++++
 .../paged/PageEvictionTouchOrderTest.java       | 109 +++++++++
 .../paged/PageEvictionWithRebalanceTest.java    |  81 +++++++
 .../Random2LruPageEvictionMultinodeTest.java    |  30 +++
 ...Random2LruPageEvictionWithRebalanceTest.java |  30 +++
 .../RandomLruPageEvictionMultinodeTest.java     |  30 +++
 .../RandomLruPageEvictionWithRebalanceTest.java |  30 +++
 .../cache/eviction/paged/TestObject.java        |  78 ++++++
 .../database/FreeListImplSelfTest.java          |  13 +-
 .../IgniteCacheEvictionSelfTestSuite.java       |  13 +
 .../processors/query/h2/opt/GridH2Row.java      |   5 +
 44 files changed, 2130 insertions(+), 101 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/configuration/DataPageEvictionMode.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/DataPageEvictionMode.java b/modules/core/src/main/java/org/apache/ignite/configuration/DataPageEvictionMode.java
new file mode 100644
index 0000000..bada68e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/DataPageEvictionMode.java
@@ -0,0 +1,32 @@
+/*
+* 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;
+
+/**
+ * Enumeration defines data page eviction modes.
+ */
+public enum DataPageEvictionMode {
+    /** Disabled. */
+    DISABLED,
+
+    /** Random lru. */
+    RANDOM_LRU,
+
+    /** Random 2-lru. */
+    RANDOM_2_LRU
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java
index 2add64f..d6203c6 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/MemoryPolicyConfiguration.java
@@ -19,6 +19,8 @@ package org.apache.ignite.configuration;
 import java.io.Serializable;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.processors.cache.database.MemoryPolicy;
+import org.apache.ignite.internal.processors.cache.database.freelist.FreeList;
+import org.apache.ignite.internal.processors.cache.database.tree.io.DataPageIO;
 
 /**
  * Configuration bean used for creating {@link MemoryPolicy} instances.
@@ -43,6 +45,18 @@ public final class MemoryPolicyConfiguration implements Serializable {
         return name;
     }
 
+    /** Algorithm for per-page eviction. If {@link DataPageEvictionMode#DISABLED} set, eviction is not performed. */
+    private DataPageEvictionMode pageEvictionMode = DataPageEvictionMode.DISABLED;
+
+    /** Allocation of new {@link DataPageIO} pages is stopped when this percentage of pages are allocated. */
+    private double evictionThreshold = 0.9;
+
+    /** Allocation of new {@link DataPageIO} pages is stopped by maintaining this amount of empty pages in
+     * corresponding {@link FreeList} bucket. Pages get into the bucket through evicting all data entries one by one.
+     * Higher load and contention require larger pool size.
+     */
+    private int emptyPagesPoolSize = 100;
+
     /**
      * @param name Unique name of MemoryPolicy.
      */
@@ -83,4 +97,60 @@ public final class MemoryPolicyConfiguration implements Serializable {
 
         return this;
     }
+
+    /**
+     * Gets data page eviction mode.
+     */
+    public DataPageEvictionMode getPageEvictionMode() {
+        return pageEvictionMode;
+    }
+
+    /**
+     * Sets data page eviction mode.
+     *
+     * @param evictionMode Eviction mode.
+     */
+    public MemoryPolicyConfiguration setPageEvictionMode(DataPageEvictionMode evictionMode) {
+        pageEvictionMode = evictionMode;
+
+        return this;
+    }
+
+    /**
+     * Gets data page eviction threshold.
+     *
+     * @return Data page eviction threshold.
+     */
+    public double getEvictionThreshold() {
+        return evictionThreshold;
+    }
+
+    /**
+     * Sets data page eviction threshold.
+     *
+     * @param evictionThreshold Eviction threshold.
+     */
+    public MemoryPolicyConfiguration setEvictionThreshold(double evictionThreshold) {
+        this.evictionThreshold = evictionThreshold;
+
+        return this;
+    }
+
+    /**
+     * Gets empty pages pool size.
+     */
+    public int getEmptyPagesPoolSize() {
+        return emptyPagesPoolSize;
+    }
+
+    /**
+     * Sets empty pages pool size.
+     *
+     * @param emptyPagesPoolSize Empty pages pool size.
+     */
+    public MemoryPolicyConfiguration setEmptyPagesPoolSize(int emptyPagesPoolSize) {
+        this.emptyPagesPoolSize = emptyPagesPoolSize;
+
+        return this;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageSupport.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageSupport.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageSupport.java
index 8076f28..0f39058 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageSupport.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageSupport.java
@@ -53,6 +53,16 @@ public interface PageSupport {
     public long readLock(int cacheId, long pageId, long page);
 
     /**
+     * Obtains read lock without checking page tag.
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @return Pointer for reading the page.
+     */
+    public long readLockForce(int cacheId, long pageId, long page);
+
+    /**
      * Releases locked page.
      *
      * @param cacheId Cache ID.

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageMemoryNoStoreImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageMemoryNoStoreImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageMemoryNoStoreImpl.java
index f24113c..7134cff 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageMemoryNoStoreImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageMemoryNoStoreImpl.java
@@ -416,6 +416,14 @@ public class PageMemoryNoStoreImpl implements PageMemory {
     }
 
     /** {@inheritDoc} */
+    public long readLockForce(int cacheId, long pageId, long page) {
+        if (rwLock.readLock(page + LOCK_OFFSET, -1))
+            return page + PAGE_OVERHEAD;
+
+        return 0L;
+    }
+
+    /** {@inheritDoc} */
     @Override public void readUnlock(int cacheId, long pageId, long page) {
         rwLock.readUnlock(page + LOCK_OFFSET);
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageNoStoreImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageNoStoreImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageNoStoreImpl.java
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
index 99df39d..f8e9f32 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
@@ -27,10 +27,6 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.Nullable;
 
 /**
- * TODO GG-11140.
- *
- * Temporary implementation, ignores configured EvictionPolicy, evictions to be reconsidered as
- * part of GG-11140.
  *
  */
 public class CacheOffheapEvictionManager extends GridCacheManagerAdapter implements CacheEvictionManager {
@@ -51,7 +47,7 @@ public class CacheOffheapEvictionManager extends GridCacheManagerAdapter impleme
                 return;
             }
 
-            boolean evicted = e.evictInternal(GridCacheVersionManager.EVICT_VER, null);
+            boolean evicted = e.evictInternal(GridCacheVersionManager.EVICT_VER, null, false);
 
             if (evicted)
                 cctx.cache().removeEntry(e);

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index 9a6ff11..d791b7c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -2073,6 +2073,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
                                             GridCacheEntryEx entry = null;
 
                                             try {
+                                                ctx.shared().database().ensureFreeSpace(ctx.memoryPolicy());
+
                                                 entry = entryEx(key);
 
                                                 entry.unswap();

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
index 99f9744..2066342 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryEx.java
@@ -213,11 +213,12 @@ public interface GridCacheEntryEx {
     /**
      * @param obsoleteVer Version for eviction.
      * @param filter Optional filter.
+     * @param evictOffheap Evict offheap value flag.
      * @return {@code True} if entry could be evicted.
      * @throws IgniteCheckedException In case of error.
      */
-    public boolean evictInternal(GridCacheVersion obsoleteVer, @Nullable CacheEntryPredicate[] filter)
-        throws IgniteCheckedException;
+    public boolean evictInternal(GridCacheVersion obsoleteVer, @Nullable CacheEntryPredicate[] filter,
+        boolean evictOffheap) throws IgniteCheckedException;
 
     /**
      * Evicts entry when batch evict is performed. When called, does not write entry data to swap, but instead

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEvictionManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEvictionManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEvictionManager.java
index 26f37a7..0deae07 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEvictionManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEvictionManager.java
@@ -135,7 +135,7 @@ public class GridCacheEvictionManager extends GridCacheManagerAdapter implements
 
         boolean hasVal = recordable && entry.hasValue();
 
-        boolean evicted = entry.evictInternal(obsoleteVer, filter);
+        boolean evicted = entry.evictInternal(obsoleteVer, filter, false);
 
         if (evicted) {
             // Remove manually evicted entry from policy.

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
index 7fad9f5..9e2cd70 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
@@ -43,6 +43,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheUpdateAtomicResult.UpdateOutcome;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.database.MemoryPolicy;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicAbstractUpdateFuture;
@@ -809,6 +810,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
         boolean touch = false;
 
         try {
+            ensureFreeSpace();
+
             synchronized (this) {
                 long ttl = ttlExtras();
 
@@ -908,6 +911,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
 
         long updateCntr0;
 
+        ensureFreeSpace();
+
         synchronized (this) {
             checkObsolete();
 
@@ -1641,6 +1646,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
 
         AtomicCacheUpdateClosure c;
 
+        if (!primary && !isNear())
+            ensureFreeSpace();
+
         synchronized (this) {
             checkObsolete();
 
@@ -2567,6 +2575,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
         GridDrType drType,
         boolean fromStore
     ) throws IgniteCheckedException, GridCacheEntryRemovedException {
+        ensureFreeSpace();
+
         synchronized (this) {
             checkObsolete();
 
@@ -3378,6 +3388,16 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
     }
 
     /**
+     * Evicts necessary number of data pages if per-page eviction is configured in current {@link MemoryPolicy}.
+     */
+    private void ensureFreeSpace() throws IgniteCheckedException {
+        // Deadlock alert: evicting data page causes removing (and locking) all entries on the page one by one.
+        assert !Thread.holdsLock(this);
+
+        cctx.shared().database().ensureFreeSpace(cctx.memoryPolicy());
+    }
+
+    /**
      * @return Entry which holds key, value and version.
      */
     private synchronized <K, V> CacheEntryImplEx<K, V> wrapVersionedWithValue() {
@@ -3387,8 +3407,12 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
     }
 
     /** {@inheritDoc} */
-    @Override public boolean evictInternal(GridCacheVersion obsoleteVer, @Nullable CacheEntryPredicate[] filter)
+    @Override public boolean evictInternal(
+        GridCacheVersion obsoleteVer,
+        @Nullable CacheEntryPredicate[] filter,
+        boolean evictOffheap)
         throws IgniteCheckedException {
+
         boolean marked = false;
 
         try {
@@ -3411,6 +3435,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                         // Nullify value after swap.
                         value(null);
 
+                        if (evictOffheap)
+                            removeValue();
+
                         marked = true;
 
                         return true;
@@ -3451,6 +3478,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                             // Nullify value after swap.
                             value(null);
 
+                            if (evictOffheap)
+                                removeValue();
+
                             marked = true;
 
                             return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
index e022e57..73edbe1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
@@ -28,6 +28,7 @@ import javax.cache.Cache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.DataPageEvictionMode;
 import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.pagemem.FullPageId;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
@@ -37,7 +38,6 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRowAdapter;
 import org.apache.ignite.internal.processors.cache.database.CacheSearchRow;
-import org.apache.ignite.internal.processors.cache.database.IgniteCacheDatabaseSharedManager;
 import org.apache.ignite.internal.processors.cache.database.RootPage;
 import org.apache.ignite.internal.processors.cache.database.RowStore;
 import org.apache.ignite.internal.processors.cache.database.freelist.FreeList;
@@ -965,8 +965,12 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             CacheObject val,
             GridCacheVersion ver,
             long expireTime,
-            @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
-            DataRow dataRow = new DataRow(key, val, ver, partId, expireTime);
+            @Nullable CacheDataRow oldRow) throws IgniteCheckedException
+        {
+            int cacheId = cctx.memoryPolicy().config().getPageEvictionMode() == DataPageEvictionMode.DISABLED ?
+                0 : cctx.cacheId();
+
+            DataRow dataRow = new DataRow(key, val, ver, partId, expireTime, cacheId);
 
             if (canUpdateOldRow(oldRow, dataRow) && rowStore.updateRow(oldRow.link(), dataRow))
                 dataRow.link(oldRow.link());
@@ -997,7 +1001,10 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                 throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
 
             try {
-                DataRow dataRow = new DataRow(key, val, ver, p, expireTime);
+                int cacheId = cctx.memoryPolicy().config().getPageEvictionMode() != DataPageEvictionMode.DISABLED ?
+                    cctx.cacheId() : 0;
+
+                DataRow dataRow = new DataRow(key, val, ver, p, expireTime, cacheId);
 
                 CacheObjectContext coCtx = cctx.cacheObjectContext();
 
@@ -1140,9 +1147,12 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             CacheDataRow row = dataTree.findOne(new SearchRow(key), CacheDataRowAdapter.RowData.NO_KEY);
 
-            if (row != null)
+            if (row != null) {
                 row.key(key);
 
+                cctx.memoryPolicy().evictionTracker().touchPage(row.link());
+            }
+
             return row;
         }
 
@@ -1345,7 +1355,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
          * @param part Partition.
          * @param expireTime Expire time.
          */
-        DataRow(KeyCacheObject key, CacheObject val, GridCacheVersion ver, int part, long expireTime) {
+        DataRow(KeyCacheObject key, CacheObject val, GridCacheVersion ver, int part, long expireTime, int cacheId) {
             super(0);
 
             this.hash = key.hashCode();
@@ -1354,6 +1364,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             this.ver = ver;
             this.part = part;
             this.expireTime = expireTime;
+            this.cacheId = cacheId;
         }
 
         /** {@inheritDoc} */
@@ -1473,6 +1484,9 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                     if (data.nextLink() == 0) {
                         long addr = pageAddr + data.offset();
 
+                        if (cctx.memoryPolicy().config().getPageEvictionMode() != DataPageEvictionMode.DISABLED)
+                            addr += 4; // Skip cache id.
+
                         final int len = PageUtils.getInt(addr, 0);
 
                         int lenCmp = Integer.compare(len, bytes.length);
@@ -1672,6 +1686,14 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         @Override public int getHash(long pageAddr, int idx) {
             return PageUtils.getInt(pageAddr, offset(idx) + 8);
         }
+
+        /** {@inheritDoc} */
+        @Override public void visit(long pageAddr, IgniteInClosure<CacheSearchRow> c) {
+            int cnt = getCount(pageAddr);
+
+            for (int i = 0; i < cnt; i++)
+                c.apply(new CacheDataRowAdapter(getLink(pageAddr, i)));
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
index cc26b21..e0076d5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
@@ -36,6 +36,11 @@ public interface CacheDataRow extends CacheSearchRow {
     public GridCacheVersion version();
 
     /**
+     * @return Cache id. Stored only if memory policy with configured per-page eviction is used.
+     */
+    public int cacheId();
+
+    /**
      * @return Expire time.
      */
     public long expireTime();

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
index eca59d6..afeada5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
@@ -19,12 +19,14 @@ package org.apache.ignite.internal.processors.cache.database;
 
 import java.nio.ByteBuffer;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.DataPageEvictionMode;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.PageUtils;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.IncompleteCacheObject;
 import org.apache.ignite.internal.processors.cache.IncompleteObject;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
@@ -36,6 +38,7 @@ import org.apache.ignite.internal.util.tostring.GridToStringExclude;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.pagemem.PageIdUtils.itemId;
 import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
@@ -63,6 +66,10 @@ public class CacheDataRowAdapter implements CacheDataRow {
     @GridToStringInclude
     protected GridCacheVersion ver;
 
+    /** */
+    @GridToStringInclude
+    protected int cacheId;
+
     /**
      * @param link Link.
      */
@@ -92,14 +99,35 @@ public class CacheDataRowAdapter implements CacheDataRow {
      * @throws IgniteCheckedException If failed.
      */
     public final void initFromLink(GridCacheContext<?, ?> cctx, RowData rowData) throws IgniteCheckedException {
-        assert cctx != null : "cctx";
+        initFromLink(cctx, cctx.shared(), cctx.memoryPolicy().pageMemory(), rowData);
+    }
+
+    /**
+     * Read row from data pages.
+     * Can be called with cctx == null, if cache instance is unknown, but its ID is stored in the data row.
+     *
+     * @param cctx Cctx.
+     * @param sharedCtx Shared context.
+     * @param pageMem Page memory.
+     * @param rowData Row data.
+     */
+    public final void initFromLink(
+        @Nullable GridCacheContext<?, ?> cctx,
+        GridCacheSharedContext<?, ?> sharedCtx,
+        PageMemory pageMem,
+        RowData rowData)
+        throws IgniteCheckedException {
         assert link != 0 : "link";
         assert key == null : "key";
 
-        final CacheObjectContext coctx = cctx.cacheObjectContext();
-        final PageMemory pageMem = cctx.memoryPolicy().pageMemory();
+        CacheObjectContext coctx = null;
+
+        if (cctx != null) {
+            cacheId = cctx.memoryPolicy().config().getPageEvictionMode() == DataPageEvictionMode.DISABLED ?
+                cctx.cacheId() : 0; // Force cacheId reading for evictable memory policies.
 
-        final int cacheId = cctx.cacheId();
+            coctx = cctx.cacheObjectContext();
+        }
 
         long nextLink = link;
         IncompleteObject<?> incomplete = null;
@@ -126,7 +154,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
                     if (first) {
                         if (nextLink == 0) {
                             // Fast path for a single page row.
-                            readFullRow(coctx, pageAddr + data.offset(), rowData);
+                            readFullRow(sharedCtx, coctx, pageAddr + data.offset(), rowData);
 
                             return;
                         }
@@ -141,7 +169,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
 
                     boolean keyOnly = rowData == RowData.KEY_ONLY;
 
-                    incomplete = readFragment(coctx, buf, keyOnly, incomplete);
+                    incomplete = readFragment(sharedCtx, coctx, buf, keyOnly, incomplete);
 
                     if (keyOnly && key != null)
                         return;
@@ -168,11 +196,24 @@ public class CacheDataRowAdapter implements CacheDataRow {
      * @return Read object.
      */
     private IncompleteObject<?> readFragment(
+        GridCacheSharedContext<?, ?> sharedCtx,
         CacheObjectContext coctx,
         ByteBuffer buf,
         boolean keyOnly,
         IncompleteObject<?> incomplete
     ) throws IgniteCheckedException {
+        if (cacheId == 0) {
+            incomplete = readIncompleteCacheId(buf, incomplete);
+
+            if (cacheId == 0)
+                return incomplete;
+
+            incomplete = null;
+        }
+
+        if (coctx == null)
+            coctx = sharedCtx.cacheContext(cacheId).cacheObjectContext();
+
         // Read key.
         if (key == null) {
             incomplete = readIncompleteKey(coctx, buf, (IncompleteCacheObject)incomplete);
@@ -215,9 +256,23 @@ public class CacheDataRowAdapter implements CacheDataRow {
      * @param rowData Required row data.
      * @throws IgniteCheckedException If failed.
      */
-    private void readFullRow(CacheObjectContext coctx, long addr, RowData rowData) throws IgniteCheckedException {
+    private void readFullRow(
+        GridCacheSharedContext<?, ?> sharedCtx,
+        CacheObjectContext coctx,
+        long addr,
+        RowData rowData)
+        throws IgniteCheckedException {
         int off = 0;
 
+        if (cacheId == 0) {
+            cacheId = PageUtils.getInt(addr, off);
+
+            off += 4;
+        }
+
+        if (coctx == null)
+            coctx = sharedCtx.cacheContext(cacheId).cacheObjectContext();
+
         int len = PageUtils.getInt(addr, off);
         off += 4;
 
@@ -255,6 +310,44 @@ public class CacheDataRowAdapter implements CacheDataRow {
     }
 
     /**
+     * @param buf Buffer.
+     * @param incomplete Incomplete.
+     */
+    private IncompleteObject<?> readIncompleteCacheId(
+        ByteBuffer buf,
+        IncompleteObject<?> incomplete
+    ) {
+        if (incomplete == null) {
+            int remaining = buf.remaining();
+
+            if (remaining == 0)
+                return null;
+
+            int size = 4;
+
+            if (remaining >= size) {
+                cacheId = buf.getInt();
+
+                return null;
+            }
+
+            incomplete = new IncompleteObject<>(new byte[size]);
+        }
+
+        incomplete.readData(buf);
+
+        if (incomplete.isReady()) {
+            final ByteBuffer timeBuf = ByteBuffer.wrap(incomplete.data());
+
+            timeBuf.order(buf.order());
+
+            cacheId = timeBuf.getInt();
+        }
+
+        return incomplete;
+    }
+
+    /**
      * @param coctx Cache object context.
      * @param buf Buffer.
      * @param incomplete Incomplete object.
@@ -313,7 +406,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
     private IncompleteObject<?> readIncompleteExpireTime(
         ByteBuffer buf,
         IncompleteObject<?> incomplete
-    ) throws IgniteCheckedException {
+    ) {
         if (incomplete == null) {
             int remaining = buf.remaining();
 
@@ -414,13 +507,18 @@ public class CacheDataRowAdapter implements CacheDataRow {
     /**
      * @param key Key.
      */
-    public void key(KeyCacheObject key) {
+    @Override public void key(KeyCacheObject key) {
         assert key != null;
 
         this.key = key;
     }
 
     /** {@inheritDoc} */
+    @Override public int cacheId() {
+        return cacheId;
+    }
+
+    /** {@inheritDoc} */
     @Override public CacheObject value() {
         assert val != null : "Value is not ready: " + this;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
index d61130b..2d2295c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
@@ -29,6 +29,7 @@ import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.MemoryMetrics;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.DataPageEvictionMode;
 import org.apache.ignite.configuration.MemoryConfiguration;
 import org.apache.ignite.configuration.MemoryPolicyConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
@@ -40,7 +41,13 @@ import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.snapshot.StartFullSnapshotAckDiscoveryMessage;
 import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedManagerAdapter;
+import org.apache.ignite.internal.processors.cache.database.evict.FairFifoPageEvictionTracker;
+import org.apache.ignite.internal.processors.cache.database.evict.NoOpPageEvictionTracker;
+import org.apache.ignite.internal.processors.cache.database.evict.PageEvictionTracker;
+import org.apache.ignite.internal.processors.cache.database.evict.Random2LruPageEvictionTracker;
+import org.apache.ignite.internal.processors.cache.database.evict.RandomLruPageEvictionTracker;
 import org.apache.ignite.internal.processors.cache.database.freelist.FreeList;
 import org.apache.ignite.internal.processors.cache.database.freelist.FreeListImpl;
 import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList;
@@ -102,7 +109,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
 
             initPageMemoryPolicies(dbCfg);
 
-            startPageMemoryPools();
+            startMemoryPolicies();
 
             initPageMemoryDataStructures(dbCfg);
         }
@@ -123,8 +130,8 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
 
             FreeListImpl freeList = new FreeListImpl(0,
                     cctx.igniteInstanceName(),
-                    memPlc.pageMemory(),
                     memMetrics,
+                    memPlc,
                     null,
                     cctx.wal(),
                     0L,
@@ -148,9 +155,12 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
     /**
      *
      */
-    private void startPageMemoryPools() {
-        for (MemoryPolicy memPlc : memPlcMap.values())
+    private void startMemoryPolicies() {
+        for (MemoryPolicy memPlc : memPlcMap.values()) {
             memPlc.pageMemory().start();
+
+            memPlc.evictionTracker().start();
+        }
     }
 
     /**
@@ -205,9 +215,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
             for (MemoryPolicyConfiguration memPlcCfg : memPlcsCfgs) {
                 MemoryMetricsImpl memMetrics = new MemoryMetricsImpl(memPlcCfg);
 
-                PageMemory pageMem = initMemory(dbCfg, memPlcCfg, memMetrics);
-
-                MemoryPolicy memPlc = new MemoryPolicy(pageMem, memMetrics, memPlcCfg);
+                MemoryPolicy memPlc = initMemory(dbCfg, memPlcCfg, memMetrics);
 
                 memPlcMap.put(memPlcCfg.getName(), memPlc);
 
@@ -222,7 +230,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
 
         MemoryMetricsImpl sysMemMetrics = new MemoryMetricsImpl(sysPlcCfg);
 
-        memPlcMap.put(SYSTEM_MEMORY_POLICY_NAME, new MemoryPolicy(initMemory(dbCfg, sysPlcCfg, sysMemMetrics), sysMemMetrics, sysPlcCfg));
+        memPlcMap.put(SYSTEM_MEMORY_POLICY_NAME, initMemory(dbCfg, sysPlcCfg, sysMemMetrics));
 
         memMetricsMap.put(SYSTEM_MEMORY_POLICY_NAME, sysMemMetrics);
     }
@@ -253,9 +261,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
      * @param memMetrics MemoryMetrics instance.
      */
     private MemoryPolicy createDefaultMemoryPolicy(MemoryConfiguration dbCfg, MemoryPolicyConfiguration memPlcCfg, MemoryMetricsImpl memMetrics) {
-        PageMemory pageMem = initMemory(dbCfg, memPlcCfg, memMetrics);
-
-        return new MemoryPolicy(pageMem, memMetrics, memPlcCfg);
+        return initMemory(dbCfg, memPlcCfg, memMetrics);
     }
 
     /**
@@ -285,6 +291,8 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
                 checkPolicyName(plcCfg.getName(), plcNames);
 
                 checkPolicySize(plcCfg);
+
+                checkPolicyEvictionProperties(plcCfg, dbCfg);
             }
         }
 
@@ -307,6 +315,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
 
     /**
      * @param plcCfg MemoryPolicyConfiguration to validate.
+     * @throws IgniteCheckedException If config is invalid.
      */
     private static void checkPolicySize(MemoryPolicyConfiguration plcCfg) throws IgniteCheckedException {
         if (plcCfg.getSize() < MIN_PAGE_MEMORY_SIZE)
@@ -314,8 +323,35 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
     }
 
     /**
+     * @param plcCfg MemoryPolicyConfiguration to validate.
+     * @param dbCfg Memory configuration.
+     * @throws IgniteCheckedException If config is invalid.
+     */
+    protected void checkPolicyEvictionProperties(MemoryPolicyConfiguration plcCfg, MemoryConfiguration dbCfg)
+        throws IgniteCheckedException {
+        if (plcCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED)
+            return;
+
+        if (plcCfg.getEvictionThreshold() < 0.5 || plcCfg.getEvictionThreshold() > 0.999) {
+            throw new IgniteCheckedException("Page eviction threshold must be between 0.5 and 0.999: " +
+                plcCfg.getName());
+        }
+
+        if (plcCfg.getEmptyPagesPoolSize() <= 10)
+            throw new IgniteCheckedException("Evicted pages pool size should be greater than 10: " + plcCfg.getName());
+
+        long maxPoolSize = plcCfg.getSize() / dbCfg.getPageSize() / 10;
+
+        if (plcCfg.getEmptyPagesPoolSize() >= maxPoolSize) {
+            throw new IgniteCheckedException("Evicted pages pool size should be lesser than " + maxPoolSize +
+                ": " + plcCfg.getName());
+        }
+    }
+
+    /**
      * @param plcName MemoryPolicy name to validate.
      * @param observedNames Names of MemoryPolicies observed before.
+     * @throws IgniteCheckedException If config is invalid.
      */
     private static void checkPolicyName(String plcName, Set<String> observedNames) throws IgniteCheckedException {
         if (plcName == null || plcName.isEmpty())
@@ -406,8 +442,11 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
     /** {@inheritDoc} */
     @Override protected void stop0(boolean cancel) {
         if (memPlcMap != null) {
-            for (MemoryPolicy memPlc : memPlcMap.values())
+            for (MemoryPolicy memPlc : memPlcMap.values()) {
                 memPlc.pageMemory().stop();
+
+                memPlc.evictionTracker().stop();
+            }
         }
     }
 
@@ -512,12 +551,49 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
     }
 
     /**
+     * See {@link GridCacheMapEntry#ensureFreeSpace()}
+     *
+     * @param memPlc Memory policy.
+     */
+    public void ensureFreeSpace(MemoryPolicy memPlc) throws IgniteCheckedException {
+        if (memPlc == null)
+            return;
+
+        MemoryPolicyConfiguration plcCfg = memPlc.config();
+
+        if (plcCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED)
+            return;
+
+        long memorySize = plcCfg.getSize();
+
+        PageMemory pageMem = memPlc.pageMemory();
+
+        int sysPageSize = pageMem.systemPageSize();
+
+        FreeListImpl freeListImpl = freeListMap.get(plcCfg.getName());
+
+        for (;;) {
+            long allocatedPagesCnt = pageMem.loadedPages();
+
+            int emptyDataPagesCnt = freeListImpl.emptyDataPages();
+
+            boolean shouldEvict = allocatedPagesCnt > (memorySize / sysPageSize * plcCfg.getEvictionThreshold()) &&
+                emptyDataPagesCnt < plcCfg.getEmptyPagesPoolSize();
+
+            if (shouldEvict)
+                memPlc.evictionTracker().evictDataPage();
+            else
+                break;
+        }
+    }
+
+    /**
      * @param dbCfg memory configuration with common parameters.
      * @param plc memory policy with PageMemory specific parameters.
      * @param memMetrics {@link MemoryMetrics} object to collect memory usage metrics.
-     * @return Page memory instance.
+     * @return Memory policy instance.
      */
-    private PageMemory initMemory(MemoryConfiguration dbCfg, MemoryPolicyConfiguration plc, MemoryMetricsImpl memMetrics) {
+    private MemoryPolicy initMemory(MemoryConfiguration dbCfg, MemoryPolicyConfiguration plc, MemoryMetricsImpl memMetrics) {
         long[] sizes = calculateFragmentSizes(
                 dbCfg.getConcurrencyLevel(),
                 plc.getSize());
@@ -532,7 +608,27 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
                 true,
                 sizes);
 
-        return createPageMemory(memProvider, dbCfg.getPageSize(), memMetrics);
+        PageMemory pageMem = createPageMemory(memProvider, dbCfg.getPageSize(), memMetrics);
+
+        return new MemoryPolicy(pageMem, plc, memMetrics, createPageEvictionTracker(plc, pageMem));
+    }
+
+    /**
+     * @param plc Memory Policy Configuration.
+     * @param pageMem Page memory.
+     */
+    private PageEvictionTracker createPageEvictionTracker(MemoryPolicyConfiguration plc, PageMemory pageMem) {
+        if (Boolean.getBoolean("override.fair.fifo.page.eviction.tracker"))
+            return new FairFifoPageEvictionTracker(pageMem, plc, cctx);
+
+        switch (plc.getPageEvictionMode()) {
+            case RANDOM_LRU:
+                return new RandomLruPageEvictionTracker(pageMem, plc, cctx);
+            case RANDOM_2_LRU:
+                return new Random2LruPageEvictionTracker(pageMem, plc, cctx);
+            default:
+                return new NoOpPageEvictionTracker();
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryPolicy.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryPolicy.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryPolicy.java
index be23b38..90e5ac1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryPolicy.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryPolicy.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.cache.database;
 import org.apache.ignite.MemoryMetrics;
 import org.apache.ignite.configuration.MemoryPolicyConfiguration;
 import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.database.evict.PageEvictionTracker;
 
 /**
  * Memory policy provides access to objects configured with {@link MemoryPolicyConfiguration} configuration.
@@ -33,15 +34,24 @@ public class MemoryPolicy {
     /** */
     private final MemoryPolicyConfiguration cfg;
 
+    /** */
+    private final PageEvictionTracker evictionTracker;
+
     /**
      * @param pageMem PageMemory instance.
      * @param memMetrics MemoryMetrics instance.
      * @param cfg Configuration of given MemoryPolicy.
+     * @param evictionTracker Eviction tracker.
      */
-    public MemoryPolicy(PageMemory pageMem, MemoryMetrics memMetrics, MemoryPolicyConfiguration cfg) {
+    public MemoryPolicy(
+        PageMemory pageMem,
+        MemoryPolicyConfiguration cfg,
+        MemoryMetrics memMetrics,
+        PageEvictionTracker evictionTracker) {
         this.pageMem = pageMem;
         this.memMetrics = memMetrics;
         this.cfg = cfg;
+        this.evictionTracker = evictionTracker;
     }
 
     /**
@@ -64,4 +74,11 @@ public class MemoryPolicy {
     public MemoryMetrics memoryMetrics() {
         return memMetrics;
     }
+
+    /**
+     *
+     */
+    public PageEvictionTracker evictionTracker() {
+        return evictionTracker;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/FairFifoPageEvictionTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/FairFifoPageEvictionTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/FairFifoPageEvictionTracker.java
new file mode 100644
index 0000000..8847013
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/FairFifoPageEvictionTracker.java
@@ -0,0 +1,74 @@
+/*
+* 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.database.evict;
+
+import java.util.LinkedList;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.configuration.MemoryPolicyConfiguration;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+
+/**
+ * On-heap FIFO page eviction tracker. Only for test purposes.
+ */
+public class FairFifoPageEvictionTracker extends PageAbstractEvictionTracker {
+    /** Page usage deque. */
+    private final LinkedList<Integer> pageUsageList = new LinkedList<>();
+
+    /**
+     * @param pageMem Page memory.
+     * @param plcCfg Memory policy configuration.
+     * @param sharedCtx Shared context.
+     */
+    public FairFifoPageEvictionTracker(PageMemory pageMem,
+        MemoryPolicyConfiguration plcCfg,
+        GridCacheSharedContext sharedCtx) {
+        super(pageMem, plcCfg, sharedCtx);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() throws IgniteException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized void touchPage(long pageId) throws IgniteCheckedException {
+        pageUsageList.addLast(PageIdUtils.pageIndex(pageId));
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized void evictDataPage() throws IgniteCheckedException {
+        evictDataPage(pageUsageList.pollFirst());
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized void forgetPage(long pageId) throws IgniteCheckedException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected synchronized boolean checkTouch(long pageId) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/NoOpPageEvictionTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/NoOpPageEvictionTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/NoOpPageEvictionTracker.java
new file mode 100644
index 0000000..ba466bf
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/NoOpPageEvictionTracker.java
@@ -0,0 +1,50 @@
+/*
+* 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.database.evict;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+
+/**
+ *
+ */
+public class NoOpPageEvictionTracker implements PageEvictionTracker {
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() throws IgniteException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void touchPage(long pageId) throws IgniteCheckedException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void evictDataPage() throws IgniteCheckedException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void forgetPage(long pageId) throws IgniteCheckedException {
+        // No-op.
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageAbstractEvictionTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageAbstractEvictionTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageAbstractEvictionTracker.java
new file mode 100644
index 0000000..88de545
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageAbstractEvictionTracker.java
@@ -0,0 +1,243 @@
+/*
+* 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.database.evict;
+
+import java.util.List;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.MemoryConfiguration;
+import org.apache.ignite.configuration.MemoryPolicyConfiguration;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.processors.cache.database.CacheDataRowAdapter;
+import org.apache.ignite.internal.processors.cache.database.tree.io.DataPageIO;
+import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersionManager;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ *
+ */
+public abstract class PageAbstractEvictionTracker implements PageEvictionTracker {
+    /** This number of least significant bits is dropped from timestamp. */
+    private static final int COMPACT_TS_SHIFT = 8; // Enough if grid works for less than 17 years.
+
+    /** Millis in day. */
+    private final static int DAY = 24 * 60 * 60 * 1000;
+
+    /** Page memory. */
+    protected final PageMemory pageMem;
+
+    /** Tracking array size. */
+    final int trackingSize;
+
+    /** Base compact timestamp. */
+    private final long baseCompactTs;
+
+    /** Shared context. */
+    private final GridCacheSharedContext sharedCtx;
+
+    /* TODO: IGNITE-4921: Will be removed after segments refactoring >>>> */
+    protected final int segBits;
+    protected final int idxBits;
+    protected final int segMask;
+    protected final int idxMask;
+    protected final int segmentPageCount;
+    /* <<<< */
+
+    /**
+     * @param pageMem Page memory.
+     * @param plcCfg Memory policy configuration.
+     * @param sharedCtx Shared context.
+     */
+    PageAbstractEvictionTracker(
+        PageMemory pageMem,
+        MemoryPolicyConfiguration plcCfg,
+        GridCacheSharedContext sharedCtx
+    ) {
+        this.pageMem = pageMem;
+
+        this.sharedCtx = sharedCtx;
+
+        MemoryConfiguration memCfg = sharedCtx.kernalContext().config().getMemoryConfiguration();
+
+        /* TODO: IGNITE-4921: Will be removed after segments refactoring >>>> */
+        int concurrencyLevel = memCfg.getConcurrencyLevel();
+
+        if (concurrencyLevel < 1)
+            concurrencyLevel = Runtime.getRuntime().availableProcessors();
+
+        int pageSize = memCfg.getPageSize();
+
+        long segSize = plcCfg.getSize() / concurrencyLevel;
+
+        if (segSize < 1024 * 1024)
+            segSize = 1024 * 1024;
+
+        segmentPageCount = (int)(segSize / pageSize);
+
+        segBits = Integer.SIZE - Integer.numberOfLeadingZeros(concurrencyLevel - 1);
+
+        idxBits = PageIdUtils.PAGE_IDX_SIZE - segBits;
+
+        segMask = ~(-1 << segBits);
+
+        idxMask = ~(-1 << idxBits);
+        /* <<<< */
+
+        trackingSize = segmentPageCount << segBits;
+
+        baseCompactTs = (U.currentTimeMillis() - DAY) >> COMPACT_TS_SHIFT;
+        // We subtract day to avoid fail in case of daylight shift or timezone change.
+    }
+
+    /**
+     * @param pageIdx Page index.
+     * @return true if at least one data row has been evicted
+     * @throws IgniteCheckedException If failed.
+     */
+    final boolean evictDataPage(int pageIdx) throws IgniteCheckedException {
+        long fakePageId = PageIdUtils.pageId(0, (byte)0, pageIdx);
+
+        long page = pageMem.acquirePage(0, fakePageId);
+
+        List<CacheDataRowAdapter> rowsToEvict;
+
+        try {
+            long pageAddr = pageMem.readLockForce(0, fakePageId, page);
+
+            try {
+                if (PageIO.getType(pageAddr) != PageIO.T_DATA)
+                    return false; // Can't evict: page has been recycled into non-data page.
+
+                DataPageIO io = DataPageIO.VERSIONS.forPage(pageAddr);
+
+                long realPageId = PageIO.getPageId(pageAddr);
+
+                if (!checkTouch(realPageId))
+                    return false; // Can't evict: another thread concurrently invoked forgetPage()
+
+                rowsToEvict = io.forAllItems(pageAddr, new DataPageIO.CC<CacheDataRowAdapter>() {
+                    @Override public CacheDataRowAdapter apply(long link) throws IgniteCheckedException {
+                        CacheDataRowAdapter row = new CacheDataRowAdapter(link);
+
+                        row.initFromLink(null, sharedCtx, pageMem, CacheDataRowAdapter.RowData.KEY_ONLY);
+
+                        assert row.cacheId() != 0 : "Cache ID should be stored in rows of evictable cache";
+
+                        return row;
+                    }
+                });
+            }
+            finally {
+                pageMem.readUnlock(0, fakePageId, page);
+            }
+        }
+        finally {
+            pageMem.releasePage(0, fakePageId, page);
+        }
+
+        boolean evictionDone = false;
+
+        for (CacheDataRowAdapter dataRow : rowsToEvict) {
+            GridCacheContext<?, ?> cacheCtx = sharedCtx.cacheContext(dataRow.cacheId());
+
+            if (!cacheCtx.userCache())
+                continue;
+
+            GridCacheEntryEx entryEx = cacheCtx.cache().entryEx(dataRow.key());
+
+            evictionDone |= entryEx.evictInternal(GridCacheVersionManager.EVICT_VER, null, true);
+        }
+
+        return evictionDone;
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @return true if page was touched at least once.
+     */
+    protected abstract boolean checkTouch(long pageId);
+
+    /**
+     * @param epochMilli Time millis.
+     * @return Compact timestamp. Comparable and fits in 4 bytes.
+     */
+    final long compactTimestamp(long epochMilli) {
+        return (epochMilli >> COMPACT_TS_SHIFT) - baseCompactTs;
+    }
+
+    /**
+     * Resolves position in tracking array by page index.
+     *
+     * @param pageIdx Page index.
+     * @return Position of page in tracking array.
+     */
+    int trackingIdx(int pageIdx) {
+        int inSegmentPageIdx = inSegmentPageIdx(pageIdx);
+
+        assert inSegmentPageIdx < segmentPageCount : inSegmentPageIdx;
+
+        int trackingIdx = segmentIdx(pageIdx) * segmentPageCount + inSegmentPageIdx;
+
+        assert trackingIdx < trackingSize : trackingIdx;
+
+        return trackingIdx;
+    }
+
+    /**
+     * Reverse of {@link #trackingIdx(int)}.
+     *
+     * @param trackingIdx Tracking index.
+     * @return Page index.
+     */
+    int pageIdx(int trackingIdx) {
+        assert trackingIdx < trackingSize;
+
+        long res = 0;
+
+        long segIdx = trackingIdx / segmentPageCount;
+        long pageIdx = trackingIdx % segmentPageCount;
+
+        res = (res << segBits) | (segIdx & segMask);
+        res = (res << idxBits) | (pageIdx & idxMask);
+
+        assert (res & (-1L << 32)) == 0 : res;
+
+        return (int)res;
+    }
+
+    /* TODO: IGNITE-4921: Will be removed after segments refactoring >>>> */
+    /**
+     * @param pageIdx Page index.
+     * @return Number of segment.
+     */
+    private int segmentIdx(int pageIdx) {
+        return (pageIdx >> idxBits) & segMask;
+    }
+
+    /**
+     * @param pageIdx Page index.
+     * @return Number of page inside segment.
+     */
+    private int inSegmentPageIdx(int pageIdx) {
+        return pageIdx & idxMask;
+    }
+    /* <<<< */
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageEvictionTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageEvictionTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageEvictionTracker.java
new file mode 100644
index 0000000..b13dcf8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/PageEvictionTracker.java
@@ -0,0 +1,52 @@
+/*
+* 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.database.evict;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.lifecycle.LifecycleAware;
+
+/**
+ * Entry point for per-page eviction. Accepts information about touching data pages,
+ * capable of evicting "the least needed" page (according to implemented eviction algorithm).
+ */
+public interface PageEvictionTracker extends LifecycleAware {
+    /**
+     * Call this method when data page is accessed.
+     *
+     * @param pageId Page id.
+     * @throws IgniteCheckedException In case of page memory error.
+     */
+    public void touchPage(long pageId) throws IgniteCheckedException;
+
+    /**
+     * Evicts one data page.
+     * In most cases, all entries will be removed from the page.
+     * Method guarantees removing at least one entry from "evicted" data page. Removing all entries may be
+     * not possible, as some of them can be used by active transactions.
+     *
+     * @throws IgniteCheckedException In case of page memory error.
+     */
+    public void evictDataPage() throws IgniteCheckedException;
+
+    /**
+     * Call this method when last entry is removed from data page.
+     *
+     * @param pageId Page id.
+     * @throws IgniteCheckedException In case of page memory error.
+     */
+    public void forgetPage(long pageId) throws IgniteCheckedException;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/Random2LruPageEvictionTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/Random2LruPageEvictionTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/Random2LruPageEvictionTracker.java
new file mode 100644
index 0000000..f0ad813
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/Random2LruPageEvictionTracker.java
@@ -0,0 +1,180 @@
+/*
+* 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.database.evict;
+
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.configuration.MemoryConfiguration;
+import org.apache.ignite.configuration.MemoryPolicyConfiguration;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.internal.util.typedef.internal.LT;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ *
+ */
+public class Random2LruPageEvictionTracker extends PageAbstractEvictionTracker {
+    /** Evict attempts limit. */
+    private static final int EVICT_ATTEMPTS_LIMIT = 30;
+
+    /** LRU Sample size. */
+    private static final int SAMPLE_SIZE = 5;
+
+    /** Maximum sample search spin count */
+    private static final int SAMPLE_SPIN_LIMIT = SAMPLE_SIZE * 1000;
+
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /** Tracking array ptr. */
+    private long trackingArrPtr;
+
+    /**
+     * @param pageMem Page memory.
+     * @param plcCfg Policy config.
+     * @param sharedCtx Shared context.
+     */
+    public Random2LruPageEvictionTracker(
+        PageMemory pageMem,
+        MemoryPolicyConfiguration plcCfg,
+        GridCacheSharedContext<?, ?> sharedCtx
+    ) {
+        super(pageMem, plcCfg, sharedCtx);
+
+        MemoryConfiguration memCfg = sharedCtx.kernalContext().config().getMemoryConfiguration();
+
+        assert plcCfg.getSize() / memCfg.getPageSize() < Integer.MAX_VALUE;
+
+        log = sharedCtx.logger(getClass());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteException {
+        trackingArrPtr = GridUnsafe.allocateMemory(trackingSize * 8);
+
+        GridUnsafe.setMemory(trackingArrPtr, trackingSize * 8, (byte)0);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() throws IgniteException {
+        GridUnsafe.freeMemory(trackingArrPtr);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void touchPage(long pageId) throws IgniteCheckedException {
+        int pageIdx = PageIdUtils.pageIndex(pageId);
+
+        long latestTs = compactTimestamp(U.currentTimeMillis());
+
+        assert latestTs >= 0 && latestTs < Integer.MAX_VALUE;
+
+        boolean success;
+
+        do {
+            int trackingIdx = trackingIdx(pageIdx);
+
+            int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8);
+
+            int secondTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8 + 4);
+
+            if (firstTs <= secondTs)
+                success = GridUnsafe.compareAndSwapInt(null, trackingArrPtr + trackingIdx * 8, firstTs, (int)latestTs);
+            else {
+                success = GridUnsafe.compareAndSwapInt(
+                    null, trackingArrPtr + trackingIdx * 8 + 4, secondTs, (int)latestTs);
+            }
+        } while (!success);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void evictDataPage() throws IgniteCheckedException {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+        int evictAttemptsCnt = 0;
+
+        while (evictAttemptsCnt < EVICT_ATTEMPTS_LIMIT) {
+            int lruTrackingIdx = -1;
+
+            int lruCompactTs = Integer.MAX_VALUE;
+
+            int dataPagesCnt = 0;
+
+            int sampleSpinCnt = 0;
+
+            while (dataPagesCnt < SAMPLE_SIZE) {
+                int trackingIdx = rnd.nextInt(trackingSize);
+
+                int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8);
+
+                int secondTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8 + 4);
+
+                int minTs = Math.min(firstTs, secondTs);
+
+                int maxTs = Math.max(firstTs, secondTs);
+
+                if (maxTs != 0) {
+                    // We chose data page with at least one touch.
+                    if (minTs < lruCompactTs) {
+                        lruTrackingIdx = trackingIdx;
+
+                        lruCompactTs = minTs;
+                    }
+
+                    dataPagesCnt++;
+                }
+
+                sampleSpinCnt++;
+
+                if (sampleSpinCnt > SAMPLE_SPIN_LIMIT) {
+                    LT.warn(log, "Too many attempts to choose data page: " + SAMPLE_SPIN_LIMIT);
+
+                    return;
+                }
+            }
+
+            if (evictDataPage(pageIdx(lruTrackingIdx)))
+                return;
+
+            evictAttemptsCnt++;
+        }
+
+        LT.warn(log, "Too many failed attempts to evict page: " + EVICT_ATTEMPTS_LIMIT);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected boolean checkTouch(long pageId) {
+        int trackingIdx = trackingIdx(PageIdUtils.pageIndex(pageId));
+
+        int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8);
+
+        return firstTs != 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void forgetPage(long pageId) {
+        int pageIdx = PageIdUtils.pageIndex(pageId);
+
+        int trackingIdx = trackingIdx(pageIdx);
+
+        GridUnsafe.putLongVolatile(null, trackingArrPtr + trackingIdx * 8, 0L);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/RandomLruPageEvictionTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/RandomLruPageEvictionTracker.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/RandomLruPageEvictionTracker.java
new file mode 100644
index 0000000..8818b1c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/evict/RandomLruPageEvictionTracker.java
@@ -0,0 +1,157 @@
+/*
+* 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.database.evict;
+
+import java.util.concurrent.ThreadLocalRandom;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.configuration.MemoryConfiguration;
+import org.apache.ignite.configuration.MemoryPolicyConfiguration;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
+import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.internal.util.typedef.internal.LT;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ *
+ */
+public class RandomLruPageEvictionTracker extends PageAbstractEvictionTracker {
+    /** Evict attempts limit. */
+    private static final int EVICT_ATTEMPTS_LIMIT = 30;
+
+    /** LRU Sample size. */
+    private static final int SAMPLE_SIZE = 5;
+
+    /** Maximum sample search spin count */
+    private static final int SAMPLE_SPIN_LIMIT = SAMPLE_SIZE * 1000;
+
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /** Tracking array ptr. */
+    private long trackingArrPtr;
+
+    /**
+     * @param pageMem Page memory.
+     * @param plcCfg Policy config.
+     * @param sharedCtx Shared context.
+     */
+    public RandomLruPageEvictionTracker(
+        PageMemory pageMem,
+        MemoryPolicyConfiguration plcCfg,
+        GridCacheSharedContext<?, ?> sharedCtx
+    ) {
+        super(pageMem, plcCfg, sharedCtx);
+
+        MemoryConfiguration memCfg = sharedCtx.kernalContext().config().getMemoryConfiguration();
+
+        assert plcCfg.getSize() / memCfg.getPageSize() < Integer.MAX_VALUE;
+
+        log = sharedCtx.logger(getClass());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteException {
+        trackingArrPtr = GridUnsafe.allocateMemory(trackingSize * 4);
+
+        GridUnsafe.setMemory(trackingArrPtr, trackingSize * 4, (byte)0);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop() throws IgniteException {
+        GridUnsafe.freeMemory(trackingArrPtr);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void touchPage(long pageId) throws IgniteCheckedException {
+        int pageIdx = PageIdUtils.pageIndex(pageId);
+
+        long res = compactTimestamp(U.currentTimeMillis());
+        
+        assert res >= 0 && res < Integer.MAX_VALUE;
+        
+        GridUnsafe.putIntVolatile(null, trackingArrPtr + trackingIdx(pageIdx) * 4, (int)res);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void evictDataPage() throws IgniteCheckedException {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+        int evictAttemptsCnt = 0;
+
+        while (evictAttemptsCnt < EVICT_ATTEMPTS_LIMIT) {
+            int lruTrackingIdx = -1;
+
+            int lruCompactTs = Integer.MAX_VALUE;
+
+            int dataPagesCnt = 0;
+
+            int sampleSpinCnt = 0;
+
+            while (dataPagesCnt < SAMPLE_SIZE) {
+                int sampleTrackingIdx = rnd.nextInt(trackingSize);
+
+                int compactTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + sampleTrackingIdx * 4);
+
+                if (compactTs != 0) {
+                    // We chose data page with at least one touch.
+                    if (compactTs < lruCompactTs) {
+                        lruTrackingIdx = sampleTrackingIdx;
+
+                        lruCompactTs = compactTs;
+                    }
+
+                    dataPagesCnt++;
+                }
+
+                sampleSpinCnt++;
+
+                if (sampleSpinCnt > SAMPLE_SPIN_LIMIT) {
+                    LT.warn(log, "Too many attempts to choose data page: " + SAMPLE_SPIN_LIMIT);
+
+                    return;
+                }
+            }
+
+            if (evictDataPage(pageIdx(lruTrackingIdx)))
+                return;
+
+            evictAttemptsCnt++;
+        }
+
+        LT.warn(log, "Too many failed attempts to evict page: " + EVICT_ATTEMPTS_LIMIT);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected boolean checkTouch(long pageId) {
+        int trackingIdx = trackingIdx(PageIdUtils.pageIndex(pageId));
+
+        int ts = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 4);
+
+        return ts != 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void forgetPage(long pageId) {
+        int pageIdx = PageIdUtils.pageIndex(pageId);
+
+        GridUnsafe.putIntVolatile(null, trackingArrPtr + trackingIdx(pageIdx) * 4, 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/ff5b3e16/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl.java
index d433172..cb68f7b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl.java
@@ -22,7 +22,6 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.pagemem.PageIdAllocator;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
-import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.PageUtils;
 import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
 import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageInsertFragmentRecord;
@@ -31,6 +30,8 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageRemoveRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageUpdateRecord;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.database.MemoryMetricsImpl;
+import org.apache.ignite.internal.processors.cache.database.MemoryPolicy;
+import org.apache.ignite.internal.processors.cache.database.evict.PageEvictionTracker;
 import org.apache.ignite.internal.processors.cache.database.tree.io.CacheVersionIO;
 import org.apache.ignite.internal.processors.cache.database.tree.io.DataPageIO;
 import org.apache.ignite.internal.processors.cache.database.tree.io.DataPagePayload;
@@ -71,17 +72,22 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     private final int MIN_SIZE_FOR_DATA_PAGE;
 
     /** */
+    private final int emptyDataPagesBucket;
+
+    /** */
     private final PageHandler<CacheDataRow, Boolean> updateRow = new UpdateRowHandler();
 
     /** */
     private final MemoryMetricsImpl memMetrics;
 
+    /** */
+    private final PageEvictionTracker evictionTracker;
+
     /**
      *
      */
     private final class UpdateRowHandler extends PageHandler<CacheDataRow, Boolean> {
-        @Override
-        public Boolean run(
+        @Override public Boolean run(
             int cacheId,
             long pageId,
             long page,
@@ -97,6 +103,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
             boolean updated = io.updateRow(pageAddr, itemId, pageSize(), null, row, rowSize);
 
+            evictionTracker.touchPage(pageId);
+
             if (updated && needWalDeltaRecord(pageId, page, walPlc)) {
                 // TODO This record must contain only a reference to a logical WAL record with the actual data.
                 byte[] payload = new byte[rowSize];
@@ -125,8 +133,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
      *
      */
     private final class WriteRowHandler extends PageHandler<CacheDataRow, Integer> {
-        @Override
-        public Integer run(
+        @Override public Integer run(
             int cacheId,
             long pageId,
             long page,
@@ -156,6 +163,9 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
                 put(null, pageId, page, pageAddr, bucket);
             }
 
+            if (written == rowSize)
+                evictionTracker.touchPage(pageId);
+
             // Avoid boxing with garbage generation for usual case.
             return written == rowSize ? COMPLETE : written;
         }
@@ -287,6 +297,9 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
                 }
                 else
                     put(null, pageId, page, pageAddr, newBucket);
+
+                if (io.isEmpty(pageAddr))
+                    evictionTracker.forgetPage(pageId);
             }
 
             // For common case boxed 0L will be cached inside of Long, so no garbage will be produced.
@@ -297,8 +310,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     /**
      * @param cacheId Cache ID.
      * @param name Name (for debug purpose).
-     * @param pageMem Page memory.
      * @param memMetrics Memory metrics.
+     * @param memPlc Memory policy.
      * @param reuseList Reuse list or {@code null} if this free list will be a reuse list for itself.
      * @param wal Write ahead log manager.
      * @param metaPageId Metadata page ID.
@@ -308,13 +321,14 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     public FreeListImpl(
         int cacheId,
         String name,
-        PageMemory pageMem,
         MemoryMetricsImpl memMetrics,
+        MemoryPolicy memPlc,
         ReuseList reuseList,
         IgniteWriteAheadLogManager wal,
         long metaPageId,
         boolean initNew) throws IgniteCheckedException {
-        super(cacheId, name, pageMem, BUCKETS, wal, metaPageId);
+        super(cacheId, name, memPlc.pageMemory(), BUCKETS, wal, metaPageId);
+        this.evictionTracker = memPlc.evictionTracker();
         this.reuseList = reuseList == null ? this : reuseList;
         int pageSize = pageMem.pageSize();
 
@@ -337,6 +351,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
         this.memMetrics = memMetrics;
 
+        emptyDataPagesBucket = bucket(MIN_SIZE_FOR_DATA_PAGE, false);
+
         init(metaPageId, initNew);
     }
 
@@ -446,25 +462,26 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
             int freeSpace = Math.min(MIN_SIZE_FOR_DATA_PAGE, rowSize - written);
 
-            int bucket = bucket(freeSpace, false);
+            long pageId = 0L;
+
+            if (freeSpace == MIN_SIZE_FOR_DATA_PAGE)
+                pageId = takeEmptyPage(emptyDataPagesBucket, DataPageIO.VERSIONS);
 
-            long pageId = 0;
             boolean reuseBucket = false;
 
             // TODO: properly handle reuse bucket.
-            for (int b = bucket + 1; b < BUCKETS - 1; b++) {
-                pageId = takeEmptyPage(b, DataPageIO.VERSIONS);
+            if (pageId == 0L) {
+                for (int b = bucket(freeSpace, false) + 1; b < BUCKETS - 1; b++) {
+                    pageId = takeEmptyPage(b, DataPageIO.VERSIONS);
 
-                if (pageId != 0L) {
-                    reuseBucket = isReuseBucket(b);
+                    if (pageId != 0L) {
+                        reuseBucket = isReuseBucket(b);
 
-                    break;
+                        break;
+                    }
                 }
             }
 
-            if (pageId == 0L)
-                pageId = takeEmptyPage(bucket, DataPageIO.VERSIONS);
-
             boolean allocated = pageId == 0L;
 
             if (allocated)
@@ -531,6 +548,13 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
         return bucket == REUSE_BUCKET;
     }
 
+    /**
+     * @return Number of empty data pages in free list.
+     */
+    public int emptyDataPages() {
+        return bucketsSize[emptyDataPagesBucket].intValue();
+    }
+
     /** {@inheritDoc} */
     @Override public void addForRecycle(ReuseBag bag) throws IgniteCheckedException {
         assert reuseList == this: "not allowed to be a reuse list";
@@ -561,7 +585,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
         int keyLen = row.key().valueBytesLength(null);
         int valLen = row.value().valueBytesLength(null);
 
-        return keyLen + valLen + CacheVersionIO.size(row.version(), false) + 8;
+        return keyLen + valLen + CacheVersionIO.size(row.version(), false) + 8 + (row.cacheId() == 0 ? 0 : 4);
     }
 
     /** {@inheritDoc} */