IGNITE-10291: SQL: fixed index re-build routine. Now indexes which appeared 
during node shutdown are rebuilt as well. This closes #5525.


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

Branch: refs/heads/ignite-10044
Commit: 9b0ce92d9f60004ec07cff137d7b0582c644a500
Parents: d19123d
Author: devozerov <voze...@gridgain.com>
Authored: Mon Dec 3 18:08:53 2018 +0300
Committer: devozerov <voze...@gridgain.com>
Committed: Mon Dec 3 18:08:53 2018 +0300

----------------------------------------------------------------------
 .../cache/IgniteCacheOffheapManager.java        |   5 +-
 .../cache/IgniteCacheOffheapManagerImpl.java    |   4 +-
 .../GridCacheDatabaseSharedManager.java         |  14 +-
 .../persistence/GridCacheOffheapManager.java    |  15 +-
 .../cache/persistence/IndexStorage.java         |  26 ++-
 .../cache/persistence/IndexStorageImpl.java     |  37 +++-
 .../UpgradePendingTreeToPerPartitionTask.java   |   4 +-
 .../processors/query/GridQueryIndexing.java     |  16 +-
 .../processors/query/GridQueryProcessor.java    |  92 +++-------
 ...IgniteClientCacheInitializationFailTest.java |  10 +-
 .../database/IndexStorageSelfTest.java          |  21 ++-
 .../processors/query/h2/IgniteH2Indexing.java   | 113 +++++++++---
 .../processors/query/h2/IndexBuildClosure.java  |  54 ++++++
 .../query/h2/IndexRebuildFullClosure.java       |  49 +++++
 .../query/h2/IndexRebuildPartialClosure.java    |  76 ++++++++
 .../query/h2/RebuildIndexFromHashClosure.java   |  47 -----
 .../processors/query/h2/database/H2Tree.java    |  14 ++
 .../query/h2/database/H2TreeIndex.java          |  48 +++--
 .../processors/query/h2/opt/GridH2Table.java    |  56 +++++-
 .../cache/IgniteDynamicSqlRestoreTest.java      | 180 +++++++++++++++++--
 .../query/h2/GridIndexRebuildSelfTest.java      |   9 +-
 21 files changed, 667 insertions(+), 223 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 cdc44a5..ed3697a 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
@@ -588,17 +588,18 @@ public interface IgniteCacheOffheapManager {
     /**
      * @param cacheId Cache ID.
      * @param idxName Index name.
+     * @param segment Segment.
      * @return Root page for index tree.
      * @throws IgniteCheckedException If failed.
      */
-    public RootPage rootPageForIndex(int cacheId, String idxName) throws 
IgniteCheckedException;
+    public RootPage rootPageForIndex(int cacheId, String idxName, int segment) 
throws IgniteCheckedException;
 
     /**
      * @param cacheId Cache ID.
      * @param idxName Index name.
      * @throws IgniteCheckedException If failed.
      */
-    public void dropRootPageForIndex(int cacheId, String idxName) throws 
IgniteCheckedException;
+    public void dropRootPageForIndex(int cacheId, String idxName, int segment) 
throws IgniteCheckedException;
 
     /**
      * @param idxName Index name.

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 e15009e..ca14f68 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
@@ -1142,14 +1142,14 @@ public class IgniteCacheOffheapManagerImpl implements 
IgniteCacheOffheapManager
     }
 
     /** {@inheritDoc} */
-    @Override public RootPage rootPageForIndex(int cacheId, String idxName) 
throws IgniteCheckedException {
+    @Override public RootPage rootPageForIndex(int cacheId, String idxName, 
int segment) throws IgniteCheckedException {
         long pageId = allocateForTree();
 
         return new RootPage(new FullPageId(pageId, grp.groupId()), true);
     }
 
     /** {@inheritDoc} */
-    @Override public void dropRootPageForIndex(int cacheId, String idxName) 
throws IgniteCheckedException {
+    @Override public void dropRootPageForIndex(int cacheId, String idxName, 
int segment) throws IgniteCheckedException {
         // No-op.
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 d610a51..9a03337 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;
@@ -1342,16 +1343,19 @@ 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);
 
-                    if (!cctx.pageStore().hasIndexStore(cacheCtx.groupId()) && 
cacheCtx.affinityNode()
-                        && cacheCtx.group().persistenceEnabled()) {
-                        IgniteInternalFuture<?> rebuildFut = 
cctx.kernalContext().query()
-                            
.rebuildIndexesFromHash(Collections.singleton(cacheCtx.cacheId()));
+                    IgniteInternalFuture<?> rebuildFut = 
qryProc.rebuildIndexesFromHash(cacheCtx);
+
+                    if (rebuildFut != null) {
+                        log().info("Started indexes rebuilding for cache 
[name=" + cacheCtx.name()
+                            + ", grpName=" + cacheCtx.group().name() + ']');
 
                         assert usrFut != null : "Missing user future for 
cache: " + cacheCtx.name();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 44ae6f1..e1bd8dd 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
@@ -142,6 +142,7 @@ public class GridCacheOffheapManager extends 
IgniteCacheOffheapManagerImpl imple
             ctx.wal(),
             globalRemoveId(),
             grp.groupId(),
+            grp.sharedGroup(),
             PageIdAllocator.INDEX_PARTITION,
             PageIdAllocator.FLAG_IDX,
             reuseList,
@@ -835,19 +836,13 @@ public class GridCacheOffheapManager extends 
IgniteCacheOffheapManagerImpl imple
     }
 
     /** {@inheritDoc} */
-    @Override public RootPage rootPageForIndex(int cacheId, String idxName) 
throws IgniteCheckedException {
-        if (grp.sharedGroup())
-            idxName = Integer.toString(cacheId) + "_" + idxName;
-
-        return indexStorage.getOrAllocateForTree(idxName);
+    @Override public RootPage rootPageForIndex(int cacheId, String idxName, 
int segment) throws IgniteCheckedException {
+        return indexStorage.allocateCacheIndex(cacheId, idxName, segment);
     }
 
     /** {@inheritDoc} */
-    @Override public void dropRootPageForIndex(int cacheId, String idxName) 
throws IgniteCheckedException {
-        if (grp.sharedGroup())
-            idxName = Integer.toString(cacheId) + "_" + idxName;
-
-        indexStorage.dropRootPage(idxName);
+    @Override public void dropRootPageForIndex(int cacheId, String idxName, 
int segment) throws IgniteCheckedException {
+        indexStorage.dropCacheIndex(cacheId, idxName, segment);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 5141b04..295ff00 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
@@ -24,6 +24,17 @@ import org.apache.ignite.IgniteCheckedException;
  */
 public interface IndexStorage {
     /**
+     * Allocate page for cache index. Index name will be masked if needed.
+     *
+     * @param cacheId Cache ID.
+     * @param idxName Index name.
+     * @param segment Segment.
+     * @return Root page.
+     * @throws IgniteCheckedException If failed.
+     */
+    public RootPage allocateCacheIndex(Integer cacheId, String idxName, int 
segment) throws IgniteCheckedException;
+
+    /**
      * Get or allocate initial page for an index.
      *
      * @param idxName Index name.
@@ -31,7 +42,18 @@ public interface IndexStorage {
      *      was newly allocated, and rootId that is counter which increments 
each time new page allocated.
      * @throws IgniteCheckedException If failed.
      */
-    public RootPage getOrAllocateForTree(String idxName) throws 
IgniteCheckedException;
+    public RootPage allocateIndex(String idxName) throws 
IgniteCheckedException;
+
+    /**
+     * Deallocate index page and remove from tree.
+     *
+     * @param cacheId Cache ID.
+     * @param idxName Index name.
+     * @param segment Segment.
+     * @return Root ID or -1 if no page was removed.
+     * @throws IgniteCheckedException  If failed.
+     */
+    public RootPage dropCacheIndex(Integer cacheId, String idxName, int 
segment) throws IgniteCheckedException;
 
     /**
      * Deallocate index page and remove from tree.
@@ -40,7 +62,7 @@ public interface IndexStorage {
      * @return Root ID or -1 if no page was removed.
      * @throws IgniteCheckedException  If failed.
      */
-    public RootPage dropRootPage(String idxName) throws IgniteCheckedException;
+    public RootPage dropIndex(String idxName) throws IgniteCheckedException;
 
     /**
      * Destroy this meta store.

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 6248765..b29553c 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
@@ -61,6 +61,9 @@ public class IndexStorageImpl implements IndexStorage {
     /** Cache group ID. */
     private final int grpId;
 
+    /** Whether group is shared. */
+    private final boolean grpShared;
+
     /** */
     private final int allocPartId;
 
@@ -76,6 +79,7 @@ public class IndexStorageImpl implements IndexStorage {
         final IgniteWriteAheadLogManager wal,
         final AtomicLong globalRmvId,
         final int grpId,
+        boolean grpShared,
         final int allocPartId,
         final byte allocSpace,
         final ReuseList reuseList,
@@ -86,6 +90,7 @@ public class IndexStorageImpl implements IndexStorage {
         try {
             this.pageMem = pageMem;
             this.grpId = grpId;
+            this.grpShared = grpShared;
             this.allocPartId = allocPartId;
             this.allocSpace = allocSpace;
             this.reuseList = reuseList;
@@ -99,7 +104,15 @@ public class IndexStorageImpl implements IndexStorage {
     }
 
     /** {@inheritDoc} */
-    @Override public RootPage getOrAllocateForTree(final String idxName) 
throws IgniteCheckedException {
+    @Override public RootPage allocateCacheIndex(Integer cacheId, String 
idxName, int segment)
+        throws IgniteCheckedException {
+        String maskedIdxName = maskCacheIndexName(cacheId, idxName, segment);
+
+        return allocateIndex(maskedIdxName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public RootPage allocateIndex(String idxName) throws 
IgniteCheckedException {
         final MetaTree tree = metaTree;
 
         synchronized (this) {
@@ -132,8 +145,15 @@ public class IndexStorageImpl implements IndexStorage {
     }
 
     /** {@inheritDoc} */
-    @Override public RootPage dropRootPage(final String idxName)
+    @Override public RootPage dropCacheIndex(Integer cacheId, String idxName, 
int segment)
         throws IgniteCheckedException {
+        String maskedIdxName = maskCacheIndexName(cacheId, idxName, segment);
+
+        return dropIndex(maskedIdxName);
+    }
+
+    /** {@inheritDoc} */
+    @Override public RootPage dropIndex(final String idxName) throws 
IgniteCheckedException {
         byte[] idxNameBytes = idxName.getBytes(StandardCharsets.UTF_8);
 
         final IndexItem row = metaTree.remove(new IndexItem(idxNameBytes, 0));
@@ -152,6 +172,19 @@ public class IndexStorageImpl implements IndexStorage {
     }
 
     /**
+     * Mask cache index name.
+     *
+     * @param idxName Index name.
+     * @return Masked name.
+     */
+    private String maskCacheIndexName(Integer cacheId, String idxName, int 
segment) {
+        if (grpShared)
+            idxName = Integer.toString(cacheId) + "_" + idxName;
+
+        return idxName + "%" + segment;
+    }
+
+    /**
      *
      */
     private static class MetaTree extends BPlusTree<IndexItem, IndexItem> {

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/migration/UpgradePendingTreeToPerPartitionTask.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/migration/UpgradePendingTreeToPerPartitionTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/migration/UpgradePendingTreeToPerPartitionTask.java
index 6fa039d..1cba69a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/migration/UpgradePendingTreeToPerPartitionTask.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/migration/UpgradePendingTreeToPerPartitionTask.java
@@ -131,14 +131,14 @@ public class UpgradePendingTreeToPerPartitionTask 
implements IgniteCallable<Bool
             IndexStorage indexStorage = 
((GridCacheOffheapManager)grp.offheap()).getIndexStorage();
 
             //TODO: IGNITE-5874: replace with some check-method to avoid 
unnecessary page allocation.
-            RootPage pendingRootPage = 
indexStorage.getOrAllocateForTree(PENDING_ENTRIES_TREE_NAME);
+            RootPage pendingRootPage = 
indexStorage.allocateIndex(PENDING_ENTRIES_TREE_NAME);
 
             if (pendingRootPage.isAllocated()) {
                 log.info("No pending tree found for cache group: [grpId=" + 
grp.groupId() +
                     ", grpName=" + grp.name() + ']');
 
                 // Nothing to do here as just allocated tree is obviously 
empty.
-                indexStorage.dropRootPage(PENDING_ENTRIES_TREE_NAME);
+                indexStorage.dropIndex(PENDING_ENTRIES_TREE_NAME);
 
                 return;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 c8c9502..2abafab 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.GridCacheContextInfo;
@@ -276,19 +277,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/9b0ce92d/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 db52f40..da5233a 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
@@ -64,6 +64,7 @@ import 
org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManager;
 import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.QueryCursorImpl;
@@ -93,7 +94,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 +106,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;
@@ -1776,78 +1774,46 @@ 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>();
-
-            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()));
-            }
-
-            fut.markInitialized();
-
-            return fut;
-        }
-        finally {
-            busyLock.leaveBusy();
-        }
-    }
-
-    /**
-     * @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) {
+    public IgniteInternalFuture<?> rebuildIndexesFromHash(GridCacheContext 
cctx) {
+        // Indexing module is disabled, nothing to rebuild.
         if (idx == null)
-            return new GridFinishedFuture<>(new 
IgniteCheckedException("Indexing is disabled."));
-
-        if (desc == null)
-            return new GridFinishedFuture<>();
-
-        final GridWorkerFuture<Object> fut = new GridWorkerFuture<>();
+            return null;
 
-        idx.markForRebuildFromHash(cacheName);
+        // No data on non-affinity nodes.
+        if (!cctx.affinityNode())
+            return null;
 
-        GridWorker w = new GridWorker(ctx.igniteInstanceName(), 
"index-rebuild-worker", log) {
-            @Override protected void body() {
-                try {
-                    idx.rebuildIndexesFromHash(cacheName);
+        // No indexes to rebuild when there are no QueryEntities.
+        if (!cctx.isQueryEnabled())
+            return null;
 
-                    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);
+        // No need to rebuild if cache has no data.
+        boolean empty = true;
 
-                    fut.onDone(e);
+        for (IgniteCacheOffheapManager.CacheDataStore store : 
cctx.offheap().cacheDataStores()) {
+            if (!store.isEmpty()) {
+                empty = false;
 
-                    throw e;
-                }
+                break;
             }
-        };
+        }
 
-        fut.setWorker(w);
+        if (empty)
+            return null;
 
-        ctx.getExecutorService().execute(w);
+        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();
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 ce7f4d7..11f5138 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/9b0ce92d/modules/core/src/test/java/org/apache/ignite/internal/processors/database/IndexStorageSelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/IndexStorageSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/IndexStorageSelfTest.java
index bbdcd38..de2161a 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/IndexStorageSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/IndexStorageSelfTest.java
@@ -96,15 +96,24 @@ public class IndexStorageSelfTest extends 
GridCommonAbstractTest {
                 IndexStorageImpl metaStore = storeMap.get(cacheId);
 
                 if (metaStore == null) {
-                    metaStore = new IndexStorageImpl(mem, null, new 
AtomicLong(), cacheId,
-                        PageIdAllocator.INDEX_PARTITION, PageMemory.FLAG_IDX,
-                        null, mem.allocatePage(cacheId, 
PageIdAllocator.INDEX_PARTITION, PageMemory.FLAG_IDX), true,
-                            null);
+                    metaStore = new IndexStorageImpl(
+                        mem,
+                        null,
+                        new AtomicLong(),
+                        cacheId,
+                        false,
+                        PageIdAllocator.INDEX_PARTITION,
+                        PageMemory.FLAG_IDX,
+                        null,
+                        mem.allocatePage(cacheId, 
PageIdAllocator.INDEX_PARTITION, PageMemory.FLAG_IDX),
+                        true,
+                        null
+                    );
 
                     storeMap.put(cacheId, metaStore);
                 }
 
-                final RootPage rootPage = 
metaStore.getOrAllocateForTree(idxName);
+                final RootPage rootPage = metaStore.allocateIndex(idxName);
 
                 assertTrue(rootPage.isAllocated());
 
@@ -118,7 +127,7 @@ public class IndexStorageSelfTest extends 
GridCommonAbstractTest {
                     String idxName = entry.getKey();
                     FullPageId rootPageId = entry.getValue().pageId();
 
-                    final RootPage rootPage = 
storeMap.get(cacheId).getOrAllocateForTree(idxName);
+                    final RootPage rootPage = 
storeMap.get(cacheId).allocateIndex(idxName);
 
                     assertEquals("Invalid root page ID restored [cacheId=" + 
cacheId + ", idxName=" + idxName + ']',
                         rootPageId, rootPage.pageId());

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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 e726857..e42fc6a 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,7 @@ import 
org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
 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.cache.query.GridCacheQueryManager;
+import org.apache.ignite.internal.processors.query.h2.affinity.PartitionInfo;
 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;
@@ -100,7 +101,6 @@ import 
org.apache.ignite.internal.processors.query.QueryIndexDescriptorImpl;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.query.SqlClientContext;
 import org.apache.ignite.internal.processors.query.UpdateSourceIterator;
-import org.apache.ignite.internal.processors.query.h2.affinity.PartitionInfo;
 import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
 import 
org.apache.ignite.internal.processors.query.h2.database.H2TreeClientIndex;
 import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
@@ -115,7 +115,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;
@@ -166,6 +165,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;
@@ -644,15 +645,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.
@@ -2692,33 +2685,97 @@ 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;
+
+        SchemaIndexCacheVisitorClosure 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.
+            IndexRebuildPartialClosure clo0 = new IndexRebuildPartialClosure();
+
+            for (H2TableDescriptor tblDesc : tables(cctx.name())) {
+                assert tblDesc.table() != null;
+
+                tblDesc.table().collectIndexesForPartialRebuild(clo0);
+            }
+
+            if (clo0.hasIndexes())
+                clo = clo0;
+            else
+                return null;
+        }
+
+        // Closure prepared, do rebuild.
+        final GridWorkerFuture<?> fut = new GridWorkerFuture<>();
+
+        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(), 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, boolean val) {
         for (H2TableDescriptor tblDesc : tables(cacheName)) {
             assert tblDesc.table() != null;
 
-            tblDesc.table().markRebuildFromHashInProgress(true);
+            tblDesc.table().markRebuildFromHashInProgress(val);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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..8d1923f
--- /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 row0 = rowDesc.createRow(row);
+
+        idx.putx(row0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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..8018839
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildFullClosure.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * Closure to rebuild all indexes.
+ */
+public class IndexRebuildFullClosure implements SchemaIndexCacheVisitorClosure 
{
+    /** */
+    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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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..2672f06
--- /dev/null
+++ 
b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IndexRebuildPartialClosure.java
@@ -0,0 +1,76 @@
+/*
+ * 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 
org.apache.ignite.internal.processors.query.schema.SchemaIndexCacheVisitorClosure;
+
+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 
SchemaIndexCacheVisitorClosure {
+    /** 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);
+        }
+    }
+
+    /**
+     * @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/9b0ce92d/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/9b0ce92d/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/9b0ce92d/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 2c0a9d8..65be8d6 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
@@ -83,6 +83,9 @@ public class H2TreeIndex extends H2TreeIndexBase {
     /** */
     private final String idxName;
 
+    /** Tree name. */
+    private final String treeName;
+
     /** */
     private final IgniteLogger log;
 
@@ -125,14 +128,11 @@ public class H2TreeIndex extends H2TreeIndexBase {
 
         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);
 
@@ -152,7 +152,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
             db.checkpointReadLock();
 
             try {
-                RootPage page = getMetaPage(treeName, i);
+                RootPage page = getMetaPage(i);
 
                 segments[i] = new H2Tree(
                     treeName,
@@ -203,6 +203,30 @@ public class H2TreeIndex extends H2TreeIndexBase {
     }
 
     /**
+     * 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 {
+                H2Tree segment = segments[i];
+
+                if (segment.created())
+                    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.
      */
@@ -398,7 +422,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
 
                     tree.destroy();
 
-                    dropMetaPage(tree.getName(), i);
+                    dropMetaPage(i);
                 }
             }
         }
@@ -493,22 +517,20 @@ public class H2TreeIndex extends H2TreeIndexBase {
     }
 
     /**
-     * @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 name Name.
      * @param segIdx Segment index.
      * @throws IgniteCheckedException If failed.
      */
-    private void dropMetaPage(String name, int segIdx) throws 
IgniteCheckedException {
-        cctx.offheap().dropRootPageForIndex(cctx.cacheId(), name + "%" + 
segIdx);
+    private void dropMetaPage(int segIdx) throws IgniteCheckedException {
+        cctx.offheap().dropRootPageForIndex(cctx.cacheId(), treeName, segIdx);
     }
 
     /** {@inheritDoc} */
@@ -542,7 +564,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
          * @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/9b0ce92d/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 ef31f20..59597be 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
@@ -26,7 +26,6 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.LongAdder;
 import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteInterruptedException;
@@ -36,7 +35,9 @@ 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.database.H2TreeIndexBase;
 import 
org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor;
 import org.apache.ignite.internal.util.typedef.F;
@@ -89,7 +90,7 @@ public class GridH2Table extends TableBase {
     private final Map<String, GridH2IndexBase> tmpIdxs = new HashMap<>();
 
     /** */
-    private final ReadWriteLock lock;
+    private final ReentrantReadWriteLock lock;
 
     /** */
     private boolean destroyed;
@@ -127,6 +128,7 @@ public class GridH2Table extends TableBase {
      * @param idxsFactory Indexes factory.
      * @param cacheInfo Cache context info.
      */
+    @SuppressWarnings("ConstantConditions")
     public GridH2Table(CreateTableData createTblData, GridH2RowDescriptor 
desc, H2RowFactory rowFactory,
         GridH2SystemIndexFactory idxsFactory, GridCacheContextInfo cacheInfo) {
         super(createTblData);
@@ -175,11 +177,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();
@@ -565,12 +570,39 @@ 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);
+            }
+        }
+    }
+
+    /**
+     * Mark or unmark index rebuild state.
      */
     public void markRebuildFromHashInProgress(boolean value) {
         assert !value || (idxs.size() >= 2 && 
index(1).getIndexType().isHash()) : "Table has no hash index.";
 
         rebuildFromHashInProgress = value;
+
+        lock.writeLock().lock();
+
+        try {
+            incrementModificationCounter();
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
     }
 
     /**
@@ -651,7 +683,7 @@ public class GridH2Table extends TableBase {
             if (cloneIdx != null)
                 database.addSchemaObject(ses, cloneIdx);
 
-            setModified();
+            incrementModificationCounter();
 
             return idx;
         }
@@ -955,7 +987,7 @@ public class GridH2Table extends TableBase {
 
             desc.refreshMetadataFromTypeDescriptor();
 
-            setModified();
+            incrementModificationCounter();
         }
         finally {
             unlock(true);
@@ -963,9 +995,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;
@@ -1017,7 +1050,7 @@ public class GridH2Table extends TableBase {
                     ((GridH2IndexBase)idx).refreshColumnIds();
             }
 
-            setModified();
+            incrementModificationCounter();
         }
         finally {
             unlock(true);
@@ -1046,6 +1079,15 @@ public class GridH2Table extends TableBase {
     }
 
     /**
+     * Increment modification counter to force recompilation of existing 
prepared statements.
+     */
+    private void incrementModificationCounter() {
+        assert lock.isWriteLockedByCurrentThread();
+
+        setModified();
+    }
+
+    /**
      * Set insert hack flag.
      *
      * @param val Value.

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
index f7dc7b4..d009f33 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteDynamicSqlRestoreTest.java
@@ -18,12 +18,20 @@
 package org.apache.ignite.internal.processors.cache;
 
 import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.sql.Timestamp;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.Ignition;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cache.QueryEntity;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
@@ -33,8 +41,10 @@ import 
org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.configuration.WALMode;
 import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.util.typedef.PA;
 import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.spi.IgniteSpiException;
+import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.jetbrains.annotations.NotNull;
 
@@ -44,6 +54,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 /**
  *
  */
+@SuppressWarnings("Duplicates")
 public class IgniteDynamicSqlRestoreTest extends GridCommonAbstractTest 
implements Serializable {
 
     public static final String TEST_CACHE_NAME = "test";
@@ -117,7 +128,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
             //and: change data
             try (IgniteDataStreamer<Object, Object> s = 
ig.dataStreamer(TEST_CACHE_NAME)) {
                 s.allowOverwrite(true);
-                for (int i = 0; i < 5_000; i++)
+                for (int i = 0; i < 50; i++)
                     s.addData(i, null);
             }
 
@@ -134,7 +145,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
             //then: everything is ok
             try (IgniteDataStreamer<Object, Object> s = 
ig1.dataStreamer(TEST_CACHE_NAME)) {
                 s.allowOverwrite(true);
-                for (int i = 0; i < 50_000; i++) {
+                for (int i = 0; i < 50; i++) {
                     BinaryObject bo = ig1.binary().builder(TEST_INDEX_OBJECT)
                         .setField("a", i, Object.class)
                         .setField("b", String.valueOf(i), Object.class)
@@ -147,12 +158,132 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
 
             IgniteCache<Object, Object> cache = ig1.cache(TEST_CACHE_NAME);
 
-            assertThat(doExplainPlan(cache, "explain select * from 
TestIndexObject where a > 5"), containsString("myindexa"));
+            assertIndexUsed(cache, "explain select * from TestIndexObject 
where a > 5", "myindexa");
             assertFalse(cache.query(new SqlFieldsQuery("SELECT a,b,c FROM 
TestIndexObject limit 1")).getAll().isEmpty());
         }
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    @SuppressWarnings("AssertWithSideEffects")
+    public void testIndexCreationWhenNodeStopped() throws Exception {
+        // Start topology.
+        startGrid(0);
+        Ignite srv2 = startGrid(1);
+        Ignite cli;
+
+        Ignition.setClientMode(true);
+
+        try {
+            cli = startGrid(2);
+        }
+        finally {
+            Ignition.setClientMode(false);
+        }
+
+        cli.cluster().active(true);
+
+        // Create table, add some data.
+        int entryCnt = 50;
+
+        try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:10802")) {
+            executeJdbc(conn,
+                " 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\"");
+
+            try (PreparedStatement stmt = conn.prepareStatement(
+                "insert into Person(LANG, FIRST_NAME, ADDRESS, LAST_NAME, 
BIRTH_DATE) values(?,?,?,?,?)")) {
+                for (int i = 0; i < entryCnt; i++) {
+                    String s = String.valueOf(i);
+
+                    stmt.setString(1, s);
+                    stmt.setString(2, s);
+                    stmt.setString(3, s);
+                    stmt.setString(4, s);
+                    stmt.setTimestamp(5, new 
Timestamp(System.currentTimeMillis()));
+
+                    stmt.executeUpdate();
+                }
+            }
+        }
+
+        // Stop second node.
+        srv2.close();
+
+        // Create an index on remaining node.
+        try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:10802")) {
+            executeJdbc(conn, "create index PERSON_FIRST_NAME_IDX on 
PERSON(FIRST_NAME)");
+        }
+
+        // Restart second node.
+        startGrid(1);
+
+        // Await for index rebuild on started node.
+        assert GridTestUtils.waitForCondition(new PA() {
+            @Override public boolean apply() {
+                try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:10801")) {
+                    try (PreparedStatement stmt = conn.prepareStatement(
+                        "EXPLAIN SELECT * FROM Person USE 
INDEX(PERSON_FIRST_NAME_IDX) WHERE FIRST_NAME=?")) {
+                        stmt.setString(1, String.valueOf(1));
+
+                        StringBuilder fullPlan = new StringBuilder();
+
+                        try (ResultSet rs = stmt.executeQuery()) {
+                            while (rs.next())
+                                fullPlan.append(rs.getString(1)).append("; ");
+                        }
+
+                        System.out.println("PLAN: " + fullPlan);
+
+                        return 
fullPlan.toString().contains("PUBLIC.PERSON_FIRST_NAME_IDX");
+                    }
+                }
+                catch (Exception e) {
+                    throw new RuntimeException("Query failed.", e);
+                }
+            }
+        }, 5_000);
+
+        // Make sure that data could be queried.
+        try (Connection conn = 
DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1:10802")) {
+            try (PreparedStatement stmt = conn.prepareStatement(
+                "SELECT COUNT(*) FROM Person USE INDEX(PERSON_FIRST_NAME_IDX) 
WHERE FIRST_NAME=?")) {
+                for (int i = 0; i < entryCnt; i ++) {
+                    stmt.setString(1, String.valueOf(i));
+
+                    try (ResultSet rs = stmt.executeQuery()) {
+                        rs.next();
+
+                        long cnt = rs.getLong(1);
+
+                        assertEquals(1L, cnt);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Execute a statement through JDBC connection.
+     *
+     * @param conn Connection.
+     * @param sql Statement.
+     * @throws Exception If failed.
+     */
+    private static void executeJdbc(Connection conn, String sql) throws 
Exception {
+        try (Statement stmt = conn.createStatement()) {
+            stmt.execute(sql);
+        }
+    }
+
+    /**
      * @throws Exception if failed.
      */
     public void testTakeConfigFromJoiningNodeOnInactiveGrid() throws Exception 
{
@@ -186,7 +317,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
             //then: config for cache was applying successful
             IgniteCache<Object, Object> cache = ig.cache(TEST_CACHE_NAME);
 
-            assertThat(doExplainPlan(cache, "explain select * from 
TestIndexObject where a > 5"), containsString("myindexa"));
+            assertIndexUsed(cache, "explain select * from TestIndexObject 
where a > 5", "myindexa");
             assertFalse(cache.query(new SqlFieldsQuery("SELECT a,b,c FROM 
TestIndexObject limit 1")).getAll().isEmpty());
         }
     }
@@ -233,7 +364,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
 
             IgniteCache<Object, Object> cache = ig.cache(TEST_CACHE_NAME);
 
-            assertThat(doExplainPlan(cache, "explain select * from 
TestIndexObject where a > 5"), containsString("myindexa"));
+            assertIndexUsed(cache, "explain select * from TestIndexObject 
where a > 5", "myindexa");
             assertFalse(cache.query(new SqlFieldsQuery("SELECT a,b,c FROM 
TestIndexObject limit 1")).getAll().isEmpty());
         }
     }
@@ -241,6 +372,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
     /**
      * @throws Exception if failed.
      */
+    @SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
     public void testMergeChangedConfigOnInactiveGrid() throws Exception {
         {
             //given: two started nodes with test table
@@ -288,7 +420,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
             //then: config should be merged
             try (IgniteDataStreamer<Object, Object> s = 
ig1.dataStreamer(TEST_CACHE_NAME)) {
                 s.allowOverwrite(true);
-                for (int i = 0; i < 5_000; i++) {
+                for (int i = 0; i < 50; i++) {
                     BinaryObject bo = ig1.binary().builder("TestIndexObject")
                         .setField("a", i, Object.class)
                         .setField("b", String.valueOf(i), Object.class)
@@ -300,13 +432,32 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
             IgniteCache<Object, Object> cache = ig1.cache(TEST_CACHE_NAME);
 
             //then: index "myindexa" and column "b" restored from node "1"
-            assertThat(doExplainPlan(cache, "explain select * from 
TestIndexObject where a > 5"), containsString("myindexa"));
-            assertThat(doExplainPlan(cache, "explain select * from 
TestIndexObject where b > 5"), containsString("myindexb"));
+            assertIndexUsed(cache, "explain select * from TestIndexObject 
where a > 5", "myindexa");
+            assertIndexUsed(cache, "explain select * from TestIndexObject 
where b > 5", "myindexb");
             assertFalse(cache.query(new SqlFieldsQuery("SELECT a,b FROM 
TestIndexObject limit 1")).getAll().isEmpty());
         }
     }
 
     /**
+     * Make sure that index is used for the given statement.
+     *
+     * @param cache Cache.
+     * @param sql Statement.
+     * @param idx Index.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void assertIndexUsed(IgniteCache<Object, Object> cache, String 
sql, String idx)
+        throws IgniteCheckedException {
+        assert GridTestUtils.waitForCondition(new PA() {
+            @Override public boolean apply() {
+                String plan = doExplainPlan(cache, sql);
+
+                return plan.contains(idx);
+            }
+        }, 10_000);
+    }
+
+    /**
      * @throws Exception if failed.
      */
     public void testTakeChangedConfigOnActiveGrid() throws Exception {
@@ -341,7 +492,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
             //then: config should be merged
             try (IgniteDataStreamer<Object, Object> s = 
ig.dataStreamer(TEST_CACHE_NAME)) {
                 s.allowOverwrite(true);
-                for (int i = 0; i < 5_000; i++) {
+                for (int i = 0; i < 50; i++) {
                     BinaryObject bo = ig.binary().builder("TestIndexObject")
                         .setField("a", i, Object.class)
                         .setField("b", String.valueOf(i), Object.class)
@@ -355,7 +506,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
 
             cache.indexReadyFuture().get();
 
-            assertThat(doExplainPlan(cache, "explain select * from 
TestIndexObject where a > 5"), containsString("myindexa"));
+            assertIndexUsed(cache, "explain select * from TestIndexObject 
where a > 5", "myindexa");
             assertFalse(cache.query(new SqlFieldsQuery("SELECT a,b,c FROM 
TestIndexObject limit 1")).getAll().isEmpty());
         }
     }
@@ -363,6 +514,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
     /**
      * @throws Exception if failed.
      */
+    @SuppressWarnings("ConstantConditions")
     public void testFailJoiningNodeBecauseDifferentSql() throws Exception {
         {
             //given: two started nodes with test table
@@ -407,6 +559,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
     /**
      * @throws Exception if failed.
      */
+    @SuppressWarnings("ConstantConditions")
     public void testFailJoiningNodeBecauseFieldInlineSizeIsDifferent() throws 
Exception {
         {
             //given: two started nodes with test table
@@ -417,13 +570,13 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
 
             IgniteCache cache = 
ig.getOrCreateCache(getTestTableConfiguration());
 
-            cache.query(new SqlFieldsQuery("create index myindexa on 
TestIndexObject(a) INLINE_SIZE 1000")).getAll();
+            cache.query(new SqlFieldsQuery("create index myindexa on 
TestIndexObject(a) INLINE_SIZE 100")).getAll();
 
             //stop one node and create index on other node
             stopGrid(1);
 
             cache.query(new SqlFieldsQuery("drop index myindexa")).getAll();
-            cache.query(new SqlFieldsQuery("create index myindexa on 
TestIndexObject(a) INLINE_SIZE 2000")).getAll();
+            cache.query(new SqlFieldsQuery("create index myindexa on 
TestIndexObject(a) INLINE_SIZE 200")).getAll();
 
             //and: stopped all grid
             stopAllGrids();
@@ -447,6 +600,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
     /**
      * @throws Exception if failed.
      */
+    @SuppressWarnings("ConstantConditions")
     public void testFailJoiningNodeBecauseNeedConfigUpdateOnActiveGrid() 
throws Exception {
         {
             startGrid(0);
@@ -497,7 +651,7 @@ public class IgniteDynamicSqlRestoreTest extends 
GridCommonAbstractTest implemen
      */
     private void fillTestData(Ignite ig) {
         try (IgniteDataStreamer<Object, Object> s = 
ig.dataStreamer(TEST_CACHE_NAME)) {
-            for (int i = 0; i < 50_000; i++) {
+            for (int i = 0; i < 500; i++) {
                 BinaryObject bo = ig.binary().builder("TestIndexObject")
                     .setField("a", i, Object.class)
                     .setField("b", String.valueOf(i), Object.class)

http://git-wip-us.apache.org/repos/asf/ignite/blob/9b0ce92d/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..d8ba060 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;
 
@@ -38,7 +40,7 @@ import org.apache.ignite.internal.util.typedef.internal.U;
  */
 public class GridIndexRebuildSelfTest extends DynamicIndexAbstractSelfTest {
     /** Data size. */
-    protected static final int AMOUNT = 300;
+    protected static final int AMOUNT = 50;
 
     /** Data size. */
     protected static final String CACHE_NAME = "T";
@@ -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);
         }
     }
 }

Reply via email to