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 2020/12/31 07:18:37 UTC

[ignite] branch master updated: IGNITE-13928 Defragmentation process handles all SQL indexes - Fixes #8622.

This is an automated email from the ASF dual-hosted git repository.

sergeychugunov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new b8adbb0  IGNITE-13928 Defragmentation process handles all SQL indexes - Fixes #8622.
b8adbb0 is described below

commit b8adbb07af94cc61ac9dc225cb83b644b8f22e86
Author: Semyon Danilov <sa...@yandex.ru>
AuthorDate: Thu Dec 31 09:46:21 2020 +0300

    IGNITE-13928 Defragmentation process handles all SQL indexes - Fixes #8622.
    
    Signed-off-by: Sergey Chugunov <se...@gmail.com>
---
 .../CachePartitionDefragmentationManager.java      |   2 -
 .../cache/persistence/tree/io/BPlusMetaIO.java     |  31 ++++-
 .../persistence/IgnitePdsDefragmentationTest.java  |  68 +++++++++-
 .../defragmentation/DefragmentationMXBeanTest.java |   1 -
 .../ignite/testsuites/IgnitePdsTestSuite4.java     |   2 +
 .../processors/query/h2/database/H2Tree.java       |  34 ++++-
 .../defragmentation/IndexingDefragmentation.java   | 139 +++++++++++----------
 .../IgnitePdsIndexingDefragmentationTest.java      |  13 +-
 8 files changed, 213 insertions(+), 77 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/CachePartitionDefragmentationManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/CachePartitionDefragmentationManager.java
index b1682ba..d4833af 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/CachePartitionDefragmentationManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/CachePartitionDefragmentationManager.java
@@ -265,8 +265,6 @@ public class CachePartitionDefragmentationManager {
 
         try {
             // Now the actual process starts.
-            TreeIterator treeIter = new TreeIterator(pageSize);
-
             IgniteInternalFuture<?> idxDfrgFut = null;
             DataPageEvictionMode prevPageEvictionMode = null;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java
index 196ffca..6bc0066 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/BPlusMetaIO.java
@@ -309,12 +309,18 @@ public class BPlusMetaIO extends PageIO {
      * @param pageAddr Page address.
      * @param unwrappedPk unwrapped primary key of this tree flag.
      * @param inlineObjSupported inline POJO by created tree flag.
+     * @param inlineObjHash Whether Java objects should be inlined as hash or as bytes array.
      */
-    public void setFlags(long pageAddr, boolean unwrappedPk, boolean inlineObjSupported) {
+    public void setFlags(
+        long pageAddr,
+        boolean unwrappedPk,
+        boolean inlineObjSupported,
+        boolean inlineObjHash) {
         assert supportFlags();
 
         long flags = unwrappedPk ? FLAG_UNWRAPPED_PK : 0;
         flags |= inlineObjSupported ? FLAG_INLINE_OBJECT_SUPPORTED : 0;
+        flags |= inlineObjHash ? FLAG_INLINE_OBJECT_HASH : 0;
 
         PageUtils.putLong(pageAddr, FLAGS_OFFSET, flags);
     }
@@ -355,6 +361,27 @@ public class BPlusMetaIO extends PageIO {
 
         ioNew.setInlineSize(pageAddr, inlineSize);
         ioNew.setCreatedVersion(pageAddr, IgniteVersionUtils.VER);
-        ioNew.setFlags(pageAddr, unwrappedPk, inlineObjSupported);
+        ioNew.setFlags(pageAddr, unwrappedPk, inlineObjSupported, false);
+    }
+
+    /**
+     * Set meta page values.
+     * @param pageAddr Page address.
+     * @param inlineSize Inline size.
+     * @param unwrappedPk Unwrap PK flag.
+     * @param inlineObjSupported Supports inline object flag.
+     * @param inlineObjHash Supports inline object hash flag.
+     */
+    public static void setValues(
+        long pageAddr,
+        int inlineSize,
+        boolean unwrappedPk,
+        boolean inlineObjSupported,
+        boolean inlineObjHash
+    ) {
+        BPlusMetaIO ioNew = VERSIONS.latest();
+
+        ioNew.setInlineSize(pageAddr, inlineSize);
+        ioNew.setFlags(pageAddr, unwrappedPk, inlineObjSupported, inlineObjHash);
     }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDefragmentationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDefragmentationTest.java
index 25037d3..4a2f115 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDefragmentationTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsDefragmentationTest.java
@@ -25,7 +25,9 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -39,6 +41,7 @@ import javax.cache.expiry.ExpiryPolicy;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteState;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.IgnitionListener;
@@ -53,8 +56,11 @@ import org.apache.ignite.failure.StopNodeFailureHandler;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.maintenance.MaintenanceFileStore;
+import org.apache.ignite.internal.pagemem.store.PageStoreCollection;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.persistence.defragmentation.DefragmentationFileUtils;
 import org.apache.ignite.internal.processors.cache.persistence.file.FileIOFactory;
+import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStore;
 import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
 import org.apache.ignite.internal.util.lang.IgniteThrowableConsumer;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -64,6 +70,7 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
 
 import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.internal.pagemem.PageIdAllocator.INDEX_PARTITION;
 import static org.apache.ignite.internal.processors.cache.persistence.defragmentation.DefragmentationFileUtils.defragmentationCompletionMarkerFile;
 import static org.apache.ignite.internal.processors.cache.persistence.defragmentation.DefragmentationFileUtils.defragmentedIndexFile;
 import static org.apache.ignite.internal.processors.cache.persistence.defragmentation.DefragmentationFileUtils.defragmentedPartFile;
@@ -243,6 +250,23 @@ public class IgnitePdsDefragmentationTest extends GridCommonAbstractTest {
         validateLeftovers(workDir);
     }
 
+    protected long[] partitionSizes(CacheGroupContext grp) {
+        final int grpId = grp.groupId();
+
+        return IntStream.concat(
+            IntStream.of(INDEX_PARTITION),
+            IntStream.range(0, grp.shared().affinity().affinity(grpId).partitions())
+        ).mapToLong(p -> {
+            try {
+                final FilePageStore store = (FilePageStore) ((PageStoreCollection) grp.shared().pageStore()).getStore(grpId, p);
+
+                return new File(store.getFileAbsolutePath()).length();
+            } catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }).toArray();
+    }
+
     /**
      * @return Working directory for cache group {@link IgnitePdsDefragmentationTest#GRP_NAME}.
      * @throws IgniteCheckedException If failed for some reason, like if it's a file instead of directory.
@@ -280,12 +304,19 @@ public class IgnitePdsDefragmentationTest extends GridCommonAbstractTest {
     }
 
     /** */
-    protected void createMaintenanceRecord() throws IgniteCheckedException {
+    protected void createMaintenanceRecord(String... cacheNames) throws IgniteCheckedException {
         IgniteEx grid = grid(0);
 
         MaintenanceRegistry mntcReg = grid.context().maintenanceRegistry();
 
-        mntcReg.registerMaintenanceTask(toStore(Collections.singletonList(DEFAULT_CACHE_NAME)));
+        final List<String> caches = new ArrayList<>();
+
+        caches.add(DEFAULT_CACHE_NAME);
+
+        if (cacheNames != null && cacheNames.length != 0)
+            caches.addAll(Arrays.asList(cacheNames));
+
+        mntcReg.registerMaintenanceTask(toStore(caches));
     }
 
     /**
@@ -590,4 +621,35 @@ public class IgnitePdsDefragmentationTest extends GridCommonAbstractTest {
                 assertNotNull(val);
         }
     }
+
+    /**
+     * Start node, wait for defragmentation and validate that sizes of caches are less than those before the defragmentation.
+     * @param gridId Idx of ignite grid.
+     * @param groups Cache groups to check.
+     * @throws Exception If failed.
+     */
+    protected void defragmentAndValidateSizesDecreasedAfterDefragmentation(int gridId, CacheGroupContext... groups) throws Exception {
+        for (CacheGroupContext grp : groups) {
+            final long[] oldPartLen = partitionSizes(grp);
+
+            startGrid(0);
+
+            waitForDefragmentation(0);
+
+            stopGrid(0);
+
+            final long[] newPartLen = partitionSizes(grp);
+
+            boolean atLeastOneSmaller = false;
+
+            for (int p = 0; p < oldPartLen.length; p++) {
+                assertTrue(newPartLen[p] <= oldPartLen[p]);
+
+                if (newPartLen[p] < oldPartLen[p])
+                    atLeastOneSmaller = true;
+            }
+
+            assertTrue(atLeastOneSmaller);
+        }
+    }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/DefragmentationMXBeanTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/DefragmentationMXBeanTest.java
index f1e5c77..eb97728 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/DefragmentationMXBeanTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/defragmentation/DefragmentationMXBeanTest.java
@@ -288,7 +288,6 @@ public class DefragmentationMXBeanTest extends GridCommonAbstractTest {
         assertEquals(status1.getStartTs(), mxBean.startTime());
 
         assertTrue(mxBean.inProgress());
-        assertEquals(126, mxBean.processedPartitions());
         final int totalPartitions = status1.getTotalPartitions();
         assertEquals(totalPartitions, mxBean.totalPartitions());
 
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite4.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite4.java
index 392c2b9..2c04070 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite4.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite4.java
@@ -52,6 +52,7 @@ import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsStart
 import org.apache.ignite.internal.processors.cache.persistence.db.IgnitePdsTransactionsHangTest;
 import org.apache.ignite.internal.processors.cache.persistence.db.wal.HistoricalReservationTest;
 import org.apache.ignite.internal.processors.cache.persistence.db.wal.WalRebalanceRestartTest;
+import org.apache.ignite.internal.processors.cache.persistence.defragmentation.DefragmentationMXBeanTest;
 import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.PageLockTrackerManagerTest;
 import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.SharedPageLockTrackerTest;
 import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.dumpprocessors.ToFileDumpProcessorTest;
@@ -131,6 +132,7 @@ public class IgnitePdsTestSuite4 {
         GridTestUtils.addTestIfNeeded(suite, IgnitePdsDefragmentationTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, IgnitePdsDefragmentationRandomLruEvictionTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, IgnitePdsDefragmentationEncryptionTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, DefragmentationMXBeanTest.class, ignoredTests);
 
         GridTestUtils.addTestIfNeeded(suite, PendingTreeCorruptionTest.class, ignoredTests);
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
index 4fb9a41..1abb8c3 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
@@ -453,7 +453,7 @@ public class H2Tree extends BPlusTree<H2Row, H2Row> {
      * @return Inline size.
      * @throws IgniteCheckedException If failed.
      */
-    private MetaPageInfo getMetaInfo() throws IgniteCheckedException {
+    public MetaPageInfo getMetaInfo() throws IgniteCheckedException {
         final long metaPage = acquirePage(metaPageId);
 
         try {
@@ -508,6 +508,38 @@ public class H2Tree extends BPlusTree<H2Row, H2Row> {
         }
     }
 
+    /**
+     * Copy info from another meta page.
+     * @param info Meta page info.
+     * @throws IgniteCheckedException If failed.
+     */
+    public void copyMetaInfo(MetaPageInfo info) throws IgniteCheckedException {
+        final long metaPage = acquirePage(metaPageId);
+
+        try {
+            long pageAddr = writeLock(metaPageId, metaPage); // Meta can't be removed.
+
+            assert pageAddr != 0 : "Failed to read lock meta page [metaPageId=" +
+                U.hexLong(metaPageId) + ']';
+
+            try {
+                BPlusMetaIO.setValues(
+                    pageAddr,
+                    info.inlineSize,
+                    info.useUnwrappedPk,
+                    info.inlineObjSupported,
+                    info.inlineObjHash
+                );
+            }
+            finally {
+                writeUnlock(metaPageId, metaPage, pageAddr, true);
+            }
+        }
+        finally {
+            releasePage(metaPageId, metaPage);
+        }
+    }
+
     /** {@inheritDoc} */
     @SuppressWarnings("ForLoopReplaceableByForEach")
     @Override protected int compare(BPlusIO<H2Row> io, long pageAddr, int idx,
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/defragmentation/IndexingDefragmentation.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/defragmentation/IndexingDefragmentation.java
index d3a33e6..3ffbb68 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/defragmentation/IndexingDefragmentation.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/defragmentation/IndexingDefragmentation.java
@@ -17,11 +17,11 @@
 
 package org.apache.ignite.internal.processors.query.h2.defragmentation;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.pagemem.PageMemory;
@@ -56,6 +56,7 @@ import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.opt.H2CacheRow;
 import org.apache.ignite.internal.processors.query.h2.opt.H2Row;
 import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.util.collection.IntHashMap;
 import org.apache.ignite.internal.util.collection.IntMap;
 import org.apache.ignite.thread.IgniteThreadPoolExecutor;
 import org.h2.index.Index;
@@ -164,88 +165,98 @@ public class IndexingDefragmentation {
             GridH2RowDescriptor rowDesc = table.rowDescriptor();
 
             List<Index> indexes = table.getIndexes();
-            H2TreeIndex oldH2Idx = (H2TreeIndex)indexes.get(2);
 
-            int segments = oldH2Idx.segmentsCount();
+            final List<H2TreeIndex> oldIndexes = indexes.stream()
+                .filter(index -> index instanceof H2TreeIndex)
+                .map(H2TreeIndex.class::cast)
+                .collect(Collectors.toList());
 
-            H2Tree firstTree = oldH2Idx.treeForRead(0);
+            for (H2TreeIndex oldH2Idx : oldIndexes) {
+                int segments = oldH2Idx.segmentsCount();
 
-            PageIoResolver pageIoRslvr = pageAddr -> {
-                PageIO io = PageIoResolver.DEFAULT_PAGE_IO_RESOLVER.resolve(pageAddr);
+                H2Tree firstTree = oldH2Idx.treeForRead(0);
 
-                if (io instanceof BPlusMetaIO)
-                    return io;
+                PageIoResolver pageIoRslvr = pageAddr -> {
+                    PageIO io = PageIoResolver.DEFAULT_PAGE_IO_RESOLVER.resolve(pageAddr);
 
-                //noinspection unchecked,rawtypes,rawtypes
-                return wrap((BPlusIO)io);
-            };
+                    if (io instanceof BPlusMetaIO)
+                        return io;
 
-            H2TreeIndex newIdx = H2TreeIndex.createIndex(
-                cctx,
-                null,
-                table,
-                oldH2Idx.getName(),
-                firstTree.getPk(),
-                firstTree.getAffinityKey(),
-                Arrays.asList(firstTree.cols()),
-                Arrays.asList(firstTree.cols()),
-                oldH2Idx.inlineSize(),
-                segments,
-                newCachePageMemory,
-                newCtx.offheap(),
-                pageIoRslvr,
-                log
-            );
+                    //noinspection unchecked,rawtypes,rawtypes
+                    return wrap((BPlusIO)io);
+                };
+
+                H2TreeIndex newIdx = H2TreeIndex.createIndex(
+                    cctx,
+                    null,
+                    table,
+                    oldH2Idx.getName(),
+                    firstTree.getPk(),
+                    firstTree.getAffinityKey(),
+                    Arrays.asList(firstTree.cols()),
+                    Arrays.asList(firstTree.cols()),
+                    oldH2Idx.inlineSize(),
+                    segments,
+                    newCachePageMemory,
+                    newCtx.offheap(),
+                    pageIoRslvr,
+                    log
+                );
 
-            for (int i = 0; i < segments; i++) {
-                H2Tree tree = oldH2Idx.treeForRead(i);
+                for (int i = 0; i < segments; i++) {
+                    H2Tree tree = oldH2Idx.treeForRead(i);
+                    final H2Tree.MetaPageInfo oldInfo = tree.getMetaInfo();
 
-                newIdx.treeForRead(i).enableSequentialWriteMode();
+                    final H2Tree newTree = newIdx.treeForRead(i);
+                    newTree.copyMetaInfo(oldInfo);
 
-                treeIterator.iterate(tree, oldCachePageMem, (theTree, io, pageAddr, idx) -> {
-                    cancellationChecker.run();
+                    newTree.enableSequentialWriteMode();
 
-                    if (System.currentTimeMillis() - lastCpLockTs.get() >= cpLockThreshold) {
-                        cpLock.checkpointReadUnlock();
+                    treeIterator.iterate(tree, oldCachePageMem, (theTree, io, pageAddr, idx) -> {
+                        cancellationChecker.run();
 
-                        cpLock.checkpointReadLock();
+                        if (System.currentTimeMillis() - lastCpLockTs.get() >= cpLockThreshold) {
+                            cpLock.checkpointReadUnlock();
 
-                        lastCpLockTs.set(System.currentTimeMillis());
-                    }
+                            cpLock.checkpointReadLock();
 
-                    assert 1 == io.getVersion()
-                        : "IO version " + io.getVersion() + " is not supported by current defragmentation algorithm." +
-                        " Please implement copying of tree in a new format.";
+                            lastCpLockTs.set(System.currentTimeMillis());
+                        }
 
-                    BPlusIO<H2Row> h2IO = wrap(io);
+                        assert 1 == io.getVersion()
+                            : "IO version " + io.getVersion() + " is not supported by current defragmentation algorithm." +
+                            " Please implement copying of tree in a new format.";
 
-                    H2Row row = theTree.getRow(h2IO, pageAddr, idx);
+                        BPlusIO<H2Row> h2IO = wrap(io);
 
-                    if (row instanceof H2CacheRowWithIndex) {
-                        H2CacheRowWithIndex h2CacheRow = (H2CacheRowWithIndex)row;
+                        H2Row row = theTree.getRow(h2IO, pageAddr, idx);
 
-                        CacheDataRow cacheDataRow = h2CacheRow.getRow();
+                        if (row instanceof H2CacheRowWithIndex) {
+                            H2CacheRowWithIndex h2CacheRow = (H2CacheRowWithIndex)row;
 
-                        int partition = cacheDataRow.partition();
+                            CacheDataRow cacheDataRow = h2CacheRow.getRow();
 
-                        long link = h2CacheRow.link();
+                            int partition = cacheDataRow.partition();
 
-                        LinkMap map = mappingByPartition.get(partition);
+                            long link = h2CacheRow.link();
 
-                        long newLink = map.get(link);
+                            LinkMap map = mappingByPartition.get(partition);
 
-                        H2CacheRowWithIndex newRow = H2CacheRowWithIndex.create(
-                            rowDesc,
-                            newLink,
-                            h2CacheRow,
-                            ((H2RowLinkIO)io).storeMvccInfo()
-                        );
+                            long newLink = map.get(link);
 
-                        newIdx.putx(newRow);
-                    }
+                            H2CacheRowWithIndex newRow = H2CacheRowWithIndex.create(
+                                rowDesc,
+                                newLink,
+                                h2CacheRow,
+                                ((H2RowLinkIO)io).storeMvccInfo()
+                            );
 
-                    return true;
-                });
+                            newIdx.putx(newRow);
+                        }
+
+                        return true;
+                    });
+                }
             }
 
             return true;
@@ -268,7 +279,7 @@ public class IndexingDefragmentation {
 
         int off = io.offset(idx);
 
-        List<Value> values = new ArrayList<>();
+        IntMap<Value> values = new IntHashMap<>();
 
         if (inlineIdxs != null) {
             int fieldOff = 0;
@@ -280,7 +291,9 @@ public class IndexingDefragmentation {
 
                 fieldOff += inlineIndexColumn.inlineSizeOf(value);
 
-                values.add(value);
+                final int columnIndex = inlineIndexColumn.columnIndex();
+
+                values.put(columnIndex, value);
             }
         }
 
@@ -443,10 +456,10 @@ public class IndexingDefragmentation {
      */
     private static class H2CacheRowWithIndex extends H2CacheRow {
         /** List of index values. */
-        private final List<Value> values;
+        private final IntMap<Value> values;
 
         /** Constructor. */
-        public H2CacheRowWithIndex(GridH2RowDescriptor desc, CacheDataRow row, List<Value> values) {
+        public H2CacheRowWithIndex(GridH2RowDescriptor desc, CacheDataRow row, IntMap<Value> values) {
             super(desc, row);
             this.values = values;
         }
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsIndexingDefragmentationTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsIndexingDefragmentationTest.java
index 4ce570e..586c155 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsIndexingDefragmentationTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsIndexingDefragmentationTest.java
@@ -31,12 +31,14 @@ import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.IgniteCacheUpdateSqlQuerySelfTest;
 import org.apache.ignite.internal.processors.cache.persistence.defragmentation.DefragmentationFileUtils;
 import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
 import org.apache.ignite.internal.processors.query.GridQueryProcessor;
 import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
+import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.visor.verify.ValidateIndexesClosure;
 import org.apache.ignite.internal.visor.verify.VisorValidateIndexesJobResult;
@@ -247,6 +249,8 @@ public class IgnitePdsIndexingDefragmentationTest extends IgnitePdsDefragmentati
 
         cache.query(new SqlFieldsQuery("CREATE TABLE TEST (ID INT PRIMARY KEY, VAL_INT INT, VAL_OBJ LONG)"));
 
+        final String cacheName = "SQL_default_TEST";
+
         cache.query(new SqlFieldsQuery("CREATE INDEX TEST_VAL_INT ON TEST(VAL_INT)"));
 
         cache.query(new SqlFieldsQuery("CREATE INDEX TEST_VAL_OBJ ON TEST(VAL_OBJ)"));
@@ -256,15 +260,14 @@ public class IgnitePdsIndexingDefragmentationTest extends IgnitePdsDefragmentati
 
         cache.query(new SqlFieldsQuery("DELETE FROM TEST WHERE MOD(ID, 2) = 0"));
 
-        createMaintenanceRecord();
+        createMaintenanceRecord(cacheName);
+
+        CacheGroupContext grp = grid(0).context().cache().cacheGroup(CU.cacheId(cacheName));
 
         // Restart first time.
         stopGrid(0);
 
-        startGrid(0);
-
-        // Restart second time.
-        stopGrid(0);
+        defragmentAndValidateSizesDecreasedAfterDefragmentation(0, grp);
 
         startGrid(0);