You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2017/01/30 17:47:21 UTC

[9/9] ignite git commit: ignite-3477 lock free freelist

ignite-3477 lock free freelist


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

Branch: refs/heads/ignite-3477-freelist
Commit: a4256544b6bee9be054fc04e7317240d8d66ff3e
Parents: 0745af2
Author: sboikov <sb...@gridgain.com>
Authored: Mon Jan 30 20:02:03 2017 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Mon Jan 30 20:11:39 2017 +0300

----------------------------------------------------------------------
 .../IgniteCacheDatabaseSharedManager.java       |   2 +-
 .../cache/database/freelist/DataPageList.java   |  11 +-
 .../cache/database/freelist/FreeListImpl2.java  | 336 ++++++++++++++-----
 .../cache/database/tree/io/DataPageIO.java      |  28 +-
 .../database/FreeListImpl2SelfTest.java         |  12 +-
 5 files changed, 289 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/a4256544/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 b1da249..46df221 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
@@ -80,7 +80,7 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
      * @throws IgniteCheckedException If failed.
      */
     protected void initDataStructures() throws IgniteCheckedException {
-        freeList = new FreeListImpl2(0, cctx.gridName(), pageMem, null, cctx.wal(), 0L, true);
+        freeList = new FreeListImpl2(log, 0, cctx.gridName(), pageMem, null, cctx.wal(), 0L, true);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/a4256544/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/DataPageList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/DataPageList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/DataPageList.java
index 710164d..90884ad 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/DataPageList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/DataPageList.java
@@ -49,6 +49,8 @@ public class DataPageList {
     /** */
     private final DataPageIO io;
 
+    public volatile boolean needCompact;
+
     /**
      * @param pageMem Page memory.
      */
@@ -58,13 +60,18 @@ public class DataPageList {
         io = DataPageIO.VERSIONS.latest();
     }
 
-    public void put(Page page) throws IgniteCheckedException {
+    public void put(Page page, int bucket, int stripe) throws IgniteCheckedException {
+        long pageAddr = page.pageAddress();
+
+        io.setBucket(pageAddr, bucket);
+        io.setStripe(pageAddr, stripe);
+
         while (true) {
             Head head = this.head;
 
             Head newHead = new Head(page.id());
 
-            io.setNextPageId(page.pageAddress(), head.pageId);
+            io.setNextPageId(pageAddr, head.pageId);
 
             if (GridUnsafe.compareAndSwapObject(this, headOffset, head, newHead))
                 break;

http://git-wip-us.apache.org/repos/asf/ignite/blob/a4256544/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl2.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl2.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl2.java
index c6859f8..a85d40d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl2.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/freelist/FreeListImpl2.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.processors.cache.database.freelist;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReferenceArray;
 import org.apache.ignite.IgniteCheckedException;
@@ -37,6 +39,7 @@ import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList
 import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseListImpl;
 import org.apache.ignite.internal.processors.cache.database.tree.util.PageHandler;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jsr166.LongAdder8;
 
 import static org.apache.ignite.internal.processors.cache.database.tree.util.PageHandler.writePage;
 
@@ -66,7 +69,9 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
 
     private final PageHandler<CacheDataRow, Boolean> updateRow = new UpdateRow();
 
-    /** */
+    /**
+     *
+     */
     private class UpdateRow extends PageHandler<CacheDataRow, Boolean> {
             @Override public Boolean run(Page page, PageIO iox, long pageAddr, CacheDataRow row, int itemId)
                 throws IgniteCheckedException {
@@ -83,91 +88,104 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
     /** */
     private final PageHandler<Void, Boolean> compact = new Compact();
 
+    /**
+     *
+     */
     private class Compact extends PageHandler<Void, Boolean> {
-            @Override public Boolean run(Page page, PageIO iox, long pageAddr, Void row, int itemId)
-                throws IgniteCheckedException {
-                DataPageIO io = (DataPageIO)iox;
+        @Override public Boolean run(Page page, PageIO iox, long pageAddr, Void row, int itemId)
+            throws IgniteCheckedException {
+            DataPageIO io = (DataPageIO)iox;
 
-                int freeSpace = io.getFreeSpace(pageAddr);
+            int freeSpace = io.getFreeSpace(pageAddr);
 
-                int newFreeSpace = io.compact(pageAddr, freeSpace, pageSize());
+            int newFreeSpace = io.compact(pageAddr, freeSpace, pageSize());
 
-                assert freeSpace == newFreeSpace;
+            assert freeSpace == newFreeSpace;
 
-                if (newFreeSpace > MIN_PAGE_FREE_SPACE) {
-                    int newBucket = bucket(newFreeSpace);
+            if (newFreeSpace > MIN_PAGE_FREE_SPACE) {
+                int newBucket = bucket(newFreeSpace);
 
 //                    System.out.println("End compact [freeSpace=" + freeSpace +
 //                        ", newSpace=" + newFreeSpace +
 //                        ", b=" + newBucket + ']');
 
-                    putInBucket(newBucket, page);
-                }
+                putInBucket(newBucket, page);
+            }
 //                else
 //                    System.out.println("End compact, no reuse [freeSpace=" + freeSpace +
 //                        ", newSpace=" + newFreeSpace + ']');
 
-                return Boolean.TRUE;
-            }
-        };
+            return Boolean.TRUE;
+        }
+    };
 
     /** */
-    private final PageHandler<Void, Boolean> compact2 = new Compact2();
+    private final PageHandler<Integer, Compact2Res> compact2 = new Compact2();
 
-    private class Compact2 extends PageHandler<Void, Boolean> {
-        @Override public Boolean run(Page page, PageIO iox, long pageAddr, Void ignore, int reqSpace)
+    /**
+     *
+     */
+    private class Compact2 extends PageHandler<Integer, Compact2Res> {
+        @Override public Compact2Res run(Page page, PageIO iox, long pageAddr, Integer b, int reqSpace)
             throws IgniteCheckedException {
             DataPageIO io = (DataPageIO)iox;
 
             int freeSpace = io.getFreeSpace(pageAddr);
 
+            int newBucket = bucket(freeSpace);
+
+            if (newBucket == b)
+                return Compact2Res.BUCKET_NOT_CHANGED;
+
             int newFreeSpace = io.compact(pageAddr, freeSpace, pageSize());
 
             assert freeSpace == newFreeSpace;
 
             if (newFreeSpace > MIN_PAGE_FREE_SPACE) {
                 if (newFreeSpace >= reqSpace)
-                    return Boolean.TRUE;
-
-                int newBucket = bucket(newFreeSpace);
+                    return Compact2Res.FOUND;
 
                 putInBucket(newBucket, page);
             }
 
-            return Boolean.FALSE;
+            return Compact2Res.MOVED;
         }
     };
 
     /** */
     private final PageHandler<CacheDataRow, Integer> writeRow = new WriteRow();
 
+    /**
+     *
+     */
     private class WriteRow extends PageHandler<CacheDataRow, Integer> {
-            @Override public Integer run(Page page, PageIO iox, long pageAddr, CacheDataRow row, int written)
-                throws IgniteCheckedException {
-                DataPageIO io = (DataPageIO)iox;
-
-                int rowSize = getRowSize(row);
-                int oldFreeSpace = io.getFreeSpace(pageAddr);
+        @Override public Integer run(Page page, PageIO iox, long pageAddr, CacheDataRow row, int written)
+            throws IgniteCheckedException {
+            DataPageIO io = (DataPageIO)iox;
 
-                assert oldFreeSpace > 0 : oldFreeSpace;
+            int rowSize = getRowSize(row);
+            int oldFreeSpace = io.getFreeSpace(pageAddr);
 
-                // 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);
+            assert oldFreeSpace > 0 : oldFreeSpace;
 
-                // Reread free space after update.
-                int newFreeSpace = io.getFreeSpace(pageAddr);
+            // 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);
 
-                if (newFreeSpace > MIN_PAGE_FREE_SPACE) {
-                    int bucket = bucket(newFreeSpace);
+            // Reread free space after update.
+            //int newFreeSpace = io.getFreeSpace(pageAddr);
+            int newFreeSpace = Math.max(io.getFreeSpace(pageAddr), io.getFreeSpace2(pageAddr));
 
-                    putInBucket(bucket, page);
-                }
+            if (newFreeSpace > MIN_PAGE_FREE_SPACE) {
+                int bucket = bucket(newFreeSpace);
 
-                // Avoid boxing with garbage generation for usual case.
-                return written == rowSize ? COMPLETE : written;
+                putInBucket(bucket, page);
             }
 
+            // Avoid boxing with garbage generation for usual case.
+            return written == rowSize ? COMPLETE : written;
+        }
+
             /**
              * @param page Page.
              * @param pageAddr Page address.
@@ -218,6 +236,9 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
     /** */
     private final PageHandler<Void, Long> rmvRow = new RemoveRow();
 
+    /**
+     *
+     */
     private class RemoveRow extends PageHandler<Void, Long> {
         @Override public Long run(Page page, PageIO iox, long pageAddr, Void arg, int itemId)
             throws IgniteCheckedException {
@@ -225,13 +246,34 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
 
             int oldFreeSpace = io.getFreeSpace(pageAddr);
 
-            assert oldFreeSpace >= 0: oldFreeSpace;
+            assert oldFreeSpace >= 0 : oldFreeSpace;
 
             long nextLink = io.removeRow(pageAddr, itemId, pageSize());
 
             int newFreeSpace = io.getFreeSpace(pageAddr);
 
-            assert newFreeSpace > oldFreeSpace;
+            //assert newFreeSpace > oldFreeSpace : "n=" + newFreeSpace + ", o=" + oldFreeSpace;
+
+            if (newFreeSpace > MIN_PAGE_FREE_SPACE) {
+                int curBucket = io.getBucket(pageAddr);
+
+                assert curBucket == -1 || (curBucket >= 0 && curBucket < BUCKETS) : curBucket;
+
+                if (curBucket >= 0) {
+                    int newBucket = bucket(newFreeSpace);
+
+                    if ((newBucket != curBucket)) {
+                        int stripe = io.getStripe(pageAddr);
+
+                        assert stripe >= 0 && stripe < STACKS_PER_BUCKET : stripe;
+
+                        DataPageList list = (DataPageList)buckets[curBucket].get(stripe);
+
+                        if (list != null)
+                            list.needCompact = true;
+                    }
+                }
+            }
 
             // For common case boxed 0L will be cached inside of Long, so no garbage will be produced.
             return nextLink;
@@ -248,6 +290,16 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
 
     private final ReuseListImpl reuseList;
 
+    private LongAdder8 scanCnt = new LongAdder8();
+    private LongAdder8 scanCntSuccess = new LongAdder8();
+    private LongAdder8 scanBuckets = new LongAdder8();
+
+    private LongAdder8 syncClearCnt = new LongAdder8();
+    private LongAdder8 syncClearSuccess = new LongAdder8();
+    private LongAdder8 syncClearBuckets = new LongAdder8();
+    private LongAdder8 syncClearLists = new LongAdder8();
+    private LongAdder8 syncClearPages = new LongAdder8();
+
     /**
      * @param cacheId Cache ID.
      * @param name Name (for debug purpose).
@@ -259,6 +311,7 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
      * @throws IgniteCheckedException If failed.
      */
     public FreeListImpl2(
+        final IgniteLogger log,
         int cacheId,
         String name,
         PageMemory pageMem,
@@ -267,6 +320,7 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
         long metaPageId,
         boolean initNew) throws IgniteCheckedException {
         super(cacheId, pageMem, wal);
+        this.log = log;
         this.reuseList = new ReuseListImpl(cacheId, name, pageMem, wal, 0, true);
 
         int pageSize = pageMem.pageSize();
@@ -317,6 +371,53 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
         compacter.setDaemon(true);
 
         compacter.start();
+
+        Thread dump = new Thread(new Runnable() {
+            @Override public void run() {
+                try {
+                    while (!Thread.currentThread().isInterrupted()) {
+                        {
+                            long scanCnt0 = scanCnt.sumThenReset();
+
+                            if (scanCnt0 > 0) {
+                                long scanCntSuccess0 = scanCntSuccess.sumThenReset();
+                                long scanBuckets0 = scanBuckets.sumThenReset();
+
+                                log.info("Bucket scans [total=" + scanCnt0 +
+                                    ", success=" + scanCntSuccess0 +
+                                    ", success %=" + scanCntSuccess0 / (double)scanCnt0 +
+                                    ", avgBuckets=" + scanBuckets0 / (double)scanCnt0 + ']');
+                            }
+                        }
+
+                        long syncClearCnt0 = syncClearCnt.sumThenReset();
+
+                        if (syncClearCnt0 > 0) {
+                            long syncClearSuccess0 = syncClearSuccess.sumThenReset();
+                            long syncClearBuckets0 = syncClearBuckets.sumThenReset();
+                            long syncClearLists0 = syncClearLists.sumThenReset();
+                            long syncClearPages0 = syncClearPages.sumThenReset();
+
+                            log.info("Sync clear [total=" + syncClearCnt0 +
+                                ", success=" + syncClearSuccess0 +
+                                ", success %=" + syncClearSuccess0 / (double)syncClearCnt0 +
+                                ", avgBuckets=" + syncClearBuckets0 / (double)syncClearCnt0 +
+                                ", avgLists=" + syncClearLists0 / (double)syncClearCnt0 +
+                                ", avgPages=" + syncClearPages0 / (double)syncClearCnt0 +
+                                ']');
+                        }
+
+                        Thread.sleep(5000);
+                    }
+                }
+                catch (Exception e) {
+                    if (!(e instanceof InterruptedException))
+                        e.printStackTrace();
+                }
+            }
+        });
+        dump.setDaemon(true);
+        dump.start();
     }
 
     private void putInBucket(int bucket, Page page) throws IgniteCheckedException {
@@ -330,7 +431,7 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
             if (list != null) {
                 //System.out.println(Thread.currentThread().getName() + " put in bucket [b=" + bucket + ", stripe=" + idx + ']');
 
-                list.put(page);
+                list.put(page, bucket, idx);
 
                 return;
             }
@@ -434,12 +535,14 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
         for (int i = 0; i < stacks.length(); i++) {
             DataPageList pageList = stacks.get(i);
 
-            if (pageList != null) {
+            if (pageList != null && pageList.needCompact) {
                 boolean take = stacks.compareAndSet(i, pageList, null);
 
                 if (take) {
                     compactStack(pageList);
 
+                    pageList.needCompact = false;
+
                     stacks.set(i, pageList);
                 }
             }
@@ -471,84 +574,139 @@ public class FreeListImpl2 extends DataStructure implements FreeList, ReuseList
         int written = 0;
 
         do {
-            int freeSpace = Math.min(MIN_SIZE_FOR_DATA_PAGE, rowSize - written);
+            final int freeSpace = Math.min(MIN_SIZE_FOR_DATA_PAGE, rowSize - written);
 
-            int bucket = bucket(freeSpace);
+            final int bucket = bucket(freeSpace);
 
             Page foundPage = null;
 
+            int cntr = 0;
+
             // TODO: properly handle reuse bucket.
             for (int b = bucket; b < BUCKETS; b++) {
+                cntr++;
+
                 foundPage = takeFromBucket(b);
 
                 if (foundPage != null)
                     break;
             }
 
-            if (locCompact && foundPage == null && bucket > 0 && cg.compareAndSet(false, true)) {
-                try {
-                    for (int b = 0; b < bucket; b++) {
-                        AtomicReferenceArray<DataPageList> stacks = buckets[b];
+            scanCnt.increment();
+            scanBuckets.add(cntr);
+
+            if (foundPage != null)
+                scanCntSuccess.increment();
+
+            if (foundPage == null && bucket > 0)
+                foundPage = syncClear(freeSpace, bucket);
+
+            try (Page page = foundPage == null ? allocateDataPage(row.partition()) : foundPage) {
+                // If it is an existing page, we do not need to initialize it.
+                DataPageIO init = foundPage == null ? DataPageIO.VERSIONS.latest() : null;
+
+                written = writePage(pageMem, page, this, writeRow, init, wal, row, written, FAIL_I);
+
+                assert written != FAIL_I; // We can't fail here.
+            }
+        }
+        while (written != COMPLETE);
+    }
+
+    enum Compact2Res {
+        FOUND,
+        MOVED,
+        BUCKET_NOT_CHANGED
+    }
+
+    private Page syncClear(final int freeSpace, final int bucket) throws IgniteCheckedException {
+        if (locCompact && cg.compareAndSet(false, true)) {
+            int buckets = 0;
+            int lists = 0;
+            int pages = 0;
+
+            boolean res = false;
+
+            try {
+                for (int b = 0; b < bucket; b++) {
+                    buckets++;
+
+                    AtomicReferenceArray<DataPageList> stacks = this.buckets[b];
+
+                    final Integer B = b;
+
+                    for (int i = 0; i < STACKS_PER_BUCKET; i++) {
+                        DataPageList pageList = stacks.get(i);
+
+                        if (pageList != null && pageList.needCompact) {
+                            lists++;
+
+                            boolean take = stacks.compareAndSet(i, pageList, null);
+
+                            if (take) {
+                                Page page;
 
-                        for (int i = 0; i < STACKS_PER_BUCKET; i++) {
-                            DataPageList pageList = stacks.get(i);
+                                List<Page> mvPages = null;
 
-                            if (pageList != null) {
-                                boolean take = stacks.compareAndSet(i, pageList, null);
+                                try {
+                                    while ((page = pageList.take(cacheId)) != null) {
+                                        pages++;
 
-                                if (take) {
-                                    Page page;
+                                        Compact2Res found = writePage(pageMem,
+                                            page,
+                                            this,
+                                            compact2,
+                                            null,
+                                            wal,
+                                            B,
+                                            freeSpace,
+                                            null);
 
-                                    try {
-                                        while ((page = pageList.take(cacheId)) != null) {
-                                            Boolean found = writePage(pageMem,
-                                                page,
-                                                this,
-                                                compact2,
-                                                null,
-                                                wal,
-                                                null,
-                                                freeSpace,
-                                                null);
+                                        assert found != null;
 
-                                            assert found != null;
+                                        if (found == Compact2Res.FOUND) {
+                                            res = true;
 
-                                            if (found) {
-                                                foundPage = page;
+                                            return page;
+                                        }
+                                        else if (found == Compact2Res.BUCKET_NOT_CHANGED) {
+                                            if (mvPages == null)
+                                                mvPages = new ArrayList<>();
 
-                                                break;
-                                            }
+                                            mvPages.add(page);
                                         }
                                     }
-                                    finally {
-                                        stacks.set(i, pageList);
+                                }
+                                finally {
+                                    if (mvPages != null) {
+                                        for (int p = 0; p < mvPages.size(); p++)
+                                            pageList.put(mvPages.get(p), b, i);
                                     }
+
+                                    if (!res)
+                                        pageList.needCompact = false;
+
+                                    stacks.set(i, pageList);
                                 }
                             }
-
-                            if (foundPage != null)
-                                break;
                         }
-
-                        if (foundPage != null)
-                            break;
                     }
                 }
-                finally {
-                    cg.set(false);
-                }
             }
+            finally {
+                if (res)
+                    syncClearSuccess.increment();
 
-            try (Page page = foundPage == null ? allocateDataPage(row.partition()) : foundPage) {
-                // If it is an existing page, we do not need to initialize it.
-                DataPageIO init = foundPage == null ? DataPageIO.VERSIONS.latest() : null;
-
-                written = writePage(pageMem, page, this, writeRow, init, wal, row, written, FAIL_I);
+                syncClearCnt.increment();
+                syncClearBuckets.add(buckets);
+                syncClearLists.add(lists);
+                syncClearPages.add(pages);
 
-                assert written != FAIL_I; // We can't fail here.
+                cg.set(false);
             }
         }
-        while (written != COMPLETE);
+
+        return null;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/a4256544/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 5618a8c..ac2cf13 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
@@ -54,7 +54,13 @@ public class DataPageIO extends PageIO {
     private static final int NEXT_PAGE_ID_OFF = COMMON_HEADER_END;
 
     /** */
-    private static final int FREE_LIST_PAGE_ID_OFF = NEXT_PAGE_ID_OFF + 8;
+    private static final int BUCKET_OFF = NEXT_PAGE_ID_OFF + 8;
+
+    /** */
+    private static final int STRIPE_OFF = BUCKET_OFF + 4;
+
+    /** */
+    private static final int FREE_LIST_PAGE_ID_OFF = STRIPE_OFF + 4;
 
     /** */
     private static final int FREE_SPACE_OFF = FREE_LIST_PAGE_ID_OFF + 8;
@@ -99,6 +105,8 @@ public class DataPageIO extends PageIO {
 
         setEmptyPage(pageAddr, pageSize);
         setFreeListPageId(pageAddr, 0L);
+        setBucket(pageAddr, -1);
+        setStripe(pageAddr, -1);
     }
 
     /**
@@ -144,6 +152,22 @@ public class DataPageIO extends PageIO {
         return PageUtils.getLong(pageAddr, NEXT_PAGE_ID_OFF);
     }
 
+    public void setBucket(long pageAddr, int bucket) {
+        PageUtils.putInt(pageAddr, BUCKET_OFF, bucket);
+    }
+
+    public int getBucket(long pageAddr) {
+        return PageUtils.getInt(pageAddr, BUCKET_OFF);
+    }
+
+    public void setStripe(long pageAddr, int stripe) {
+        PageUtils.putInt(pageAddr, STRIPE_OFF, stripe);
+    }
+
+    public int getStripe(long pageAddr) {
+        return PageUtils.getInt(pageAddr, STRIPE_OFF);
+    }
+
     /**
      * @param pageAddr Page address.
      * @param dataOff Data offset.
@@ -218,7 +242,7 @@ public class DataPageIO extends PageIO {
         int indirectCnt = getIndirectCount(pageAddr);
         int firstEntryOff = getFirstEntryOffset(pageAddr);
 
-        return firstEntryOff - (ITEMS_OFF + ITEM_SIZE * (directCnt + indirectCnt));
+        return firstEntryOff - (ITEMS_OFF + ITEM_SIZE * (directCnt + indirectCnt)) - (ITEM_SIZE + PAYLOAD_LEN_SIZE + LINK_SIZE);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/a4256544/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImpl2SelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImpl2SelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImpl2SelfTest.java
index 6bac890..ce3595e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImpl2SelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListImpl2SelfTest.java
@@ -90,7 +90,7 @@ public class FreeListImpl2SelfTest extends GridCommonAbstractTest {
     public void testCompact() throws Exception {
         pageMem = createPageMemory(1024);
 
-        FreeListImpl2 fl = new FreeListImpl2(1, "freelist", pageMem, null, null, 0, true);
+        FreeListImpl2 fl = new FreeListImpl2(log, 1, "freelist", pageMem, null, null, 0, true);
         fl.rnd = new Random();
 
         for (int iter = 0; iter < 100_000; iter++) {
@@ -121,7 +121,7 @@ public class FreeListImpl2SelfTest extends GridCommonAbstractTest {
     public void testCompact1() throws Exception {
         pageMem = createPageMemory(1024);
 
-        FreeListImpl2 fl = new FreeListImpl2(1, "freelist", pageMem, null, null, 0, true);
+        FreeListImpl2 fl = new FreeListImpl2(log, 1, "freelist", pageMem, null, null, 0, true);
 
         fl.rnd = new Random(1);
         fl.log = log;
@@ -171,7 +171,7 @@ public class FreeListImpl2SelfTest extends GridCommonAbstractTest {
 
             assert id != 0;
 
-            pl.put(pageMem.page(1, id));
+            pl.put(pageMem.page(1, id), 0, 0);
 
             ids.push(id);
         }
@@ -206,7 +206,7 @@ public class FreeListImpl2SelfTest extends GridCommonAbstractTest {
             System.out.println("Iter: " + i);
 
             for (Long id : ids)
-                pl.put(pageMem.page(1, id));
+                pl.put(pageMem.page(1, id), 0, 0);
 
             final int THREADS = 16;
 
@@ -258,7 +258,7 @@ public class FreeListImpl2SelfTest extends GridCommonAbstractTest {
             System.out.println("Iter: " + i);
 
             for (Long id : ids)
-                pl.put(pageMem.page(1, id));
+                pl.put(pageMem.page(1, id), 0, 0);
 
             final int THREADS = 16;
 
@@ -279,7 +279,7 @@ public class FreeListImpl2SelfTest extends GridCommonAbstractTest {
                             takeCnt.incrementAndGet();
 
                             if (ThreadLocalRandom.current().nextBoolean()) {
-                                pl.put(page);
+                                pl.put(page, 0, 0);
 
                                 putCnt.incrementAndGet();
                             }