You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by se...@apache.org on 2017/04/06 17:02:50 UTC

[04/12] ignite git commit: GC pressure

GC pressure


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

Branch: refs/heads/ignite-3477-master
Commit: 37eed3420d559922568261b37f1e9f7aa5f25905
Parents: 226d698
Author: Igor Seliverstov <gv...@gmail.com>
Authored: Mon Mar 27 13:36:51 2017 +0300
Committer: Igor Seliverstov <gv...@gmail.com>
Committed: Tue Mar 28 16:39:21 2017 +0300

----------------------------------------------------------------------
 .../apache/ignite/internal/pagemem/Page.java    |  84 --
 .../ignite/internal/pagemem/PageMemory.java     |  20 +-
 .../ignite/internal/pagemem/PageSupport.java    | 102 +++
 .../pagemem/impl/PageMemoryNoStoreImpl.java     | 177 ++--
 .../internal/pagemem/impl/PageNoStoreImpl.java  | 141 ---
 .../cache/CacheOffheapEvictionManager.java      |   3 +-
 .../cache/IgniteCacheOffheapManagerImpl.java    |  16 +-
 .../cache/database/CacheDataRowAdapter.java     |  26 +-
 .../cache/database/DataStructure.java           | 243 +++++-
 .../processors/cache/database/RowStore.java     |  10 -
 .../cache/database/freelist/FreeListImpl.java   | 148 ++--
 .../cache/database/freelist/PagesList.java      | 687 ++++++++-------
 .../cache/database/tree/BPlusTree.java          | 856 ++++++++++---------
 .../cache/database/tree/io/PageIO.java          |   3 +-
 .../database/tree/reuse/ReuseListImpl.java      |   2 +-
 .../cache/database/tree/util/PageHandler.java   | 391 ++++++---
 .../database/tree/util/PageLockListener.java    |  42 +-
 .../dht/atomic/GridDhtAtomicCache.java          |  18 +-
 .../cache/version/GridCacheVersionManager.java  |   3 +
 .../clock/GridClockDeltaSnapshot.java           |   2 +-
 .../pagemem/impl/PageMemoryNoLoadSelfTest.java  | 116 +--
 .../database/BPlusTreeReuseSelfTest.java        |  29 +-
 .../processors/database/BPlusTreeSelfTest.java  |  92 +-
 .../processors/query/h2/database/H2Tree.java    |  13 +-
 .../h2/database/InlineIndexHelperTest.java      |  44 +-
 25 files changed, 1817 insertions(+), 1451 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/modules/core/src/main/java/org/apache/ignite/internal/pagemem/Page.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/Page.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/Page.java
deleted file mode 100644
index a93d186..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/Page.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.pagemem;
-
-/**
- *
- */
-public interface Page extends AutoCloseable {
-    /**
-     * Gets the page ID. Page ID is a unique page identifier that does not change when partitions migrate
-     * from one node to another. Links (which is a page ID and 8-byte offset within a page) must be used
-     * when referencing data across pages.
-     *
-     * @return Page ID.
-     */
-    public long id();
-
-    /**
-     * @return Full page ID.
-     */
-    public FullPageId fullId();
-
-    /**
-     * @return Pointer for reading the page.
-     */
-    public long getForReadPointer();
-
-    /**
-     * Releases reserved page. Released page can be evicted from RAM after flushing modifications to disk.
-     */
-    public void releaseRead();
-
-    /**
-     * @return ByteBuffer for modifying the page.
-     */
-    public long getForWritePointer();
-
-    /**
-     * @return ByteBuffer for modifying the page of {@code null} if failed to get write lock.
-     */
-    public long tryGetForWritePointer();
-
-    /**
-     * Releases reserved page. Released page can be evicted from RAM after flushing modifications to disk.
-     */
-    public void releaseWrite(boolean markDirty);
-
-    /**
-     * @return {@code True} if the page was modified since the last checkpoint.
-     */
-    public boolean isDirty();
-
-    /**
-     * @param plc {@code true} If page should be always recorded to WAL on {@link #releaseWrite(boolean)},
-     *            {@code false} if the page must never be recorded and {@code null} for the default behavior.
-     */
-    public void fullPageWalRecordPolicy(Boolean plc);
-
-    /**
-     * @return Policy for the page.
-     * @see #fullPageWalRecordPolicy(Boolean)
-     */
-    public Boolean fullPageWalRecordPolicy();
-
-    /**
-     * Release page.
-     */
-    @Override public void close();
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageMemory.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageMemory.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageMemory.java
index 6333ff9..c20e1a7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageMemory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageMemory.java
@@ -18,29 +18,11 @@
 package org.apache.ignite.internal.pagemem;
 
 import java.nio.ByteBuffer;
-import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.lifecycle.LifecycleAware;
 
 /**
  */
-public interface PageMemory extends LifecycleAware, PageIdAllocator {
-    /**
-     * Gets the page associated with the given page ID. Each page obtained with this method must be released by
-     * calling {@link #releasePage(Page)}. This method will allocate page with given ID if it doesn't exist.
-     *
-     * @param cacheId Cache ID.
-     * @param pageId Page ID.
-     * @return Page.
-     * @throws IgniteCheckedException If failed.
-     */
-    public Page page(int cacheId, long pageId) throws IgniteCheckedException;
-
-    /**
-     * @param page Page to release.
-     * @throws IgniteCheckedException If failed.
-     */
-    public void releasePage(Page page) throws IgniteCheckedException;
-
+public interface PageMemory extends LifecycleAware, PageIdAllocator, PageSupport {
     /**
      * @return Page size in bytes.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/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
new file mode 100644
index 0000000..7d1f711
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageSupport.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.pagemem;
+
+import org.apache.ignite.IgniteCheckedException;
+
+/**
+ * Supports operations on pages.
+ */
+public interface PageSupport {
+    /**
+     * Gets the page absolute pointer associated with the given page ID. Each page obtained with this method must be
+     * released by calling {@link #releasePage(int, long, long)}. This method will allocate page with given ID if it doesn't
+     * exist.
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @return Page pointer.
+     * @throws IgniteCheckedException If failed.
+     */
+    public long acquirePage(int cacheId, long pageId) throws IgniteCheckedException;
+
+    /**
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID to release.
+     * @param page Page pointer.
+     */
+    public void releasePage(int cacheId, long pageId, long page);
+
+    /**
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @return Pointer for reading the page.
+     */
+    public long readLock(int cacheId, long pageId, long page);
+
+    /**
+     * Releases locked page.
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     */
+    public void readUnlock(int cacheId, long pageId, long page);
+
+    /**
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @return ByteBuffer for modifying the page.
+     */
+    public long writeLock(int cacheId, long pageId, long page);
+
+    /**
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @return ByteBuffer for modifying the page of {@code null} if failed to get write lock.
+     */
+    public long tryWriteLock(int cacheId, long pageId, long page);
+
+    /**
+     * Releases locked page.
+     *
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @param walPlc {@code True} if page should be recorded to WAL, {@code false} if the page must not
+*                                be recorded and {@code null} for the default behavior.
+     * @param dirtyFlag Determines whether the page was modified since the last checkpoint.
+     */
+    public void writeUnlock(int cacheId, long pageId, long page, Boolean walPlc,
+        boolean dirtyFlag);
+
+    /**
+     * @param cacheId Cache ID.
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @return {@code True} if the page is dirty.
+     */
+    public boolean isDirty(int cacheId, long pageId, long page);
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/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 05fce3d..7afd5bd 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
@@ -22,17 +22,16 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.mem.DirectMemory;
 import org.apache.ignite.internal.mem.DirectMemoryProvider;
 import org.apache.ignite.internal.mem.DirectMemoryRegion;
 import org.apache.ignite.internal.mem.OutOfMemoryException;
-import org.apache.ignite.internal.pagemem.Page;
 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.processors.cache.database.tree.io.PageIO;
 import org.apache.ignite.internal.util.GridUnsafe;
 import org.apache.ignite.internal.util.OffheapReadWriteLock;
 import org.apache.ignite.internal.util.offheap.GridOffHeapOutOfMemoryException;
@@ -67,7 +66,7 @@ import static org.apache.ignite.internal.util.GridUnsafe.wrapPointer;
 @SuppressWarnings({"LockAcquiredButNotSafelyReleased", "FieldAccessedSynchronizedAndUnsynchronized"})
 public class PageMemoryNoStoreImpl implements PageMemory {
     /** */
-    public static final long PAGE_MARKER = 0xBEEAAFDEADBEEF01L;
+    private static final long PAGE_MARKER = 0xBEEAAFDEADBEEF01L;
 
     /** Full relative pointer mask. */
     private static final long RELATIVE_PTR_MASK = 0xFFFFFFFFFFFFFFL;
@@ -85,16 +84,16 @@ public class PageMemoryNoStoreImpl implements PageMemory {
     private static final long COUNTER_INC = ADDRESS_MASK + 1;
 
     /** Page ID offset. */
-    public static final int PAGE_ID_OFFSET = 8;
+    private static final int PAGE_ID_OFFSET = 8;
 
     /** Page pin counter offset. */
-    public static final int LOCK_OFFSET = 16;
+    private static final int LOCK_OFFSET = 16;
 
     /**
      * Need a 8-byte pointer for linked list, 8 bytes for internal needs (flags),
      * 4 bytes cache ID, 8 bytes timestamp.
      */
-    public static final int PAGE_OVERHEAD = LOCK_OFFSET + OffheapReadWriteLock.LOCK_SIZE;
+    static final int PAGE_OVERHEAD = LOCK_OFFSET + OffheapReadWriteLock.LOCK_SIZE;
 
     /** Page size. */
     private int sysPageSize;
@@ -269,24 +268,6 @@ public class PageMemoryNoStoreImpl implements PageMemory {
     }
 
     /** {@inheritDoc} */
-    @Override public Page page(int cacheId, long pageId) throws IgniteCheckedException {
-        int pageIdx = PageIdUtils.pageIndex(pageId);
-
-        Segment seg = segment(pageIdx);
-
-        return seg.acquirePage(pageIdx, pageId);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void releasePage(Page p) {
-        if (trackAcquiredPages) {
-            Segment seg = segment(PageIdUtils.pageIndex(p.id()));
-
-            seg.onPageRelease();
-        }
-    }
-
-    /** {@inheritDoc} */
     @Override public int pageSize() {
         return sysPageSize - PAGE_OVERHEAD;
     }
@@ -296,7 +277,9 @@ public class PageMemoryNoStoreImpl implements PageMemory {
         return sysPageSize;
     }
 
-    /** */
+    /**
+     * @return Next index.
+     */
     private int nextRoundRobinIndex() {
         while (true) {
             int idx = selector.get();
@@ -339,7 +322,11 @@ public class PageMemoryNoStoreImpl implements PageMemory {
             seg.readLock().lock();
 
             try {
-                total += seg.acquiredPages();
+                int acquired = seg.acquiredPages();
+
+                assert acquired >= 0;
+
+                total += acquired;
             }
             finally {
                 seg.readLock().unlock();
@@ -350,68 +337,6 @@ public class PageMemoryNoStoreImpl implements PageMemory {
     }
 
     /**
-     * @param absPtr Page absolute address.
-     */
-    boolean readLockPage(long absPtr, int tag) {
-        return rwLock.readLock(absPtr + LOCK_OFFSET, tag);
-    }
-
-    /**
-     * @param absPtr Page absolute address.
-     */
-    void readUnlockPage(long absPtr) {
-        rwLock.readUnlock(absPtr + LOCK_OFFSET);
-    }
-
-    /**
-     * @param absPtr Page absolute address.
-     */
-    boolean writeLockPage(long absPtr, int tag) {
-        return rwLock.writeLock(absPtr + LOCK_OFFSET, tag);
-    }
-
-    /**
-     * @param absPtr Page absolute address.
-     * @return {@code True} if locked page.
-     */
-    boolean tryWriteLockPage(long absPtr, int tag) {
-        return rwLock.tryWriteLock(absPtr + LOCK_OFFSET, tag);
-    }
-
-    /**
-     * @param absPtr Page absolute address.
-     */
-    void writeUnlockPage(long absPtr, int newTag) {
-        rwLock.writeUnlock(absPtr + LOCK_OFFSET, newTag);
-    }
-
-    /**
-     * @param absPtr Absolute pointer to the page.
-     * @return {@code True} if write lock acquired for the page.
-     */
-    boolean isPageWriteLocked(long absPtr) {
-        return rwLock.isWriteLocked(absPtr + LOCK_OFFSET);
-    }
-
-    /**
-     * @param absPtr Absolute pointer to the page.
-     * @return {@code True} if read lock acquired for the page.
-     */
-    boolean isPageReadLocked(long absPtr) {
-        return rwLock.isReadLocked(absPtr + LOCK_OFFSET);
-    }
-
-    /**
-     * Reads page ID from the page at the given absolute position.
-     *
-     * @param absPtr Absolute memory pointer to the page header.
-     * @return Page ID written to the page.
-     */
-    long readPageId(long absPtr) {
-        return GridUnsafe.getLong(absPtr + PAGE_ID_OFFSET);
-    }
-
-    /**
      * Writes page ID to the page at the given absolute position.
      *
      * @param absPtr Absolute memory pointer to the page header.
@@ -453,6 +378,68 @@ public class PageMemoryNoStoreImpl implements PageMemory {
         return res;
     }
 
+    // *** PageSupport methods ***
+
+    /** {@inheritDoc} */
+    @Override public long acquirePage(int cacheId, long pageId) {
+        int pageIdx = PageIdUtils.pageIndex(pageId);
+
+        Segment seg = segment(pageIdx);
+
+        return seg.acquire(pageIdx);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void releasePage(int cacheId, long pageId, long page) {
+        if (trackAcquiredPages) {
+            Segment seg = segment(PageIdUtils.pageIndex(pageId));
+
+            seg.onPageRelease();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public long readLock(int cacheId, long pageId, long page) {
+        if (rwLock.readLock(page + LOCK_OFFSET, PageIdUtils.tag(pageId)))
+            return page + PAGE_OVERHEAD;
+
+        return 0L;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readUnlock(int cacheId, long pageId, long page) {
+        rwLock.readUnlock(page + LOCK_OFFSET);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long writeLock(int cacheId, long pageId, long page) {
+        if (rwLock.writeLock(page + LOCK_OFFSET, PageIdUtils.tag(pageId)))
+            return page + PAGE_OVERHEAD;
+
+        return 0L;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long tryWriteLock(int cacheId, long pageId, long page) {
+        if (rwLock.tryWriteLock(page + LOCK_OFFSET, PageIdUtils.tag(pageId)))
+            return page + PAGE_OVERHEAD;
+
+        return 0L;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeUnlock(int cacheId, long pageId, long page,
+        Boolean walPlc,
+        boolean dirtyFlag) {
+        long actualId = PageIO.getPageId(page + PAGE_OVERHEAD);
+        rwLock.writeUnlock(page + LOCK_OFFSET, PageIdUtils.tag(actualId));
+    }
+
+    @Override public boolean isDirty(int cacheId, long pageId, long page) {
+        // always false for page no store.
+        return false;
+    }
+
     /**
      *
      */
@@ -516,11 +503,9 @@ public class PageMemoryNoStoreImpl implements PageMemory {
 
         /**
          * @param pageIdx Page index.
-         * @param pageId Page ID to pin.
-         * @return Pinned page impl.
+         * @return Page absolute pointer.
          */
-        @SuppressWarnings("TypeMayBeWeakened")
-        private PageNoStoreImpl acquirePage(int pageIdx, long pageId) {
+        private long acquire(int pageIdx) {
             long absPtr = absolute(pageIdx);
 
             assert absPtr % 8 == 0 : absPtr;
@@ -528,7 +513,7 @@ public class PageMemoryNoStoreImpl implements PageMemory {
             if (trackAcquiredPages)
                 acquiredPages.incrementAndGet();
 
-            return new PageNoStoreImpl(PageMemoryNoStoreImpl.this, absPtr, pageId);
+            return absPtr;
         }
 
         /**
@@ -604,12 +589,12 @@ public class PageMemoryNoStoreImpl implements PageMemory {
                 long cnt = ((freePageRelPtrMasked & COUNTER_MASK) + COUNTER_INC) & COUNTER_MASK;
 
                 if (freePageRelPtr != INVALID_REL_PTR) {
-                    long freePageAbsPtr = absolute(PageIdUtils.pageIndex(freePageRelPtr));
+                    long freePage = absolute(PageIdUtils.pageIndex(freePageRelPtr));
 
-                    long nextFreePageRelPtr = GridUnsafe.getLong(freePageAbsPtr) & ADDRESS_MASK;
+                    long nextFreePageRelPtr = GridUnsafe.getLong(freePage) & ADDRESS_MASK;
 
                     if (GridUnsafe.compareAndSwapLong(null, freePageListPtr, freePageRelPtrMasked, nextFreePageRelPtr | cnt)) {
-                        GridUnsafe.putLong(freePageAbsPtr, PAGE_MARKER);
+                        GridUnsafe.putLong(freePage, PAGE_MARKER);
 
                         allocatedPages.incrementAndGet();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/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
deleted file mode 100644
index e82b5d2..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/impl/PageNoStoreImpl.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.pagemem.impl;
-
-import org.apache.ignite.internal.pagemem.FullPageId;
-import org.apache.ignite.internal.pagemem.Page;
-import org.apache.ignite.internal.pagemem.PageIdUtils;
-import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
-import org.apache.ignite.internal.util.typedef.internal.SB;
-
-/**
- *
- */
-public class PageNoStoreImpl implements Page {
-    /** */
-    private long absPtr;
-
-    /** */
-    private long pageId;
-
-    /** */
-    private PageMemoryNoStoreImpl pageMem;
-
-    /**
-     * @param pageMem Page memory.
-     * @param absPtr Absolute pointer.
-     * @param pageId Page ID.
-     */
-    PageNoStoreImpl(PageMemoryNoStoreImpl pageMem, long absPtr, long pageId) {
-        assert absPtr % 8 == 0 : absPtr;
-
-        this.pageMem = pageMem;
-        this.absPtr = absPtr;
-
-        this.pageId = pageId;
-    }
-
-    /**
-     * @return Data pointer.
-     */
-    private long pointer() {
-        return absPtr + PageMemoryNoStoreImpl.PAGE_OVERHEAD;
-    }
-
-    /** {@inheritDoc} */
-    @Override public long id() {
-        return pageId;
-    }
-
-    /** {@inheritDoc} */
-    @Override public FullPageId fullId() {
-        throw new UnsupportedOperationException();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long getForReadPointer() {
-        if (pageMem.readLockPage(absPtr, PageIdUtils.tag(pageId)))
-            return pointer();
-
-        return 0L;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void releaseRead() {
-        pageMem.readUnlockPage(absPtr);
-    }
-
-    /** {@inheritDoc} */
-    @Override public long getForWritePointer() {
-        int tag = PageIdUtils.tag(pageId);
-        boolean locked = pageMem.writeLockPage(absPtr, tag);
-
-        if (!locked)
-            return 0L;
-
-        return pointer();
-    }
-
-    /** {@inheritDoc} */
-    @Override public long tryGetForWritePointer() {
-        int tag = PageIdUtils.tag(pageId);
-
-        if (pageMem.tryWriteLockPage(absPtr, tag))
-            return pointer();
-
-        return 0L;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void releaseWrite(boolean markDirty) {
-        long updatedPageId = PageIO.getPageId(pointer());
-
-        pageMem.writeUnlockPage(absPtr, PageIdUtils.tag(updatedPageId));
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean isDirty() {
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void fullPageWalRecordPolicy(Boolean plc) {
-        // No-op
-    }
-
-    /** {@inheritDoc} */
-    @Override public Boolean fullPageWalRecordPolicy() {
-        return null;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void close() {
-        pageMem.releasePage(this);
-    }
-
-    /** {@inheritDoc} */
-    @Override public String toString() {
-        SB sb = new SB("PageNoStoreImpl [absPtr=0x");
-
-        sb.appendHex(absPtr);
-        sb.a(", pageId=0x").appendHex(pageId);
-        sb.a("]");
-
-        return sb.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/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 6c925ad..e6a9ee7 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
@@ -22,6 +22,7 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersionManager;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.Nullable;
 
@@ -50,7 +51,7 @@ public class CacheOffheapEvictionManager extends GridCacheManagerAdapter impleme
                 return;
             }
 
-            boolean evicted = e.evictInternal(cctx.versions().next(), null);
+            boolean evicted = e.evictInternal(GridCacheVersionManager.EVICT_VER, null);
 
             if (evicted)
                 cctx.cache().removeEntry(e);

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/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 16d3715..6aac083 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
@@ -30,7 +30,6 @@ import org.apache.ignite.IgniteException;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.pagemem.FullPageId;
-import org.apache.ignite.internal.pagemem.Page;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.PageUtils;
@@ -1472,10 +1471,10 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         private int compareKeys(KeyCacheObject key, final long link) throws IgniteCheckedException {
             byte[] bytes = key.valueBytes(cctx.cacheObjectContext());
 
-            PageMemory pageMem = cctx.shared().database().pageMemory();
-
-            try (Page page = page(pageId(link))) {
-                long pageAddr = page.getForReadPointer(); // Non-empty data page must not be recycled.
+            final long pageId = pageId(link);
+            final long page = acquirePage(pageId);
+            try {
+                long pageAddr = readLock(pageId, page); // Non-empty data page must not be recycled.
 
                 assert pageAddr != 0L : link;
 
@@ -1484,7 +1483,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
                     DataPagePayload data = io.readPayload(pageAddr,
                         itemId(link),
-                        pageMem.pageSize());
+                        pageSize());
 
                     if (data.nextLink() == 0) {
                         long addr = pageAddr + data.offset();
@@ -1524,9 +1523,12 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                     }
                 }
                 finally {
-                    page.releaseRead();
+                    readUnlock(pageId, page, pageAddr);
                 }
             }
+            finally {
+                releasePage(pageId, page);
+            }
 
             // TODO GG-11768.
             CacheDataRowAdapter other = new CacheDataRowAdapter(link);

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/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 5a62e75..b751274 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,7 +19,6 @@ package org.apache.ignite.internal.processors.cache.database;
 
 import java.nio.ByteBuffer;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.Page;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.PageUtils;
@@ -98,16 +97,20 @@ public class CacheDataRowAdapter implements CacheDataRow {
         assert key == null : "key";
 
         final CacheObjectContext coctx = cctx.cacheObjectContext();
+        final PageMemory pageMem = cctx.shared().database().pageMemory();
+
+        final int cacheId = cctx.cacheId();
 
         long nextLink = link;
         IncompleteObject<?> incomplete = null;
         boolean first = true;
 
         do {
-            PageMemory pageMem = cctx.shared().database().pageMemory();
+            final long pageId = pageId(nextLink);
+            final long page = pageMem.acquirePage(cacheId, pageId);
 
-            try (Page page = page(pageId(nextLink), cctx)) {
-                long pageAddr = page.getForReadPointer(); // Non-empty data page must not be recycled.
+            try {
+                long pageAddr = pageMem.readLock(cacheId, pageId, page); // Non-empty data page must not be recycled.
 
                 assert pageAddr != 0L : nextLink;
 
@@ -144,9 +147,12 @@ public class CacheDataRowAdapter implements CacheDataRow {
                         return;
                 }
                 finally {
-                    page.releaseRead();
+                    pageMem.readUnlock(cacheId, pageId, page);
                 }
             }
+            finally {
+                pageMem.releasePage(cacheId, pageId, page);
+            }
         }
         while(nextLink != 0);
 
@@ -454,16 +460,6 @@ public class CacheDataRowAdapter implements CacheDataRow {
     }
 
     /**
-     * @param pageId Page ID.
-     * @param cctx Cache context.
-     * @return Page.
-     * @throws IgniteCheckedException If failed.
-     */
-    private Page page(final long pageId, final GridCacheContext cctx) throws IgniteCheckedException {
-        return cctx.shared().database().pageMemory().page(cctx.cacheId(), pageId);
-    }
-
-    /**
      *
      */
     public enum RowData {

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/DataStructure.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/DataStructure.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/DataStructure.java
index f47a697..0e35bf4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/DataStructure.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/DataStructure.java
@@ -20,11 +20,12 @@ package org.apache.ignite.internal.processors.cache.database;
 import java.util.Random;
 import java.util.concurrent.ThreadLocalRandom;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.Page;
 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.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.pagemem.wal.record.delta.RecycleRecord;
+import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
 import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseBag;
 import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList;
 import org.apache.ignite.internal.processors.cache.database.tree.util.PageHandler;
@@ -118,56 +119,236 @@ public abstract class DataStructure implements PageLockListener {
 
     /**
      * @param pageId Page ID.
-     * @return Page.
+     * @return Page absolute pointer.
      * @throws IgniteCheckedException If failed.
      */
-    protected final Page page(long pageId) throws IgniteCheckedException {
+    protected final long acquirePage(long pageId) throws IgniteCheckedException {
         assert PageIdUtils.flag(pageId) == FLAG_IDX && PageIdUtils.partId(pageId) == INDEX_PARTITION ||
             PageIdUtils.flag(pageId) == FLAG_DATA && PageIdUtils.partId(pageId) <= MAX_PARTITION_ID : U.hexLong(pageId);
 
-        return pageMem.page(cacheId, pageId);
+        return pageMem.acquirePage(cacheId, pageId);
     }
 
     /**
-     * @param page Page.
-     * @return Page address.
+     * @param pageId Page ID.
+     * @param page  Page pointer.
      */
-    protected final long tryWriteLock(Page page) {
-        return PageHandler.writeLock(page, this, true);
+    protected final void releasePage(long pageId, long page) {
+        pageMem.releasePage(cacheId, pageId, page);
     }
 
+    /**
+     * @param pageId Page ID
+     * @param page Page pointer.
+     * @return Page address or {@code 0} if failed to lock due to recycling.
+     */
+    protected final long tryWriteLock(long pageId, long page) {
+        return PageHandler.writeLock(pageMem, cacheId, pageId, page, this, true);
+    }
 
     /**
-     * @param page Page.
+     * @param pageId Page ID
+     * @param page Page pointer.
      * @return Page address.
      */
-    protected final long writeLock(Page page) {
-        return PageHandler.writeLock(page, this, false);
+    protected final long writeLock(long pageId, long page) {
+        return PageHandler.writeLock(pageMem, cacheId, pageId, page, this, false);
     }
 
     /**
-     * @param page Page.
+     * <p>
+     * Note: Default WAL record policy will be used.
+     * </p>
+     * @param pageId Page ID
+     * @param page Page pointer.
      * @param pageAddr Page address.
-     * @param dirty Dirty page.
+     * @param dirty Dirty flag.
      */
-    protected final void writeUnlock(Page page, long pageAddr, boolean dirty) {
-        PageHandler.writeUnlock(page, pageAddr, this, dirty);
+    protected final void writeUnlock(long pageId, long page, long pageAddr, boolean dirty) {
+        writeUnlock(pageId, page, pageAddr, null, dirty);
     }
 
     /**
-     * @param page Page.
+     * @param pageId Page ID
+     * @param page Page pointer.
      * @return Page address.
      */
-    protected final long readLock(Page page) {
-        return PageHandler.readLock(page, this);
+    protected final long readLock(long pageId, long page) {
+        return PageHandler.readLock(pageMem, cacheId, pageId, page, this);
+    }
+
+    /**
+     * @param pageId Page ID
+     * @param page Page pointer.
+     * @param pageAddr  Page address.
+     */
+    protected final void readUnlock(long pageId, long page, long pageAddr) {
+        PageHandler.readUnlock(pageMem, cacheId, pageId, page, pageAddr, this);
     }
 
     /**
-     * @param page Page.
-     * @param buf Buffer.
+     * @param pageId Page ID
+     * @param page Page pointer.
+     * @param pageAddr  Page address.
+     * @param walPlc Full page WAL record policy.
+     * @param dirty Dirty flag.
      */
-    protected final void readUnlock(Page page, long buf) {
-        PageHandler.readUnlock(page, buf, this);
+    protected final void writeUnlock(long pageId, long page, long pageAddr, Boolean walPlc, boolean dirty) {
+        PageHandler.writeUnlock(pageMem, cacheId, pageId, page, pageAddr, this, walPlc, dirty);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @param walPlc Full page WAL record policy.
+     * @return {@code true} If we need to make a delta WAL record for the change in this page.
+     */
+    protected final boolean needWalDeltaRecord(long pageId, long page, Boolean walPlc) {
+        return PageHandler.isWalDeltaRecordNeeded(pageMem, cacheId, pageId, page, wal, walPlc);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param h Handler.
+     * @param intArg Argument of type {@code int}.
+     * @param lockFailed Result in case of lock failure due to page recycling.
+     * @return Handler result.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final <R> R write(
+        long pageId,
+        PageHandler<?, R> h,
+        int intArg,
+        R lockFailed) throws IgniteCheckedException {
+        return PageHandler.writePage(pageMem, cacheId, pageId, this, h, null, null, null, null, intArg, lockFailed);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param h Handler.
+     * @param arg Argument.
+     * @param intArg Argument of type {@code int}.
+     * @param lockFailed Result in case of lock failure due to page recycling.
+     * @return Handler result.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final <X, R> R write(
+        long pageId,
+        PageHandler<X, R> h,
+        X arg,
+        int intArg,
+        R lockFailed) throws IgniteCheckedException {
+        return PageHandler.writePage(pageMem, cacheId, pageId, this, h, null, null, null, arg, intArg, lockFailed);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @param h Handler.
+     * @param arg Argument.
+     * @param intArg Argument of type {@code int}.
+     * @param lockFailed Result in case of lock failure due to page recycling.
+     * @return Handler result.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final <X, R> R write(
+        long pageId,
+        long page,
+        PageHandler<X, R> h,
+        X arg,
+        int intArg,
+        R lockFailed) throws IgniteCheckedException {
+        return PageHandler.writePage(pageMem, cacheId, pageId, page, this, h, null, null, null, arg, intArg, lockFailed);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param h Handler.
+     * @param init IO for new page initialization or {@code null} if it is an existing page.
+     * @param arg Argument.
+     * @param intArg Argument of type {@code int}.
+     * @param lockFailed Result in case of lock failure due to page recycling.
+     * @return Handler result.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final <X, R> R write(
+        long pageId,
+        PageHandler<X, R> h,
+        PageIO init,
+        X arg,
+        int intArg,
+        R lockFailed) throws IgniteCheckedException {
+        return PageHandler.writePage(pageMem, cacheId, pageId, this, h, init, wal, null, arg, intArg, lockFailed);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param h Handler.
+     * @param arg Argument.
+     * @param intArg Argument of type {@code int}.
+     * @param lockFailed Result in case of lock failure due to page recycling.
+     * @return Handler result.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final <X, R> R read(
+        long pageId,
+        PageHandler<X, R> h,
+        X arg,
+        int intArg,
+        R lockFailed) throws IgniteCheckedException {
+        return PageHandler.readPage(pageMem, cacheId, pageId, this, h, arg, intArg, lockFailed);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @param h Handler.
+     * @param arg Argument.
+     * @param intArg Argument of type {@code int}.
+     * @param lockFailed Result in case of lock failure due to page recycling.
+     * @return Handler result.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final <X, R> R read(
+        long pageId,
+        long page,
+        PageHandler<X, R> h,
+        X arg,
+        int intArg,
+        R lockFailed) throws IgniteCheckedException {
+        return PageHandler.readPage(pageMem, cacheId, pageId, page, this, h, arg, intArg, lockFailed);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param init IO for new page initialization.
+     * @throws IgniteCheckedException if failed.
+     */
+    protected final void init(long pageId, PageIO init) throws IgniteCheckedException {
+        PageHandler.initPage(pageMem, cacheId, pageId, init, wal, this);
+    }
+
+    /**
+     * @param pageId Page ID.
+     * @param page Page pointer.
+     * @param pageAddr Page address.
+     * @param walPlc Full page WAL record policy.
+     * @return Rotated page ID.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final long recyclePage(
+        long pageId,
+        long page,
+        long pageAddr,
+        Boolean walPlc) throws IgniteCheckedException {
+        long rotated = PageIdUtils.rotatePageId(pageId);
+
+        PageIO.setPageId(pageAddr, rotated);
+
+        if (needWalDeltaRecord(pageId, page, walPlc))
+            wal.log(new RecycleRecord(cacheId, pageId, rotated));
+
+        return rotated;
     }
 
     /**
@@ -177,33 +358,27 @@ public abstract class DataStructure implements PageLockListener {
         return pageMem.pageSize();
     }
 
-    /** {@inheritDoc} */
-    @Override public void onBeforeWriteLock(Page page) {
+    @Override public void onBeforeWriteLock(int cacheId, long pageId, long page) {
         // No-op.
     }
 
-    /** {@inheritDoc} */
-    @Override public void onWriteLock(Page page, long pageAddr) {
+    @Override public void onWriteLock(int cacheId, long pageId, long page, long pageAddr) {
         // No-op.
     }
 
-    /** {@inheritDoc} */
-    @Override public void onWriteUnlock(Page page, long pageAddr) {
+    @Override public void onWriteUnlock(int cacheId, long pageId, long page, long pageAddr) {
         // No-op.
     }
 
-    /** {@inheritDoc} */
-    @Override public void onBeforeReadLock(Page page) {
+    @Override public void onBeforeReadLock(int cacheId, long pageId, long page) {
         // No-op.
     }
 
-    /** {@inheritDoc} */
-    @Override public void onReadLock(Page page, long pageAddr) {
+    @Override public void onReadLock(int cacheId, long pageId, long page, long pageAddr) {
         // No-op.
     }
 
-    /** {@inheritDoc} */
-    @Override public void onReadUnlock(Page page, long pageAddr) {
+    @Override public void onReadUnlock(int cacheId, long pageId, long page, long pageAddr) {
         // No-op.
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
index 8d54542..ce0a4a8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.processors.cache.database;
 
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.Page;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
@@ -56,15 +55,6 @@ public class RowStore {
     }
 
     /**
-     * @param pageId Page ID.
-     * @return Page.
-     * @throws IgniteCheckedException If failed.
-     */
-    protected final Page page(long pageId) throws IgniteCheckedException {
-        return pageMem.page(cctx.cacheId(), pageId);
-    }
-
-    /**
      * @param link Row link.
      * @throws IgniteCheckedException If failed.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/37eed342/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 d6debd8..4d3270c 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
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.processors.cache.database.freelist;
 import java.util.concurrent.atomic.AtomicReferenceArray;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
-import org.apache.ignite.internal.pagemem.Page;
 import org.apache.ignite.internal.pagemem.PageIdAllocator;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
@@ -40,8 +39,6 @@ import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList
 import org.apache.ignite.internal.processors.cache.database.tree.util.PageHandler;
 import org.apache.ignite.internal.util.typedef.internal.U;
 
-import static org.apache.ignite.internal.processors.cache.database.tree.util.PageHandler.writePage;
-
 /**
  */
 public class FreeListImpl extends PagesList implements FreeList, ReuseList {
@@ -61,6 +58,9 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     private static final Long FAIL_L = Long.MAX_VALUE;
 
     /** */
+    private static final Boolean FAIL_B = null;
+
+    /** */
     private static final int MIN_PAGE_FREE_SPACE = 8;
 
     /** */
@@ -78,9 +78,17 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     /**
      *
      */
-    private class UpdateRowHandler extends PageHandler<CacheDataRow, Boolean> {
-        /** {@inheritDoc} */
-        @Override public Boolean run(Page page, PageIO iox, long pageAddr, CacheDataRow row, int itemId)
+    private final class UpdateRowHandler extends PageHandler<CacheDataRow, Boolean> {
+        @Override
+        public Boolean run(
+            int cacheId,
+            long pageId,
+            long page,
+            long pageAddr,
+            PageIO iox,
+            Boolean walPlc,
+            CacheDataRow row,
+            int itemId)
             throws IgniteCheckedException {
             DataPageIO io = (DataPageIO)iox;
 
@@ -88,7 +96,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
             boolean updated = io.updateRow(pageAddr, itemId, pageSize(), null, row, rowSize);
 
-            if (updated && isWalDeltaRecordNeeded(wal, page)) {
+            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];
 
@@ -100,14 +108,14 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
                 wal.log(new DataPageUpdateRecord(
                     cacheId,
-                    page.id(),
+                    pageId,
                     itemId,
                     payload));
             }
 
             return updated;
         }
-    };
+    }
 
     /** */
     private final PageHandler<CacheDataRow, Integer> writeRow = new WriteRowHandler();
@@ -115,9 +123,17 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     /**
      *
      */
-    private class WriteRowHandler extends PageHandler<CacheDataRow, Integer> {
-        /** {@inheritDoc} */
-        @Override public Integer run(Page page, PageIO iox, long pageAddr, CacheDataRow row, int written)
+    private final class WriteRowHandler extends PageHandler<CacheDataRow, Integer> {
+        @Override
+        public Integer run(
+            int cacheId,
+            long pageId,
+            long page,
+            long pageAddr,
+            PageIO iox,
+            Boolean walPlc,
+            CacheDataRow row,
+            int written)
             throws IgniteCheckedException {
             DataPageIO io = (DataPageIO)iox;
 
@@ -127,8 +143,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
             assert oldFreeSpace > 0 : oldFreeSpace;
 
             // If the full row does not fit into this page write only a fragment.
-            written = (written == 0 && oldFreeSpace >= rowSize) ? addRow(page, pageAddr, io, row, rowSize):
-                addRowFragment(page, pageAddr, io, row, written, rowSize);
+            written = (written == 0 && oldFreeSpace >= rowSize) ? addRow(pageId, page, pageAddr, io, row, rowSize):
+                addRowFragment(pageId, page, pageAddr, io, row, written, rowSize);
 
             // Reread free space after update.
             int newFreeSpace = io.getFreeSpace(pageAddr);
@@ -136,7 +152,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
             if (newFreeSpace > MIN_PAGE_FREE_SPACE) {
                 int bucket = bucket(newFreeSpace, false);
 
-                put(null, page, pageAddr, bucket);
+                put(null, pageId, page, pageAddr, bucket);
             }
 
             // Avoid boxing with garbage generation for usual case.
@@ -144,7 +160,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
         }
 
         /**
-         * @param page Page.
+         * @param pageId Page ID.
+         * @param page Page pointer.
          * @param pageAddr Page address.
          * @param io IO.
          * @param row Row.
@@ -153,7 +170,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
          * @throws IgniteCheckedException If failed.
          */
         private int addRow(
-            Page page,
+            long pageId,
+            long page,
             long pageAddr,
             DataPageIO io,
             CacheDataRow row,
@@ -161,7 +179,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
         ) throws IgniteCheckedException {
             io.addRow(pageAddr, row, rowSize, pageSize());
 
-            if (isWalDeltaRecordNeeded(wal, page)) {
+            if (needWalDeltaRecord(pageId, page, null)) {
                 // TODO This record must contain only a reference to a logical WAL record with the actual data.
                 byte[] payload = new byte[rowSize];
 
@@ -173,7 +191,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
                 wal.log(new DataPageInsertRecord(
                     cacheId,
-                    page.id(),
+                    pageId,
                     payload));
             }
 
@@ -181,7 +199,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
         }
 
         /**
-         * @param page Page.
+         * @param pageId Page ID.
+         * @param page Page pointer.
          * @param pageAddr Page address.
          * @param io IO.
          * @param row Row.
@@ -191,7 +210,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
          * @throws IgniteCheckedException If failed.
          */
         private int addRowFragment(
-            Page page,
+            long pageId,
+            long page,
             long pageAddr,
             DataPageIO io,
             CacheDataRow row,
@@ -205,7 +225,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
             assert payloadSize > 0 : payloadSize;
 
-            if (isWalDeltaRecordNeeded(wal, page)) {
+            if (needWalDeltaRecord(pageId, page, null)) {
                 // TODO This record must contain only a reference to a logical WAL record with the actual data.
                 byte[] payload = new byte[payloadSize];
 
@@ -213,12 +233,12 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
                 PageUtils.getBytes(pageAddr, data.offset(), payload, 0, payloadSize);
 
-                wal.log(new DataPageInsertFragmentRecord(cacheId, page.id(), payload, lastLink));
+                wal.log(new DataPageInsertFragmentRecord(cacheId, pageId, payload, lastLink));
             }
 
             return written + payloadSize;
         }
-    };
+    }
 
 
     /** */
@@ -227,9 +247,17 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     /**
      *
      */
-    private class RemoveRowHandler extends PageHandler<Void, Long> {
-        /** {@inheritDoc} */
-        @Override public Long run(Page page, PageIO iox, long pageAddr, Void arg, int itemId)
+    private final class RemoveRowHandler extends PageHandler<Void, Long> {
+        @Override
+        public Long run(
+            int cacheId,
+            long pageId,
+            long page,
+            long pageAddr,
+            PageIO iox,
+            Boolean walPlc,
+            Void ignored,
+            int itemId)
             throws IgniteCheckedException {
             DataPageIO io = (DataPageIO)iox;
 
@@ -239,16 +267,8 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
             long nextLink = io.removeRow(pageAddr, itemId, pageSize());
 
-            if (isWalDeltaRecordNeeded(wal, page))
-                wal.log(new DataPageRemoveRecord(cacheId, page.id(), itemId));
-
-            // TODO: properly handle reuse bucket.
-//            if (io.isEmpty(buf)) {
-//                int oldBucket = oldFreeSpace > MIN_PAGE_FREE_SPACE ? bucket(oldFreeSpace, false) : -1;
-//
-//                if (oldBucket == -1 || removeDataPage(page, buf, io, oldBucket))
-//                    put(null, page, buf, REUSE_BUCKET);
-//            }
+            if (needWalDeltaRecord(pageId, page, walPlc))
+                wal.log(new DataPageRemoveRecord(cacheId, pageId, itemId));
 
             int newFreeSpace = io.getFreeSpace(pageAddr);
 
@@ -260,18 +280,18 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
                     if (oldBucket != newBucket) {
                         // It is possible that page was concurrently taken for put, in this case put will handle bucket change.
-                        if (removeDataPage(page, pageAddr, io, oldBucket))
-                            put(null, page, pageAddr, newBucket);
+                        if (removeDataPage(pageId, page, pageAddr, io, oldBucket))
+                            put(null, pageId, page, pageAddr, newBucket);
                     }
                 }
                 else
-                    put(null, page, pageAddr, newBucket);
+                    put(null, pageId, page, pageAddr, newBucket);
             }
 
             // For common case boxed 0L will be cached inside of Long, so no garbage will be produced.
             return nextLink;
         }
-    };
+    }
 
     /**
      * @param cacheId Cache ID.
@@ -377,16 +397,14 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
 
     /**
      * @param part Partition.
-     * @return Page.
+     * @return Page ID.
      * @throws IgniteCheckedException If failed.
      */
-    private Page allocateDataPage(int part) throws IgniteCheckedException {
+    private long allocate(int part) throws IgniteCheckedException {
         assert part <= PageIdAllocator.MAX_PARTITION_ID;
         assert part != PageIdAllocator.INDEX_PARTITION;
 
-        long pageId = pageMem.allocatePage(cacheId, part, PageIdAllocator.FLAG_DATA);
-
-        return pageMem.page(cacheId, pageId);
+        return pageMem.allocatePage(cacheId, part, PageIdAllocator.FLAG_DATA);
     }
 
     /** {@inheritDoc} */
@@ -417,14 +435,16 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
             if (pageId == 0L)
                 pageId = takeEmptyPage(bucket, DataPageIO.VERSIONS);
 
-            try (Page page = pageId == 0 ? allocateDataPage(row.partition()) : pageMem.page(cacheId, pageId)) {
-                // If it is an existing page, we do not need to initialize it.
-                DataPageIO init = reuseBucket || pageId == 0L ? DataPageIO.VERSIONS.latest() : null;
+            boolean allocated = pageId == 0L;
 
-                written = writePage(pageMem, page, this, writeRow, init, wal, row, written, FAIL_I);
+            if(allocated)
+                pageId = allocate(row.partition());
 
-                assert written != FAIL_I; // We can't fail here.
-            }
+            DataPageIO init = reuseBucket || allocated ? DataPageIO.VERSIONS.latest() : null;
+
+            written = write(pageId, writeRow, init, row, written, FAIL_I);
+
+            assert written != FAIL_I; // We can't fail here.
         }
         while (written != COMPLETE);
     }
@@ -436,13 +456,11 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
         long pageId = PageIdUtils.pageId(link);
         int itemId = PageIdUtils.itemId(link);
 
-        try (Page page = pageMem.page(cacheId, pageId)) {
-            Boolean updated = writePage(pageMem, page, this, updateRow, row, itemId, null);
+        Boolean updated = write(pageId, updateRow, row, itemId, FAIL_B);
 
-            assert updated != null; // Can't fail here.
+        assert updated != FAIL_B; // Can't fail here.
 
-            return updated != null ? updated : false;
-        }
+        return updated;
     }
 
     /** {@inheritDoc} */
@@ -452,23 +470,17 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
         long pageId = PageIdUtils.pageId(link);
         int itemId = PageIdUtils.itemId(link);
 
-        long nextLink;
+        long nextLink = write(pageId, rmvRow, itemId, FAIL_L);
 
-        try (Page page = pageMem.page(cacheId, pageId)) {
-            nextLink = writePage(pageMem, page, this, rmvRow, null, itemId, FAIL_L);
-
-            assert nextLink != FAIL_L; // Can't fail here.
-        }
+        assert nextLink != FAIL_L; // Can't fail here.
 
         while (nextLink != 0L) {
             itemId = PageIdUtils.itemId(nextLink);
             pageId = PageIdUtils.pageId(nextLink);
 
-            try (Page page = pageMem.page(cacheId, pageId)) {
-                nextLink = writePage(pageMem, page, this, rmvRow, null, itemId, FAIL_L);
+            nextLink = write(pageId, rmvRow, itemId, FAIL_L);
 
-                assert nextLink != FAIL_L; // Can't fail here.
-            }
+            assert nextLink != FAIL_L; // Can't fail here.
         }
     }
 
@@ -491,7 +503,7 @@ public class FreeListImpl extends PagesList implements FreeList, ReuseList {
     @Override public void addForRecycle(ReuseBag bag) throws IgniteCheckedException {
         assert reuseList == this: "not allowed to be a reuse list";
 
-        put(bag, null, 0L, REUSE_BUCKET);
+        put(bag, 0, 0, 0L, REUSE_BUCKET);
     }
 
     /** {@inheritDoc} */