You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2018/11/28 13:56:01 UTC

[1/4] ignite git commit: Minors.

Repository: ignite
Updated Branches:
  refs/heads/ignite-10291 10f5f9e32 -> 8a2a8610a


Minors.


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

Branch: refs/heads/ignite-10291
Commit: b35634bf58a3deb903f4ba9590c36eaa16d8fa4a
Parents: 10f5f9e
Author: devozerov <vo...@gridgain.com>
Authored: Wed Nov 28 12:51:32 2018 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Wed Nov 28 12:51:32 2018 +0300

----------------------------------------------------------------------
 .../ignite/internal/processors/query/GridQueryProcessor.java  | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/b35634bf/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index d3f2930..95ff3d5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -1764,9 +1764,10 @@ public class GridQueryProcessor extends GridProcessorAdapter {
             GridCompoundFuture<Object, ?> fut = new GridCompoundFuture<Object, Object>();
 
             for (Map.Entry<QueryTypeIdKey, QueryTypeDescriptorImpl> e : types.entrySet()) {
-                if (cacheIds.contains(CU.cacheId(e.getKey().cacheName())) &&
-                    processedCacheNames.add(e.getKey().cacheName()))
-                    fut.add(rebuildIndexesFromHash(e.getKey().cacheName(), e.getValue()));
+                String cacheName = e.getKey().cacheName();
+
+                if (cacheIds.contains(CU.cacheId(cacheName)) && processedCacheNames.add(cacheName))
+                    fut.add(rebuildIndexesFromHash(cacheName, e.getValue()));
             }
 
             fut.markInitialized();


[4/4] ignite git commit: It works.

Posted by vo...@apache.org.
It works.


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

Branch: refs/heads/ignite-10291
Commit: 8a2a8610a8439b8fedc3271153379d0d57058128
Parents: 4860158
Author: devozerov <vo...@gridgain.com>
Authored: Wed Nov 28 16:55:52 2018 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Wed Nov 28 16:55:52 2018 +0300

----------------------------------------------------------------------
 .../java/org/apache/ignite/jdbc/Reproducer.java | 164 +++++++++++++++++++
 .../processors/query/h2/IgniteH2Indexing.java   |   9 +-
 .../processors/query/h2/database/H2Tree.java    |  14 ++
 .../query/h2/database/H2TreeIndex.java          |   4 +-
 4 files changed, 188 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/8a2a8610/modules/clients/src/test/java/org/apache/ignite/jdbc/Reproducer.java
----------------------------------------------------------------------
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/Reproducer.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/Reproducer.java
new file mode 100644
index 0000000..1899a45
--- /dev/null
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/Reproducer.java
@@ -0,0 +1,164 @@
+package org.apache.ignite.jdbc;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import javax.cache.Cache;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteJdbcThinDriver;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+public class Reproducer extends GridCommonAbstractTest {
+
+    static final String NODE_1 = "node1";
+    static final String NODE_2 = "node2";
+
+    public static final String CREATE_TABLE = "CREATE TABLE PERSON (\n" +
+        " FIRST_NAME VARCHAR,\n" +
+        " LAST_NAME VARCHAR,\n" +
+        " ADDRESS VARCHAR,\n" +
+        " LANG VARCHAR,\n" +
+        " BIRTH_DATE TIMESTAMP,\n" +
+        " CONSTRAINT PK_PESON PRIMARY KEY (FIRST_NAME,LAST_NAME,ADDRESS,LANG)\n" +
+        ") WITH \"key_type=PersonKeyType, CACHE_NAME=PersonCache, value_type=PersonValueType, AFFINITY_KEY=FIRST_NAME,template=PARTITIONED,backups=1\"";
+
+    public static final String CREATE_INDEX = "create index PERSON_FIRST_NAME_IDX on  PERSON(FIRST_NAME)";
+
+    public static final String QUERY = "select * from PERSON use index(PERSON_FIRST_NAME_IDX) \n" +
+        "where \n" +
+        "FIRST_NAME=?\n" +
+        "and LAST_NAME=?\n" +
+        "and ADDRESS=?\n" +
+        "and LANG  = ? ";
+
+    public static void main(String[] args) throws Exception {
+        cleanPersistenceDir(); // TODO: Enable
+
+        System.out.println(CREATE_TABLE);
+        System.out.println(CREATE_INDEX);
+        System.out.println(QUERY);
+
+        Ignite server1 = Ignition.start(getCfg(NODE_1));
+        Ignite server2 = Ignition.start(getCfg(NODE_2));
+        Ignite client = Ignition.start(getCfg("client").setClientMode(true));
+        client.cluster().active(true);
+
+        createTable();
+        fill();
+
+        server2.close();
+
+        createIndex();
+
+        server2 = Ignition.start(getCfg(NODE_2));
+
+        findRows(client);
+    }
+
+    static IgniteConfiguration getCfg(String id) {
+        IgniteConfiguration cfg = new IgniteConfiguration();
+        TcpDiscoverySpi discovery = new TcpDiscoverySpi();
+        TcpDiscoveryVmIpFinder finder = new TcpDiscoveryVmIpFinder();
+
+        cfg.setConsistentId(id);
+        cfg.setIgniteInstanceName(id);
+
+        cfg.setDataStorageConfiguration(new DataStorageConfiguration().setDefaultDataRegionConfiguration(new DataRegionConfiguration().setPersistenceEnabled(true)));
+
+        finder.setAddresses(Arrays.asList("127.0.0.1:47500..47509"));
+        discovery.setIpFinder(finder);
+        cfg.setDiscoverySpi(discovery);
+
+        return cfg;
+    }
+
+    private static void createTable() throws SQLException {
+        try (Connection conn = new IgniteJdbcThinDriver().connect("jdbc:ignite:thin://localhost", new Properties())) {
+            conn.createStatement().execute(CREATE_TABLE);
+        }
+    }
+
+    private static void createIndex() throws SQLException {
+        try (Connection conn = new IgniteJdbcThinDriver().connect("jdbc:ignite:thin://localhost", new Properties())) {
+            conn.createStatement().execute(CREATE_INDEX);
+        }
+    }
+
+    private static void fill() throws SQLException {
+        try (Connection conn = new IgniteJdbcThinDriver().connect("jdbc:ignite:thin://localhost", new Properties())) {
+
+            PreparedStatement st = conn.prepareStatement("insert into Person(LANG,FIRST_NAME,ADDRESS,LAST_NAME,BIRTH_DATE)\n" +
+                "values(?,?,?,?,?)");
+
+            for (int i = 0; i < 1; i++) {
+                try {
+                    String s = String.valueOf(i);
+                    st.setString(1, s);
+                    st.setString(2, s);
+                    st.setString(3, s);
+                    st.setString(4, s);
+                    st.setTimestamp(6, new Timestamp(System.currentTimeMillis()));
+                    st.executeUpdate();
+                }
+                catch (Exception e) {
+                    System.out.println(e.getMessage());
+                }
+            }
+        }
+        System.out.println("Tables with data are created.");
+    }
+
+    static void findRows(Ignite ignite) throws SQLException {
+        AtomicInteger total = new AtomicInteger();
+        AtomicInteger fails = new AtomicInteger();
+        try (Connection conn = new IgniteJdbcThinDriver().connect("jdbc:ignite:thin://localhost", new Properties())) {
+            PreparedStatement st = conn.prepareStatement(QUERY);
+
+            IgniteCache<BinaryObject, BinaryObject> cache = ignite.cache("PersonCache").withKeepBinary();
+            cache.forEach(new Consumer<Cache.Entry<BinaryObject, BinaryObject>>() {
+                @Override public void accept(Cache.Entry<BinaryObject, BinaryObject> entry) {
+                    BinaryObject key = entry.getKey();
+                    try {
+                        st.setString(1, key.field("FIRST_NAME"));
+                        st.setString(2, key.field("LAST_NAME"));
+                        st.setString(3, key.field("ADDRESS"));
+                        st.setString(4, key.field("LANG"));
+                        ResultSet rs = st.executeQuery();
+                        if (!rs.next()) {
+                            System.out.println("!!!Unable to find row by key:" + key);
+                            fails.incrementAndGet();
+                        }
+
+                        total.incrementAndGet();
+                        rs.close();
+                    }
+                    catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+
+            });
+
+            st.close();
+
+        }
+        System.out.println("Finished");
+        System.out.println("Total:"+total.get()+" fails:"+fails.get());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/8a2a8610/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 9dc15e6..6e87655 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -2705,13 +2705,18 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         }
         else {
             // Otherwise iterate over tables looking for missing indexes.
-            clo = new IndexRebuildPartialClosure();
+            IndexRebuildPartialClosure clo0 = new IndexRebuildPartialClosure();
 
             for (H2TableDescriptor tblDesc : tables(cctx.name())) {
                 assert tblDesc.table() != null;
 
-                tblDesc.table().collectIndexesForPartialRebuild((IndexRebuildPartialClosure)clo);
+                tblDesc.table().collectIndexesForPartialRebuild(clo0);
             }
+
+            if (clo0.hasIndexes())
+                clo = clo0;
+            else
+                return null;
         }
 
         // Closure prepared, do rebuild.

http://git-wip-us.apache.org/repos/asf/ignite/blob/8a2a8610/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
----------------------------------------------------------------------
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 71cea7e..f2a69bb 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
@@ -108,8 +108,12 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
     /** */
     private final IgniteLogger log;
 
+    /** Whether PK is stored in unwrapped form. */
     private boolean unwrappedPk;
 
+    /** Whether index was created from scratch during owning node lifecycle. */
+    private final boolean created;
+
     /**
      * Constructor.
      *
@@ -200,6 +204,8 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
         this.log = log;
 
         initTree(initNew, inlineSize);
+
+        this.created = initNew;
     }
 
     /**
@@ -556,6 +562,14 @@ public abstract class H2Tree extends BPlusTree<GridH2SearchRow, GridH2Row> {
      */
     public abstract int compareValues(Value v1, Value v2);
 
+    /**
+     * @return {@code True} if index was created during curren node's lifetime, {@code False} if it was restored from
+     * disk.
+     */
+    public boolean created() {
+        return created;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(H2Tree.class, this, "super", super.toString());

http://git-wip-us.apache.org/repos/asf/ignite/blob/8a2a8610/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index 48d4cc4..25a5c61 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -224,8 +224,9 @@ public class H2TreeIndex extends GridH2IndexBase {
 
         for (int i = 0; i < segments.length; i++) {
             try {
+                H2Tree segment = segments[i];
 
-                if (!metaPageExists(i))
+                if (segment.created())
                     return true;
             }
             catch (Exception e) {
@@ -572,6 +573,7 @@ public class H2TreeIndex extends GridH2IndexBase {
      * @return {@code True} if exists.
      * @throws IgniteCheckedException If failed.
      */
+    // TODO: Remove?
     private boolean metaPageExists(int segIdx) throws IgniteCheckedException {
         return cctx.offheap().rootPageForIndexExists(cctx.cacheId(), treeName, segIdx);
     }


[3/4] ignite git commit: Minors.

Posted by vo...@apache.org.
Minors.


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

Branch: refs/heads/ignite-10291
Commit: 48601580e0a87bf2c5c759f736fce3543a5e105c
Parents: 898e82a
Author: devozerov <vo...@gridgain.com>
Authored: Wed Nov 28 15:29:37 2018 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Wed Nov 28 15:29:37 2018 +0300

----------------------------------------------------------------------
 .../processors/cache/IgniteCacheOffheapManagerImpl.java       | 3 ++-
 .../processors/cache/persistence/GridCacheOffheapManager.java | 3 ++-
 .../testframework/junits/common/GridCommonAbstractTest.java   | 2 +-
 .../ignite/internal/processors/query/h2/IgniteH2Indexing.java | 7 +++----
 4 files changed, 8 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/48601580/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 eeae562..7a91563 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
@@ -1154,7 +1154,8 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
     }
 
     /** {@inheritDoc} */
-    @Override public boolean rootPageForIndexExists(int cacheId, String idxName, int segment) {
+    @Override public boolean rootPageForIndexExists(int cacheId, String idxName, int segment)
+        throws IgniteCheckedException {
         assert false : "Should not be called in in-memory mode.";
 
         return false;

http://git-wip-us.apache.org/repos/asf/ignite/blob/48601580/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
index 73bf50a..b807342 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
@@ -846,7 +846,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /** {@inheritDoc} */
-    @Override public boolean rootPageForIndexExists(int cacheId, String idxName, int segment) {
+    @Override public boolean rootPageForIndexExists(int cacheId, String idxName, int segment)
+        throws IgniteCheckedException {
         return indexStorage.cacheIndexExists(cacheId, idxName, segment);
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/48601580/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
index c931a0d..883dca7 100755
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
@@ -1725,7 +1725,7 @@ public abstract class GridCommonAbstractTest extends GridAbstractTest {
     /**
      *
      */
-    protected void cleanPersistenceDir() throws Exception {
+    protected static void cleanPersistenceDir() throws Exception {
         assertTrue("Grids are not stopped", F.isEmpty(G.allGrids()));
 
         U.delete(U.resolveWorkDirectory(U.defaultWorkDirectory(), "cp", false));

http://git-wip-us.apache.org/repos/asf/ignite/blob/48601580/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 6582516..9dc15e6 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -2717,14 +2717,14 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         // Closure prepared, do rebuild.
         final GridWorkerFuture<?> fut = new GridWorkerFuture<>();
 
-        markIndexRebuild(cctx.name(), clo, true);
+        markIndexRebuild(cctx.name(), true);
 
         GridWorker worker = new GridWorker(ctx.igniteInstanceName(), "index-rebuild-worker-" + cctx.name(), log) {
             @Override protected void body() {
                 try {
                     rebuildIndexesFromHash0(cctx, clo);
 
-                    markIndexRebuild(cctx.name(), clo, false);
+                    markIndexRebuild(cctx.name(), false);
 
                     fut.onDone();
                 }
@@ -2768,11 +2768,10 @@ public class IgniteH2Indexing implements GridQueryIndexing {
      * @param cacheName Cache name.
      * @param val Value.
      */
-    private void markIndexRebuild(String cacheName, IndexRebuildClosure clo, boolean val) {
+    private void markIndexRebuild(String cacheName, boolean val) {
         for (H2TableDescriptor tblDesc : tables(cacheName)) {
             assert tblDesc.table() != null;
 
-            // TODO: Mark only some indexes.
             tblDesc.table().markRebuildFromHashInProgress(val);
         }
     }


[2/4] ignite git commit: Implemented.

Posted by vo...@apache.org.
Implemented.


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

Branch: refs/heads/ignite-10291
Commit: 898e82a27dd8a0ec7773eaa5b1e641c12c6c64cd
Parents: b35634b
Author: devozerov <vo...@gridgain.com>
Authored: Wed Nov 28 15:27:55 2018 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Wed Nov 28 15:27:55 2018 +0300

----------------------------------------------------------------------
 .../cache/IgniteCacheOffheapManager.java        |  11 ++
 .../cache/IgniteCacheOffheapManagerImpl.java    |   7 ++
 .../GridCacheDatabaseSharedManager.java         |  14 ++-
 .../persistence/GridCacheOffheapManager.java    |   5 +
 .../cache/persistence/IndexStorage.java         |  11 ++
 .../cache/persistence/IndexStorageImpl.java     |  62 ++++-------
 .../processors/query/GridQueryIndexing.java     |  16 +--
 .../processors/query/GridQueryProcessor.java    |  82 +++-----------
 ...IgniteClientCacheInitializationFailTest.java |  10 +-
 .../processors/query/h2/IgniteH2Indexing.java   | 107 ++++++++++++++-----
 .../processors/query/h2/IndexBuildClosure.java  |  54 ++++++++++
 .../query/h2/IndexRebuildClosure.java           |  36 +++++++
 .../query/h2/IndexRebuildFullClosure.java       |  55 ++++++++++
 .../query/h2/IndexRebuildPartialClosure.java    |  82 ++++++++++++++
 .../query/h2/RebuildIndexFromHashClosure.java   |  47 --------
 .../query/h2/database/H2TreeIndex.java          |  58 +++++++---
 .../processors/query/h2/opt/GridH2Table.java    |  27 ++++-
 .../query/h2/GridIndexRebuildSelfTest.java      |   7 +-
 18 files changed, 465 insertions(+), 226 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
index ed3697a..409d387 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
@@ -602,6 +602,17 @@ public interface IgniteCacheOffheapManager {
     public void dropRootPageForIndex(int cacheId, String idxName, int segment) throws IgniteCheckedException;
 
     /**
+     * Check if index root page exists.
+     *
+     * @param cacheId Cache ID.
+     * @param idxName Index name.
+     * @param segment Segment.
+     * @return {@code True} if exists.
+     * @throws IgniteCheckedException If failed.
+     */
+    public boolean rootPageForIndexExists(int cacheId, String idxName, int segment) throws IgniteCheckedException;
+
+    /**
      * @param idxName Index name.
      * @return Reuse list for index tree.
      * @throws IgniteCheckedException If failed.

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/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 ca14f68..eeae562 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
@@ -1154,6 +1154,13 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
     }
 
     /** {@inheritDoc} */
+    @Override public boolean rootPageForIndexExists(int cacheId, String idxName, int segment) {
+        assert false : "Should not be called in in-memory mode.";
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
     @Override public ReuseList reuseListForIndex(String idxName) {
         return grp.reuseList();
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
index c9bf673..eb9e4df 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
@@ -137,6 +137,7 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageParti
 import org.apache.ignite.internal.processors.cache.persistence.wal.FileWALPointer;
 import org.apache.ignite.internal.processors.cache.persistence.wal.crc.IgniteDataIntegrityViolationException;
 import org.apache.ignite.internal.processors.port.GridPortRecord;
+import org.apache.ignite.internal.processors.query.GridQueryProcessor;
 import org.apache.ignite.internal.util.GridMultiCollectionWrapper;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.internal.util.future.CountDownFuture;
@@ -1330,20 +1331,17 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
     /** {@inheritDoc} */
     @Override public void rebuildIndexesIfNeeded(GridDhtPartitionsExchangeFuture fut) {
-        if (cctx.kernalContext().query().moduleEnabled()) {
+        GridQueryProcessor qryProc = cctx.kernalContext().query();
+
+        if (qryProc.moduleEnabled()) {
             for (final GridCacheContext cacheCtx : (Collection<GridCacheContext>)cctx.cacheContexts()) {
                 if (cacheCtx.startTopologyVersion().equals(fut.initialVersion())) {
                     final int cacheId = cacheCtx.cacheId();
                     final GridFutureAdapter<Void> usrFut = idxRebuildFuts.get(cacheId);
 
-                    boolean persistentNode = cacheCtx.affinityNode() && cacheCtx.group().persistenceEnabled();
-
-                    // TODO: Index rebuild check.
-
-                    if (!cctx.pageStore().hasIndexStore(cacheCtx.groupId()) && persistentNode) {
-                        IgniteInternalFuture<?> rebuildFut = cctx.kernalContext().query()
-                            .rebuildIndexesFromHash(Collections.singleton(cacheCtx.cacheId()));
+                    IgniteInternalFuture<?> rebuildFut = qryProc.rebuildIndexesFromHash(cacheCtx);
 
+                    if (rebuildFut != null) {
                         assert usrFut != null : "Missing user future for cache: " + cacheCtx.name();
 
                         rebuildFut.listen(new CI1<IgniteInternalFuture>() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
index e1bd8dd..73bf50a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
@@ -846,6 +846,11 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /** {@inheritDoc} */
+    @Override public boolean rootPageForIndexExists(int cacheId, String idxName, int segment) {
+        return indexStorage.cacheIndexExists(cacheId, idxName, segment);
+    }
+
+    /** {@inheritDoc} */
     @Override public ReuseList reuseListForIndex(String idxName) {
         return reuseList;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorage.java
index 295ff00..1e656ef 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorage.java
@@ -65,6 +65,17 @@ public interface IndexStorage {
     public RootPage dropIndex(String idxName) throws IgniteCheckedException;
 
     /**
+     * Check if cache index exists.
+     *
+     * @param cacheId Cache ID.
+     * @param idxName Index name.
+     * @param segment Segment.
+     * @return {@code True} if exists.
+     * @throws IgniteCheckedException If failed.
+     */
+    public boolean cacheIndexExists(Integer cacheId, String idxName, int segment) throws IgniteCheckedException;
+
+    /**
      * Destroy this meta store.
      *
      * @throws IgniteCheckedException  If failed.

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
index bb244ab..4235cae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IndexStorageImpl.java
@@ -33,7 +33,6 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersion
 import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
 import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
 import org.apache.ignite.internal.processors.failure.FailureProcessor;
-import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.Nullable;
 
@@ -107,9 +106,9 @@ public class IndexStorageImpl implements IndexStorage {
     /** {@inheritDoc} */
     @Override public RootPage allocateCacheIndex(Integer cacheId, String idxName, int segment)
         throws IgniteCheckedException {
-        String mangledIdxName = mangleCacheIndexName(cacheId, idxName, segment);
+        String maskedIdxName = maskCacheIndexName(cacheId, idxName, segment);
 
-        return allocateIndex(mangledIdxName);
+        return allocateIndex(maskedIdxName);
     }
 
     /** {@inheritDoc} */
@@ -148,9 +147,9 @@ public class IndexStorageImpl implements IndexStorage {
     /** {@inheritDoc} */
     @Override public RootPage dropCacheIndex(Integer cacheId, String idxName, int segment)
         throws IgniteCheckedException {
-        String mangledIdxName = mangleCacheIndexName(cacheId, idxName, segment);
+        String maskedIdxName = maskCacheIndexName(cacheId, idxName, segment);
 
-        return dropIndex(mangledIdxName);
+        return dropIndex(maskedIdxName);
     }
 
     /** {@inheritDoc} */
@@ -168,17 +167,29 @@ public class IndexStorageImpl implements IndexStorage {
     }
 
     /** {@inheritDoc} */
+    @Override public boolean cacheIndexExists(Integer cacheId, String idxName, int segment)
+        throws IgniteCheckedException {
+        String maskedIdxName = maskCacheIndexName(cacheId, idxName, segment);
+
+        byte[] idxNameBytes = maskedIdxName.getBytes(StandardCharsets.UTF_8);
+
+        IndexItem row = metaTree.findOne(new IndexItem(idxNameBytes, 0));
+
+        return row != null;
+    }
+
+    /** {@inheritDoc} */
     @Override public void destroy() throws IgniteCheckedException {
         metaTree.destroy();
     }
 
     /**
-     * Mange cache index name.
+     * Mask cache index name.
      *
      * @param idxName Index name.
-     * @return Mangled name.
+     * @return Masked name.
      */
-    private String mangleCacheIndexName(Integer cacheId, String idxName, int segment) {
+    private String maskCacheIndexName(Integer cacheId, String idxName, int segment) {
         assert !grpShared || grpId == cacheId;
 
         if (grpShared)
@@ -188,41 +199,6 @@ public class IndexStorageImpl implements IndexStorage {
     }
 
     /**
-     * Unmangle cache index name if possible.
-     *
-     * @param mangledIdxName Mangled index name.
-     * @param expCacheId Expected cache ID.
-     * @return Unmangled index name, {@code null} if doesn't match expected cache ID.
-     * @throws IgniteCheckedException If failed.
-     */
-    @Nullable private String unmangleCacheIndexNameIfPossible(String mangledIdxName, Integer expCacheId)
-        throws IgniteCheckedException {
-        if (grpShared)
-            return mangledIdxName;
-        else {
-            int pos = mangledIdxName.indexOf('_');
-
-            if (pos != -1) {
-                String cacheIdStr = mangledIdxName.substring(0, pos - 1);
-                String idxName = mangledIdxName.substring(pos);
-
-                try {
-                    Integer cacheId = Integer.parseInt(cacheIdStr);
-
-                    if (F.eq(expCacheId, cacheId))
-                        return idxName;
-                }
-                catch (NumberFormatException e) {
-                    throw new IgniteCheckedException("Failed to parse cache ID of mangled index name: " +
-                        mangledIdxName);
-                }
-            }
-
-            return null;
-        }
-    }
-
-    /**
      *
      */
     private static class MetaTree extends BPlusTree<IndexItem, IndexItem> {

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
index dab2516..d1e5d14 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryIndexing.java
@@ -27,6 +27,7 @@ import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
@@ -273,19 +274,12 @@ public interface GridQueryIndexing {
         throws IgniteCheckedException;
 
     /**
-     * Rebuilds all indexes of given type from hash index.
+     * Rebuild indexes for the given cache if necessary.
      *
-     * @param cacheName Cache name.
-     * @throws IgniteCheckedException If failed.
-     */
-    public void rebuildIndexesFromHash(String cacheName) throws IgniteCheckedException;
-
-    /**
-     * Marks all indexes of given type for rebuild from hash index, making them unusable until rebuild finishes.
-     *
-     * @param cacheName Cache name.
+     * @param cctx Cache context.
+     * @return Future completed when index rebuild finished.
      */
-    public void markForRebuildFromHash(String cacheName);
+    public IgniteInternalFuture<?> rebuildIndexesFromHash(GridCacheContext cctx);
 
     /**
      * Returns backup filter.

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 95ff3d5..16b011f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -93,7 +93,6 @@ import org.apache.ignite.internal.processors.query.schema.operation.SchemaIndexD
 import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor;
 import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashSet;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
-import org.apache.ignite.internal.util.future.GridCompoundFuture;
 import org.apache.ignite.internal.util.future.GridFinishedFuture;
 import org.apache.ignite.internal.util.lang.GridCloseableIterator;
 import org.apache.ignite.internal.util.lang.GridClosureException;
@@ -106,8 +105,6 @@ import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.LT;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.internal.util.worker.GridWorker;
-import org.apache.ignite.internal.util.worker.GridWorkerFuture;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteFuture;
 import org.apache.ignite.lang.IgniteInClosure;
@@ -1749,30 +1746,28 @@ public class GridQueryProcessor extends GridProcessorAdapter {
     /**
      * Rebuilds indexes for provided caches from corresponding hash indexes.
      *
-     * @param cacheIds Cache IDs.
+     * @param cctx Cache context.
      * @return Future that will be completed when rebuilding is finished.
      */
-    public IgniteInternalFuture<?> rebuildIndexesFromHash(Set<Integer> cacheIds) {
-        if (!busyLock.enterBusy())
-            return new GridFinishedFuture<>(new NodeStoppingException("Failed to rebuild indexes from hash (grid is stopping)."));
-
-        // Because of alt type ids, there can be few entries in 'types' for a single cache.
-        // In order to avoid processing a cache more than once, let's track processed names.
-        Set<String> processedCacheNames = new HashSet<>();
-
-        try {
-            GridCompoundFuture<Object, ?> fut = new GridCompoundFuture<Object, Object>();
+    public IgniteInternalFuture<?> rebuildIndexesFromHash(GridCacheContext cctx) {
+        // Indexing module is disabled, nothing to rebuild.
+        if (idx == null)
+            return null;
 
-            for (Map.Entry<QueryTypeIdKey, QueryTypeDescriptorImpl> e : types.entrySet()) {
-                String cacheName = e.getKey().cacheName();
+        // No data on non-affinity nodes.
+        if (!cctx.affinityNode())
+            return null;
 
-                if (cacheIds.contains(CU.cacheId(cacheName)) && processedCacheNames.add(cacheName))
-                    fut.add(rebuildIndexesFromHash(cacheName, e.getValue()));
-            }
+        // No indexes to rebuild when there are no QueryEntities.
+        if (!cctx.isQueryEnabled())
+            return null;
 
-            fut.markInitialized();
+        if (!busyLock.enterBusy())
+            return new GridFinishedFuture<>(new NodeStoppingException("Failed to rebuild indexes from hash " +
+                "(grid is stopping)."));
 
-            return fut;
+        try {
+            return idx.rebuildIndexesFromHash(cctx);
         }
         finally {
             busyLock.leaveBusy();
@@ -1781,51 +1776,6 @@ public class GridQueryProcessor extends GridProcessorAdapter {
 
     /**
      * @param cacheName Cache name.
-     * @param desc Type descriptor.
-     * @return Future that will be completed when rebuilding of all indexes is finished.
-     */
-    private IgniteInternalFuture<Object> rebuildIndexesFromHash(@Nullable final String cacheName,
-        @Nullable final QueryTypeDescriptorImpl desc) {
-        if (idx == null)
-            return new GridFinishedFuture<>(new IgniteCheckedException("Indexing is disabled."));
-
-        if (desc == null)
-            return new GridFinishedFuture<>();
-
-        final GridWorkerFuture<Object> fut = new GridWorkerFuture<>();
-
-        idx.markForRebuildFromHash(cacheName);
-
-        GridWorker w = new GridWorker(ctx.igniteInstanceName(), "index-rebuild-worker", log) {
-            @Override protected void body() {
-                try {
-                    idx.rebuildIndexesFromHash(cacheName);
-
-                    fut.onDone();
-                }
-                catch (Exception e) {
-                    fut.onDone(e);
-                }
-                catch (Throwable e) {
-                    U.error(log, "Failed to rebuild indexes for type [cache=" + cacheName +
-                        ", name=" + desc.name() + ']', e);
-
-                    fut.onDone(e);
-
-                    throw e;
-                }
-            }
-        };
-
-        fut.setWorker(w);
-
-        ctx.getExecutorService().execute(w);
-
-        return fut;
-    }
-
-    /**
-     * @param cacheName Cache name.
      * @return Cache object context.
      */
     private CacheObjectContext cacheObjectContext(String cacheName) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
index 5432257..d889cb5 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteClientCacheInitializationFailTest.java
@@ -39,6 +39,7 @@ import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.configuration.NearCacheConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.IgniteKernal;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccQueryTracker;
@@ -336,13 +337,8 @@ public class IgniteClientCacheInitializationFailTest extends GridCommonAbstractT
         }
 
         /** {@inheritDoc} */
-        @Override public void rebuildIndexesFromHash(String cacheName) throws IgniteCheckedException {
-            // No-op
-        }
-
-        /** {@inheritDoc} */
-        @Override public void markForRebuildFromHash(String cacheName) {
-            // No-op
+        @Override public IgniteInternalFuture<?> rebuildIndexesFromHash(GridCacheContext cctx) {
+            return null;
         }
 
         /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 937363a..6582516 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -58,6 +58,7 @@ import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.GridTopic;
 import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheObjectValueContext;
 import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
@@ -74,7 +75,6 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
 import org.apache.ignite.internal.processors.query.h2.affinity.PartitionInfo;
-import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
 import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
 import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
 import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
@@ -113,7 +113,6 @@ import org.apache.ignite.internal.processors.query.h2.dml.DmlUtils;
 import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlan;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryContext;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
 import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
 import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAlias;
@@ -164,6 +163,8 @@ import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.LT;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.util.worker.GridWorker;
+import org.apache.ignite.internal.util.worker.GridWorkerFuture;
 import org.apache.ignite.lang.IgniteBiClosure;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteFuture;
@@ -642,15 +643,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
             // Populate index with existing cache data.
             final GridH2RowDescriptor rowDesc = h2Tbl.rowDescriptor();
 
-            SchemaIndexCacheVisitorClosure clo = new SchemaIndexCacheVisitorClosure() {
-                @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
-                    GridH2Row h2Row = rowDesc.createRow(row);
-
-                    h2Idx.putx(h2Row);
-                }
-            };
-
-            cacheVisitor.visit(clo);
+            cacheVisitor.visit(new IndexBuildClosure(rowDesc, h2Idx));
 
             // At this point index is in consistent state, promote it through H2 SQL statement, so that cached
             // prepared statements are re-built.
@@ -2694,33 +2687,93 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         return rowCache.forGroup(grpId);
     }
 
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> rebuildIndexesFromHash(GridCacheContext cctx) {
+        // No data in fresh in-memory cache.
+        if (!cctx.group().persistenceEnabled())
+            return null;
+
+        IgnitePageStoreManager pageStore = cctx.shared().pageStore();
+
+        assert pageStore != null;
+
+        IndexRebuildClosure clo;
+
+        if (!pageStore.hasIndexStore(cctx.groupId())) {
+            // If there are no index store, rebuild all indexes.
+            clo = new IndexRebuildFullClosure(cctx.queries(), cctx.mvccEnabled());
+        }
+        else {
+            // Otherwise iterate over tables looking for missing indexes.
+            clo = new IndexRebuildPartialClosure();
+
+            for (H2TableDescriptor tblDesc : tables(cctx.name())) {
+                assert tblDesc.table() != null;
+
+                tblDesc.table().collectIndexesForPartialRebuild((IndexRebuildPartialClosure)clo);
+            }
+        }
+
+        // Closure prepared, do rebuild.
+        final GridWorkerFuture<?> fut = new GridWorkerFuture<>();
+
+        markIndexRebuild(cctx.name(), clo, true);
+
+        GridWorker worker = new GridWorker(ctx.igniteInstanceName(), "index-rebuild-worker-" + cctx.name(), log) {
+            @Override protected void body() {
+                try {
+                    rebuildIndexesFromHash0(cctx, clo);
+
+                    markIndexRebuild(cctx.name(), clo, false);
+
+                    fut.onDone();
+                }
+                catch (Exception e) {
+                    fut.onDone(e);
+                }
+                catch (Throwable e) {
+                    U.error(log, "Failed to rebuild indexes for cache: " + cctx.name(), e);
+
+                    fut.onDone(e);
+
+                    throw e;
+                }
+            }
+        };
+
+        fut.setWorker(worker);
+
+        ctx.getExecutorService().execute(worker);
+
+        return fut;
+    }
+
     /**
-     * Rebuild indexes from hash index.
+     * Do index rebuild.
      *
-     * @param cacheName Cache name.
+     * @param cctx Cache context.
+     * @param clo Closure.
      * @throws IgniteCheckedException If failed.
      */
-    @Override public void rebuildIndexesFromHash(String cacheName) throws IgniteCheckedException {
-        int cacheId = CU.cacheId(cacheName);
-
-        GridCacheContext cctx = ctx.cache().context().cacheContext(cacheId);
-
-        final GridCacheQueryManager qryMgr = cctx.queries();
-
+    protected void rebuildIndexesFromHash0(GridCacheContext cctx, SchemaIndexCacheVisitorClosure clo)
+        throws IgniteCheckedException {
         SchemaIndexCacheVisitor visitor = new SchemaIndexCacheVisitorImpl(cctx);
 
-        visitor.visit(new RebuildIndexFromHashClosure(qryMgr, cctx.mvccEnabled()));
-
-        for (H2TableDescriptor tblDesc : tables(cacheName))
-            tblDesc.table().markRebuildFromHashInProgress(false);
+        visitor.visit(clo);
     }
 
-    /** {@inheritDoc} */
-    @Override public void markForRebuildFromHash(String cacheName) {
+    /**
+     * Mark tables for index rebuild, so that their indexes are not used.
+     *
+     * @param cacheName Cache name.
+     * @param val Value.
+     */
+    private void markIndexRebuild(String cacheName, IndexRebuildClosure clo, boolean val) {
         for (H2TableDescriptor tblDesc : tables(cacheName)) {
             assert tblDesc.table() != null;
 
-            tblDesc.table().markRebuildFromHashInProgress(true);
+            // TODO: Mark only some indexes.
+            tblDesc.table().markRebuildFromHashInProgress(val);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java
new file mode 100644
index 0000000..95a5adf
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexBuildClosure.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
+
+/**
+ * Index build closure.
+ */
+public class IndexBuildClosure implements SchemaIndexCacheVisitorClosure {
+    /** Row descriptor. */
+    private final GridH2RowDescriptor rowDesc;
+
+    /** Index. */
+    private final GridH2IndexBase idx;
+
+    /**
+     * Constructor.
+     *
+     * @param rowDesc Row descriptor.
+     * @param idx Target index.
+     */
+    public IndexBuildClosure(GridH2RowDescriptor rowDesc, GridH2IndexBase idx) {
+        this.rowDesc = rowDesc;
+        this.idx = idx;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
+        GridH2Row h2Row = rowDesc.createRow(row);
+
+        idx.putx(h2Row);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildClosure.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildClosure.java
new file mode 100644
index 0000000..75943dd
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildClosure.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2;
+
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
+
+/**
+ * Index rebuild closure. Exposes information about indexes being rebuilt.
+ */
+public interface IndexRebuildClosure extends SchemaIndexCacheVisitorClosure {
+    /**
+     * Check if the given index is being rebuild by this closure.
+     *
+     * @param tbl Table.
+     * @param idx Index.
+     * @return {@code True} if index is being rebuild by this closure, {@code false} otherwise.
+     */
+    boolean isRebuilt(GridH2Table tbl, GridH2IndexBase idx);
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildFullClosure.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildFullClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildFullClosure.java
new file mode 100644
index 0000000..e070382
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildFullClosure.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+
+/**
+ * Closure to rebuild all indexes.
+ */
+public class IndexRebuildFullClosure implements IndexRebuildClosure {
+    /** */
+    private final GridCacheQueryManager qryMgr;
+
+    /** MVCC status flag. */
+    private final boolean mvccEnabled;
+
+    /**
+     * @param qryMgr Query manager.
+     * @param mvccEnabled MVCC status flag.
+     */
+    public IndexRebuildFullClosure(GridCacheQueryManager qryMgr, boolean mvccEnabled) {
+        this.qryMgr = qryMgr;
+        this.mvccEnabled = mvccEnabled;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
+        // prevRowAvailable is always true with MVCC on, and always false *on index rebuild* with MVCC off.
+        qryMgr.store(row, null, mvccEnabled);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isRebuilt(GridH2Table tbl, GridH2IndexBase idx) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java
new file mode 100644
index 0000000..e40b364
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * Closure to rebuild some cache indexes.
+ */
+public class IndexRebuildPartialClosure implements IndexRebuildClosure {
+    /** Indexes. */
+    private final Map<GridH2Table, Collection<GridH2IndexBase>> tblIdxs = new IdentityHashMap<>();
+
+    /** {@inheritDoc} */
+    @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
+        assert hasIndexes();
+
+        for (Map.Entry<GridH2Table, Collection<GridH2IndexBase>> tblIdxEntry : tblIdxs.entrySet()) {
+            GridH2Table tbl = tblIdxEntry.getKey();
+
+            GridH2Row row0 = tbl.rowDescriptor().createRow(row);
+
+            for (GridH2IndexBase idx : tblIdxEntry.getValue())
+                idx.putx(row0);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isRebuilt(GridH2Table tbl, GridH2IndexBase idx) {
+        Collection<GridH2IndexBase> idxs = tblIdxs.get(tbl);
+
+        return idxs != null && idxs.contains(idx);
+    }
+
+    /**
+     * @param idx Index to be rebuilt.
+     */
+    public void addIndex(GridH2Table tbl, GridH2IndexBase idx) {
+        Collection<GridH2IndexBase> idxs = tblIdxs.get(tbl);
+
+        if (idxs == null) {
+            idxs = Collections.newSetFromMap(new IdentityHashMap<>());
+
+            idxs.add(idx);
+
+            tblIdxs.put(tbl, idxs);
+        }
+
+        idxs.add(idx);
+    }
+
+    /**
+     * @return {@code True} if there is at least one index to rebuild.
+     */
+    public boolean hasIndexes() {
+        return !tblIdxs.isEmpty();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java
deleted file mode 100644
index b635eac..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/RebuildIndexFromHashClosure.java
+++ /dev/null
@@ -1,47 +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.processors.query.h2;
-
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
-import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
-import org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
-
-/** */
-class RebuildIndexFromHashClosure implements SchemaIndexCacheVisitorClosure {
-    /** */
-    private final GridCacheQueryManager qryMgr;
-
-    /** MVCC status flag. */
-    private final boolean mvccEnabled;
-
-    /**
-     * @param qryMgr Query manager.
-     * @param mvccEnabled MVCC status flag.
-     */
-    RebuildIndexFromHashClosure(GridCacheQueryManager qryMgr, boolean mvccEnabled) {
-        this.qryMgr = qryMgr;
-        this.mvccEnabled = mvccEnabled;
-    }
-
-    /** {@inheritDoc} */
-    @Override public void apply(CacheDataRow row) throws IgniteCheckedException {
-        // prevRowAvailable is always true with MVCC on, and always false *on index rebuild* with MVCC off.
-        qryMgr.store(row, null, mvccEnabled);
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index b0d9e23..48d4cc4 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -88,6 +88,9 @@ public class H2TreeIndex extends GridH2IndexBase {
     /** */
     private final String idxName;
 
+    /** Tree name. */
+    private final String treeName;
+
     /** */
     private final IgniteLogger log;
 
@@ -130,14 +133,11 @@ public class H2TreeIndex extends GridH2IndexBase {
 
         this.table = tbl;
 
-
         GridQueryTypeDescriptor typeDesc = tbl.rowDescriptor().type();
 
         int typeId = cctx.binaryMarshaller() ? typeDesc.typeId() : typeDesc.valueClass().hashCode();
 
-        String treeName = (tbl.rowDescriptor() == null ? "" : typeId + "_") + idxName;
-
-        treeName = BPlusTree.treeName(treeName, "H2Tree");
+        treeName = BPlusTree.treeName((tbl.rowDescriptor() == null ? "" : typeId + "_") + idxName, "H2Tree");
 
         IndexColumnsInfo unwrappedColsInfo = new IndexColumnsInfo(unwrappedColsList, inlineSize);
 
@@ -156,7 +156,7 @@ public class H2TreeIndex extends GridH2IndexBase {
                 db.checkpointReadLock();
 
                 try {
-                    RootPage page = getMetaPage(treeName, i);
+                    RootPage page = getMetaPage(i);
 
                     segments[i] = new H2Tree(
                         treeName,
@@ -215,6 +215,29 @@ public class H2TreeIndex extends GridH2IndexBase {
     }
 
     /**
+     * Check if index exists in store.
+     *
+     * @return {@code True} if exists.
+     */
+    public boolean rebuildRequired() {
+        assert segments != null;
+
+        for (int i = 0; i < segments.length; i++) {
+            try {
+
+                if (!metaPageExists(i))
+                    return true;
+            }
+            catch (Exception e) {
+                throw new IgniteException("Failed to check index tree root page existence [cacheName=" + cctx.name() +
+                    ", tblName=" + tblName + ", idxName=" + idxName + ", segment=" + i + ']');
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * @param cols Columns array.
      * @return List of {@link InlineIndexHelper} objects.
      */
@@ -431,7 +454,7 @@ public class H2TreeIndex extends GridH2IndexBase {
 
                     tree.destroy();
 
-                    dropMetaPage(tree.getName(), i);
+                    dropMetaPage(i);
                 }
             }
         }
@@ -526,22 +549,31 @@ public class H2TreeIndex extends GridH2IndexBase {
     }
 
     /**
-     * @param name Name.
      * @param segIdx Segment index.
      * @return RootPage for meta page.
      * @throws IgniteCheckedException If failed.
      */
-    private RootPage getMetaPage(String name, int segIdx) throws IgniteCheckedException {
-        return cctx.offheap().rootPageForIndex(cctx.cacheId(), name, segIdx);
+    private RootPage getMetaPage(int segIdx) throws IgniteCheckedException {
+        return cctx.offheap().rootPageForIndex(cctx.cacheId(), treeName, segIdx);
+    }
+
+    /**
+     * @param segIdx Segment index.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void dropMetaPage(int segIdx) throws IgniteCheckedException {
+        cctx.offheap().dropRootPageForIndex(cctx.cacheId(), treeName, segIdx);
     }
 
     /**
-     * @param name Name.
+     * Check if index meta page exists.
+     *
      * @param segIdx Segment index.
+     * @return {@code True} if exists.
      * @throws IgniteCheckedException If failed.
      */
-    private void dropMetaPage(String name, int segIdx) throws IgniteCheckedException {
-        cctx.offheap().dropRootPageForIndex(cctx.cacheId(), name, segIdx);
+    private boolean metaPageExists(int segIdx) throws IgniteCheckedException {
+        return cctx.offheap().rootPageForIndexExists(cctx.cacheId(), treeName, segIdx);
     }
 
     /** {@inheritDoc} */
@@ -575,7 +607,7 @@ public class H2TreeIndex extends GridH2IndexBase {
          * @param cfgInlineSize Inline size from cache config.
          */
         public IndexColumnsInfo(List<IndexColumn> colsList, int cfgInlineSize) {
-            this.cols = colsList.toArray(new IndexColumn[colsList.size()]);
+            this.cols = colsList.toArray(new IndexColumn[0]);
 
             this.inlineIdx = getAvailableInlineColumns(cols);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
index b2da670..eafb813 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
@@ -35,6 +35,7 @@ import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.query.QueryTable;
 import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.processors.query.QueryField;
+import org.apache.ignite.internal.processors.query.h2.IndexRebuildPartialClosure;
 import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
 import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
 import org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor;
@@ -174,11 +175,14 @@ public class GridH2Table extends TableBase {
         assert idxs != null;
 
         List<Index> clones = new ArrayList<>(idxs.size());
+
         for (Index index : idxs) {
             Index clone = createDuplicateIndexIfNeeded(index);
+
             if (clone != null)
                 clones.add(clone);
         }
+
         idxs.addAll(clones);
 
         boolean hasHashIndex = idxs.size() >= 2 && index(0).getIndexType().isHash();
@@ -550,6 +554,24 @@ public class GridH2Table extends TableBase {
     }
 
     /**
+     * Collect indexes for rebuild.
+     *
+     * @param clo Closure.
+     */
+    public void collectIndexesForPartialRebuild(IndexRebuildPartialClosure clo) {
+        for (int i = sysIdxsCnt; i < idxs.size(); i++) {
+            Index idx = idxs.get(i);
+
+            if (idx instanceof H2TreeIndex) {
+                H2TreeIndex idx0 = (H2TreeIndex)idx;
+
+                if (idx0.rebuildRequired())
+                    clo.addIndex(this, idx0);
+            }
+        }
+    }
+
+    /**
      *
      */
     public void markRebuildFromHashInProgress(boolean value) {
@@ -948,9 +970,10 @@ public class GridH2Table extends TableBase {
     }
 
     /**
+     * Drop columns.
      *
-     * @param cols
-     * @param ifExists
+     * @param cols Columns.
+     * @param ifExists If EXISTS flag.
      */
     public void dropColumns(List<String> cols, boolean ifExists) {
         assert !ifExists || cols.size() == 1;

http://git-wip-us.apache.org/repos/asf/ignite/blob/898e82a2/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java
index c5f1441..fe841ae 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexRebuildSelfTest.java
@@ -24,12 +24,14 @@ import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManager;
 import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
 import org.apache.ignite.internal.processors.cache.index.DynamicIndexAbstractSelfTest;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 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.schema.SchemaIndexCacheVisitorClosure;
 import org.apache.ignite.internal.util.lang.GridCursor;
 import org.apache.ignite.internal.util.typedef.internal.U;
 
@@ -228,13 +230,14 @@ public class GridIndexRebuildSelfTest extends DynamicIndexAbstractSelfTest {
         private boolean firstRbld = true;
 
         /** {@inheritDoc} */
-        @Override public void rebuildIndexesFromHash(String cacheName) throws IgniteCheckedException {
+        @Override protected void rebuildIndexesFromHash0(GridCacheContext cctx, SchemaIndexCacheVisitorClosure clo)
+            throws IgniteCheckedException {
             if (!firstRbld)
                 U.await(INSTANCE.rebuildLatch);
             else
                 firstRbld = false;
 
-            super.rebuildIndexesFromHash(cacheName);
+            super.rebuildIndexesFromHash0(cctx, clo);
         }
     }
 }