diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php
index 08e75ceebecc6160825bc12b0380a03b4a1af0ab..337134c9453c01dad20e4581e4d82019aa956b6d 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext.php
@@ -6,11 +6,16 @@
 namespace Magento\CatalogSearch\Model\Indexer;
 
 use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\FullFactory;
+use Magento\CatalogSearch\Model\Indexer\Scope\State;
 use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource;
-use \Magento\Framework\Search\Request\Config as SearchRequestConfig;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Search\Request\Config as SearchRequestConfig;
 use Magento\Framework\Search\Request\DimensionFactory;
 use Magento\Store\Model\StoreManagerInterface;
 
+/**
+ * Provide functionality for Fulltext Search indexing
+ */
 class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface
 {
     /**
@@ -18,34 +23,51 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
      */
     const INDEXER_ID = 'catalogsearch_fulltext';
 
-    /** @var array index structure */
+    /**
+     * @var array index structure
+     */
     protected $data;
 
     /**
      * @var IndexerHandlerFactory
      */
     private $indexerHandlerFactory;
+
     /**
      * @var StoreManagerInterface
      */
     private $storeManager;
+
     /**
-     * @var DimensionFactory
+     * @var \Magento\Framework\Search\Request\DimensionFactory
      */
     private $dimensionFactory;
+
     /**
-     * @var Full
+     * @var \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\Full
      */
     private $fullAction;
+
     /**
      * @var FulltextResource
      */
     private $fulltextResource;
+
     /**
-     * @var SearchRequestConfig
+     * @var \Magento\Framework\Search\Request\Config
      */
     private $searchRequestConfig;
 
+    /**
+     * @var IndexSwitcherInterface
+     */
+    private $indexSwitcher;
+
+    /**
+     * @var \Magento\CatalogSearch\Model\Indexer\Scope\State
+     */
+    private $indexScopeState;
+
     /**
      * @param FullFactory $fullActionFactory
      * @param IndexerHandlerFactory $indexerHandlerFactory
@@ -54,6 +76,8 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
      * @param FulltextResource $fulltextResource
      * @param SearchRequestConfig $searchRequestConfig
      * @param array $data
+     * @param IndexSwitcherInterface $indexSwitcher
+     * @param Scope\State $indexScopeState
      */
     public function __construct(
         FullFactory $fullActionFactory,
@@ -62,7 +86,9 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
         DimensionFactory $dimensionFactory,
         FulltextResource $fulltextResource,
         SearchRequestConfig $searchRequestConfig,
-        array $data
+        array $data,
+        IndexSwitcherInterface $indexSwitcher = null,
+        State $indexScopeState = null
     ) {
         $this->fullAction = $fullActionFactory->create(['data' => $data]);
         $this->indexerHandlerFactory = $indexerHandlerFactory;
@@ -71,6 +97,14 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
         $this->fulltextResource = $fulltextResource;
         $this->searchRequestConfig = $searchRequestConfig;
         $this->data = $data;
+        if (null === $indexSwitcher) {
+            $indexSwitcher = ObjectManager::getInstance()->get(IndexSwitcherInterface::class);
+        }
+        if (null === $indexScopeState) {
+            $indexScopeState = ObjectManager::getInstance()->get(State::class);
+        }
+        $this->indexSwitcher = $indexSwitcher;
+        $this->indexScopeState = $indexScopeState;
     }
 
     /**
@@ -106,10 +140,14 @@ class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\F
             'data' => $this->data
         ]);
         foreach ($storeIds as $storeId) {
-            $dimension = $this->dimensionFactory->create(['name' => 'scope', 'value' => $storeId]);
-            $saveHandler->cleanIndex([$dimension]);
-            $saveHandler->saveIndex([$dimension], $this->fullAction->rebuildStoreIndex($storeId));
+            $dimensions = [$this->dimensionFactory->create(['name' => 'scope', 'value' => $storeId])];
+            $this->indexScopeState->useTemporaryIndex();
+
+            $saveHandler->cleanIndex($dimensions);
+            $saveHandler->saveIndex($dimensions, $this->fullAction->rebuildStoreIndex($storeId));
 
+            $this->indexSwitcher->switchIndex($dimensions);
+            $this->indexScopeState->useRegularIndex();
         }
         $this->fulltextResource->resetSearchResults();
         $this->searchRequestConfig->reset();
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php
index 9a2bb76c04ce76ccfa60e757c2075120ef2d09e2..d2d76bc71ccbf4223036f59e8eebc65fcbd94c4c 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructure.php
@@ -12,6 +12,7 @@ use Magento\Framework\DB\Ddl\Table;
 use Magento\Framework\Search\Request\Dimension;
 use Magento\Framework\Indexer\IndexStructureInterface;
 use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
+use Magento\Framework\Search\Request\IndexScopeResolverInterface;
 
 class IndexStructure implements IndexStructureInterface
 {
@@ -19,6 +20,7 @@ class IndexStructure implements IndexStructureInterface
      * @var Resource
      */
     private $resource;
+
     /**
      * @var IndexScopeResolver
      */
@@ -26,11 +28,11 @@ class IndexStructure implements IndexStructureInterface
 
     /**
      * @param ResourceConnection $resource
-     * @param IndexScopeResolver $indexScopeResolver
+     * @param IndexScopeResolverInterface $indexScopeResolver
      */
     public function __construct(
         ResourceConnection $resource,
-        IndexScopeResolver $indexScopeResolver
+        IndexScopeResolverInterface $indexScopeResolver
     ) {
         $this->resource = $resource;
         $this->indexScopeResolver = $indexScopeResolver;
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherInterface.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..ba5fb461dde35aa0a6cdfebc1520b8d1091d841b
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherInterface.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Model\Indexer;
+
+/**
+ * Provides a functionality to replace main index with its temporary representation
+ */
+interface IndexSwitcherInterface
+{
+    /**
+     * Switch current index with temporary index
+     *
+     * It will drop current index table and rename temporary index table to the current index table.
+     *
+     * @param array $dimensions
+     * @return void
+     */
+    public function switchIndex(array $dimensions);
+}
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherProxy.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherProxy.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ce093ed99e3a48aebadc2a02c788fd9de018591
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexSwitcherProxy.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogSearch\Model\Indexer;
+
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Store\Model\ScopeInterface;
+
+/**
+ * Proxy for adapter-specific index switcher
+ */
+class IndexSwitcherProxy implements IndexSwitcherInterface
+{
+    /**
+     * Object Manager instance
+     *
+     * @var ObjectManagerInterface
+     */
+    private $objectManager = null;
+
+    /**
+     * Instance name to create
+     *
+     * @var string
+     */
+    private $handlers;
+
+    /**
+     * @var ScopeConfigInterface
+     */
+    private $scopeConfig;
+
+    /**
+     * Configuration path by which current indexer handler stored
+     *
+     * @var string
+     */
+    private $configPath;
+
+    /**
+     * Factory constructor
+     *
+     * @param ObjectManagerInterface $objectManager
+     * @param ScopeConfigInterface $scopeConfig
+     * @param string $configPath
+     * @param string[] $handlers
+     */
+    public function __construct(
+        ObjectManagerInterface $objectManager,
+        ScopeConfigInterface $scopeConfig,
+        $configPath,
+        array $handlers = []
+    ) {
+        $this->objectManager = $objectManager;
+        $this->scopeConfig = $scopeConfig;
+        $this->configPath = $configPath;
+        $this->handlers = $handlers;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * As index switcher is an optional part of the search SPI, it may be not defined by a search engine.
+     * It is especially reasonable for search engines with pre-defined indexes declaration (like old SOLR and Sphinx)
+     * which cannot create temporary indexes on the fly.
+     * That's the reason why this method do nothing for the case
+     * when switcher is not defined for a specific search engine.
+     */
+    public function switchIndex(array $dimensions)
+    {
+        $currentHandler = $this->scopeConfig->getValue($this->configPath, ScopeInterface::SCOPE_STORE);
+        if (!isset($this->handlers[$currentHandler])) {
+            return;
+        }
+        $this->create($currentHandler)->switchIndex($dimensions);
+    }
+
+    /**
+     * Create indexer handler
+     *
+     * @param string $handler
+     * @return IndexSwitcherInterface
+     */
+    private function create($handler)
+    {
+        $indexSwitcher = $this->objectManager->create($this->handlers[$handler]);
+
+        if (!$indexSwitcher instanceof IndexSwitcherInterface) {
+            throw new \InvalidArgumentException(
+                $handler . ' index switcher doesn\'t implement ' . IndexSwitcherInterface::class
+            );
+        }
+
+        return $indexSwitcher;
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php
index 8d99cd4492bf59cd4c98439cc402ec36d93744f6..2aa9a418bd7258f9046ebdf889f7b69f8a0c8d31 100644
--- a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php
@@ -7,13 +7,11 @@ namespace Magento\CatalogSearch\Model\Indexer;
 
 use Magento\Eav\Model\Config;
 use Magento\Framework\App\ResourceConnection;
-use Magento\Framework\DB\Adapter\AdapterInterface;
 use Magento\Framework\Indexer\SaveHandler\IndexerInterface;
 use Magento\Framework\Indexer\IndexStructureInterface;
 use Magento\Framework\Search\Request\Dimension;
 use Magento\Framework\Search\Request\IndexScopeResolverInterface;
 use Magento\Framework\Indexer\SaveHandler\Batch;
-use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
 
 class IndexerHandler implements IndexerInterface
 {
@@ -62,7 +60,7 @@ class IndexerHandler implements IndexerInterface
      * @param ResourceConnection $resource
      * @param Config $eavConfig
      * @param Batch $batch
-     * @param \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver $indexScopeResolver
+     * @param IndexScopeResolverInterface $indexScopeResolver
      * @param array $data
      * @param int $batchSize
      */
@@ -71,7 +69,7 @@ class IndexerHandler implements IndexerInterface
         ResourceConnection $resource,
         Config $eavConfig,
         Batch $batch,
-        IndexScopeResolver $indexScopeResolver,
+        IndexScopeResolverInterface $indexScopeResolver,
         array $data,
         $batchSize = 100
     ) {
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexSwitcher.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexSwitcher.php
new file mode 100644
index 0000000000000000000000000000000000000000..87a7b7110d3aae24999040e62d75bdd01052528e
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexSwitcher.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Model\Indexer\Scope;
+
+use Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface;
+use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Search\Request\IndexScopeResolverInterface;
+
+/**
+ * Provides a functionality to replace main index with its temporary representation
+ */
+class IndexSwitcher implements IndexSwitcherInterface
+{
+    /**
+     * @var Resource
+     */
+    private $resource;
+
+    /**
+     * @var ScopeProxy
+     */
+    private $resolver;
+
+    /**
+     * @var State
+     */
+    private $state;
+
+    /**
+     * @param ResourceConnection $resource
+     * @param IndexScopeResolverInterface $indexScopeResolver
+     * @param State $state
+     */
+    public function __construct(
+        ResourceConnection $resource,
+        IndexScopeResolverInterface $indexScopeResolver,
+        State $state
+    ) {
+        $this->resource = $resource;
+        $this->resolver = $indexScopeResolver;
+        $this->state = $state;
+    }
+
+    /**
+     * {@inheritdoc}
+     * @throws IndexTableNotExistException
+     */
+    public function switchIndex(array $dimensions)
+    {
+        if (State::USE_TEMPORARY_INDEX === $this->state->getState()) {
+            $index = \Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID;
+
+            $temporalIndexTable = $this->resolver->resolve($index, $dimensions);
+            if (!$this->resource->getConnection()->isTableExists($temporalIndexTable)) {
+                throw new IndexTableNotExistException(
+                    __(
+                        "Temporary table for index $index doesn't exist,"
+                        . " which is inconsistent with state of scope resolver"
+                    )
+                );
+            }
+
+            $this->state->useRegularIndex();
+            $tableName = $this->resolver->resolve($index, $dimensions);
+            if ($this->resource->getConnection()->isTableExists($tableName)) {
+                $this->resource->getConnection()->dropTable($tableName);
+            }
+
+            $this->resource->getConnection()->renameTable($temporalIndexTable, $tableName);
+            $this->state->useTemporaryIndex();
+        }
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexTableNotExistException.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexTableNotExistException.php
new file mode 100644
index 0000000000000000000000000000000000000000..6974f8c278ab55af0cb41f501124eb3201f5e914
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/IndexTableNotExistException.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogSearch\Model\Indexer\Scope;
+
+
+use Magento\Framework\Exception\LocalizedException;
+
+/**
+ * Exception which represents situation where temporary index table should be used somewhere,
+ * but it does not exist in a database
+ */
+class IndexTableNotExistException extends LocalizedException
+{
+}
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/ScopeProxy.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/ScopeProxy.php
new file mode 100644
index 0000000000000000000000000000000000000000..14832af303bf4167df380776f28de09482c84e7f
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/ScopeProxy.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogSearch\Model\Indexer\Scope;
+
+
+use Magento\Framework\Search\Request\Dimension;
+
+/**
+ * Implementation of IndexScopeResolverInterface which resolves index scope dynamically
+ * depending on current scope state
+ */
+class ScopeProxy implements \Magento\Framework\Search\Request\IndexScopeResolverInterface
+{
+    /**
+     * Object Manager instance
+     *
+     * @var \Magento\Framework\ObjectManagerInterface
+     */
+    private $objectManager;
+
+    /**
+     * @var array
+     */
+    private $states = [];
+
+    /**
+     * @var State
+     */
+    private $scopeState;
+
+    /**
+     * Factory constructor
+     *
+     * @param \Magento\Framework\ObjectManagerInterface $objectManager
+     * @param State $scopeState
+     * @param array $states
+     */
+    public function __construct(
+        \Magento\Framework\ObjectManagerInterface $objectManager,
+        State $scopeState,
+        array $states
+    ) {
+        $this->objectManager = $objectManager;
+        $this->scopeState = $scopeState;
+        $this->states = $states;
+    }
+
+    /**
+     * Creates class instance with specified parameters
+     *
+     * @param string $state
+     * @return \Magento\Framework\Search\Request\IndexScopeResolverInterface
+     * @throws UnknownStateException
+     */
+    private function create($state)
+    {
+        if (!array_key_exists($state, $this->states)) {
+            throw new UnknownStateException(__("Requested resolver for unknown indexer state: $state"));
+        }
+        return $this->objectManager->create($this->states[$state]);
+    }
+
+    /**
+     * @param string $index
+     * @param Dimension[] $dimensions
+     * @return string
+     */
+    public function resolve($index, array $dimensions)
+    {
+        return $this->create($this->scopeState->getState())->resolve($index, $dimensions);
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/State.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/State.php
new file mode 100644
index 0000000000000000000000000000000000000000..2bba29ae8d842f963146bef411d3a71023740ae8
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/State.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogSearch\Model\Indexer\Scope;
+
+
+use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\App\ScopeResolverInterface;
+use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
+use Magento\Framework\Search\Request\Dimension;
+use Magento\Framework\Search\Request\IndexScopeResolverInterface;
+
+/**
+ * This class represents state that defines which table should be used during indexation process
+ *
+ * There are two possible states:
+ *   - use_temporary_table
+ *   - use_main_table
+ *
+ * The 'use_main_table' state means that default indexer table should be used.
+ *
+ * The 'use_temporary_table' state is an opposite for 'use_main_table'
+ *   which means that default indexer table should be left unchanged during indexation
+ *   and temporary table should be used instead.
+ *
+ */
+class State
+{
+    const USE_TEMPORARY_INDEX = 'use_temporary_table';
+    const USE_REGULAR_INDEX = 'use_main_table';
+
+    /**
+     * @var string
+     */
+    private $state = self::USE_REGULAR_INDEX;
+
+    /**
+     * Set the state to use temporary Index
+     * @return void
+     */
+    public function useTemporaryIndex()
+    {
+        $this->state = self::USE_TEMPORARY_INDEX;
+    }
+
+    /**
+     * Set the state to use regular Index
+     * @return void
+     */
+    public function useRegularIndex()
+    {
+        $this->state = self::USE_REGULAR_INDEX;
+    }
+
+    /**
+     * @return string
+     */
+    public function getState()
+    {
+        return $this->state;
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/TemporaryResolver.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/TemporaryResolver.php
new file mode 100644
index 0000000000000000000000000000000000000000..51037eb637cf3c346fdcd7ac64aefc0a50e14949
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/TemporaryResolver.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogSearch\Model\Indexer\Scope;
+
+
+use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
+use Magento\Framework\Search\Request\Dimension;
+
+/**
+ * Resolves name of a temporary table for indexation
+ */
+class TemporaryResolver implements \Magento\Framework\Search\Request\IndexScopeResolverInterface
+{
+    /**
+     * @var IndexScopeResolver
+     */
+    private $indexScopeResolver;
+
+    /**
+     * @inheritDoc
+     */
+    public function __construct(IndexScopeResolver $indexScopeResolver)
+    {
+        $this->indexScopeResolver = $indexScopeResolver;
+    }
+
+    /**
+     * @param string $index
+     * @param Dimension[] $dimensions
+     * @return string
+     */
+    public function resolve($index, array $dimensions)
+    {
+        $tableName = $this->indexScopeResolver->resolve($index, $dimensions);
+        $tableName .= \Magento\Framework\Indexer\Table\StrategyInterface::TMP_SUFFIX;
+
+        return $tableName;
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Scope/UnknownStateException.php b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/UnknownStateException.php
new file mode 100644
index 0000000000000000000000000000000000000000..04803ef27480efd504949ecc43773ebd3db3d2f3
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Model/Indexer/Scope/UnknownStateException.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogSearch\Model\Indexer\Scope;
+
+
+use Magento\Framework\Exception\LocalizedException;
+
+/**
+ * Exception for situation where used state which is not defined in configuration
+ */
+class UnknownStateException extends LocalizedException
+{
+
+}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php
index f4c0437590ed56c72c5e0076f79be93d181e082c..6ef656d566867dec02cf3a157e1b6d0c3cd24826 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/FulltextTest.php
@@ -8,7 +8,13 @@ namespace Magento\CatalogSearch\Test\Unit\Model\Indexer;
 use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource;
 use Magento\Framework\Search\Request\Config as SearchRequestConfig;
 use Magento\Framework\Search\Request\DimensionFactory;
+use Magento\Framework\Search\Request\Dimension;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use \Magento\CatalogSearch\Model\Indexer\Fulltext\IndexSwitcher;
 
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class FulltextTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -26,11 +32,6 @@ class FulltextTest extends \PHPUnit_Framework_TestCase
      */
     protected $storeManager;
 
-    /**
-     * @var \Magento\Framework\Search\Request\Dimension|\PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $dimension;
-
     /**
      * @var \Magento\CatalogSearch\Model\Indexer\IndexerHandler|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -47,8 +48,15 @@ class FulltextTest extends \PHPUnit_Framework_TestCase
     protected $searchRequestConfig;
 
     /**
-     *
+     * @var \Magento\Framework\Search\Request\DimensionFactory|\PHPUnit_Framework_MockObject_MockObject
      */
+    private $dimensionFactory;
+
+    /**
+     * @var \Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $indexSwitcher;
+
     protected function setUp()
     {
         $this->fullAction = $this->getClassMock(\Magento\CatalogSearch\Model\Indexer\Fulltext\Action\Full::class);
@@ -80,27 +88,29 @@ class FulltextTest extends \PHPUnit_Framework_TestCase
             []
         );
 
-        $this->dimension = $this->getClassMock(\Magento\Framework\Search\Request\Dimension::class);
-        $dimensionFactory = $this->getMock(
-            \Magento\Framework\Search\Request\DimensionFactory::class,
-            ['create'],
-            [],
-            '',
-            false
-        );
-        $dimensionFactory->expects($this->any())->method('create')->willReturn($this->dimension);
+        $this->dimensionFactory = $this->getMock(DimensionFactory::class, ['create'], [], '', false);
 
         $this->fulltextResource = $this->getClassMock(\Magento\CatalogSearch\Model\ResourceModel\Fulltext::class);
         $this->searchRequestConfig = $this->getClassMock(\Magento\Framework\Search\Request\Config::class);
 
-        $this->model = new \Magento\CatalogSearch\Model\Indexer\Fulltext(
-            $fullActionFactory,
-            $indexerHandlerFactory,
-            $this->storeManager,
-            $dimensionFactory,
-            $this->fulltextResource,
-            $this->searchRequestConfig,
-            []
+        $this->indexSwitcher = $this->getMockBuilder(\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['switchIndex'])
+            ->getMock();
+
+        $objectManagerHelper = new ObjectManagerHelper($this);
+        $this->model = $objectManagerHelper->getObject(
+            \Magento\CatalogSearch\Model\Indexer\Fulltext::class,
+            [
+                'fullActionFactory' => $fullActionFactory,
+                'indexerHandlerFactory' => $indexerHandlerFactory,
+                'storeManager' => $this->storeManager,
+                'dimensionFactory' => $this->dimensionFactory,
+                'fulltextResource' => $this->fulltextResource,
+                'searchRequestConfig' => $this->searchRequestConfig,
+                'data' => [],
+                'indexSwitcher' => $this->indexSwitcher,
+            ]
         );
     }
 
@@ -131,13 +141,38 @@ class FulltextTest extends \PHPUnit_Framework_TestCase
     public function testExecuteFull()
     {
         $stores = [0 => 'Store 1', 1 => 'Store 2'];
-        $indexData = new \ArrayObject([]);
+        $indexData = new \ArrayObject([new \ArrayObject([]), new \ArrayObject([])]);
         $this->storeManager->expects($this->once())->method('getStores')->willReturn($stores);
-        $this->saveHandler->expects($this->exactly(count($stores)))->method('cleanIndex');
-        $this->saveHandler->expects($this->exactly(2))->method('saveIndex');
+
+        $dimensionScope1 = $this->getMock(Dimension::class, [], ['scope', '1']);
+        $dimensionScope2 = $this->getMock(Dimension::class, [], ['scope', '2']);
+
+        $this->dimensionFactory->expects($this->any())->method('create')->willReturnOnConsecutiveCalls(
+            $dimensionScope1,
+            $dimensionScope2
+        );
+        $this->indexSwitcher->expects($this->exactly(2))->method('switchIndex')
+            ->withConsecutive(
+                [$this->equalTo([$dimensionScope1])],
+                [$this->equalTo([$dimensionScope2])]
+            );
+
+        $this->saveHandler->expects($this->exactly(count($stores)))->method('cleanIndex')
+            ->withConsecutive(
+                [$this->equalTo([$dimensionScope1])],
+                [$this->equalTo([$dimensionScope2])]
+            );
+
+        $this->saveHandler->expects($this->exactly(2))->method('saveIndex')
+            ->withConsecutive(
+                [$this->equalTo([$dimensionScope1]), $this->equalTo($indexData)],
+                [$this->equalTo([$dimensionScope2]), $this->equalTo($indexData)]
+            );
         $this->fullAction->expects($this->exactly(2))
             ->method('rebuildStoreIndex')
-            ->willReturn(new \ArrayObject([$indexData, $indexData]));
+            ->withConsecutive([0], [1])
+            ->willReturn($indexData);
+
         $this->fulltextResource->expects($this->once())->method('resetSearchResults');
         $this->searchRequestConfig->expects($this->once())->method('reset');
 
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Scope/IndexSwitcherTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Scope/IndexSwitcherTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..31bec5906ae5a37c34f666af0999b1cafe2f0485
--- /dev/null
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Scope/IndexSwitcherTest.php
@@ -0,0 +1,212 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Test\Unit\Model\Indexer\Scope;
+
+use Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher;
+use Magento\CatalogSearch\Model\Indexer\Scope\State;
+use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
+use Magento\Framework\Indexer\IndexStructureInterface;
+use Magento\Framework\Search\Request\Dimension;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+
+class IndexSwitcherTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $connection;
+
+    /**
+     * @var \Magento\Framework\Search\Request\IndexScopeResolverInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $indexScopeResolver;
+
+    /**
+     * @var State|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $scopeState;
+
+    /**
+     * @var IndexSwitcher
+     */
+    private $indexSwitcher;
+
+    /**
+     * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $resource;
+
+    protected function setUp()
+    {
+        $this->resource = $this->getMockBuilder(ResourceConnection::class)
+            ->setMethods(['getConnection'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class)
+            ->setMethods(['isTableExists', 'dropTable', 'renameTable'])
+            ->getMockForAbstractClass();
+        $this->resource->expects($this->any())
+            ->method('getConnection')
+            ->willReturn($this->connection);
+        $this->indexScopeResolver = $this->getMockBuilder(
+            \Magento\Framework\Search\Request\IndexScopeResolverInterface::class
+        )
+            ->setMethods(['resolve'])
+            ->getMockForAbstractClass();
+        $this->scopeState = $this->getMockBuilder(State::class)
+            ->setMethods(['getState', 'useRegularIndex', 'useTemporaryIndex'])
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $objectManagerHelper = new ObjectManagerHelper($this);
+        $this->indexSwitcher = $objectManagerHelper->getObject(
+            \Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher::class,
+            [
+                'resource' => $this->resource,
+                'indexScopeResolver' => $this->indexScopeResolver,
+                'state' => $this->scopeState,
+            ]
+        );
+    }
+
+    public function testSwitchRegularIndex()
+    {
+        $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])];
+
+        $this->scopeState->expects($this->once())
+            ->method('getState')
+            ->willReturn(State::USE_REGULAR_INDEX);
+
+        $this->indexScopeResolver->expects($this->never())->method('resolve');
+        $this->connection->expects($this->never())->method('renameTable');
+
+        $this->indexSwitcher->switchIndex($dimensions);
+    }
+
+    public function testSwitchIndexWithUnknownState()
+    {
+        $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])];
+
+        $this->scopeState->expects($this->once())
+            ->method('getState')
+            ->willReturn('unknown_state');
+
+        $this->indexScopeResolver->expects($this->never())->method('resolve');
+        $this->connection->expects($this->never())->method('renameTable');
+
+        $this->indexSwitcher->switchIndex($dimensions);
+    }
+
+    public function testSwitchTemporaryIndexWhenRegularIndexExist()
+    {
+        $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])];
+
+        $this->scopeState->expects($this->once())
+            ->method('getState')
+            ->willReturn(State::USE_TEMPORARY_INDEX);
+
+        $this->scopeState->expects($this->at(1))->method('useRegularIndex');
+        $this->scopeState->expects($this->at(2))->method('useTemporaryIndex');
+
+        $this->indexScopeResolver->expects($this->exactly(2))->method('resolve')
+            ->withConsecutive(
+                [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)],
+                [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)]
+            )
+            ->willReturnOnConsecutiveCalls(
+                'catalogsearch_fulltext_scope1_tmp1',
+                'catalogsearch_fulltext_scope1'
+            );
+
+        $this->connection->expects($this->exactly(2))->method('isTableExists')
+            ->withConsecutive(
+                [$this->equalTo('catalogsearch_fulltext_scope1_tmp1'), $this->equalTo(null)],
+                [$this->equalTo('catalogsearch_fulltext_scope1'), $this->equalTo(null)]
+            )
+            ->willReturnOnConsecutiveCalls(
+                true,
+                true
+            );
+
+        $this->connection->expects($this->once())->method('dropTable')->with('catalogsearch_fulltext_scope1', null);
+        $this->connection->expects($this->once())
+            ->method('renameTable')
+            ->with('catalogsearch_fulltext_scope1_tmp1', 'catalogsearch_fulltext_scope1');
+
+        $this->indexSwitcher->switchIndex($dimensions);
+    }
+
+    public function testSwitchTemporaryIndexWhenRegularIndexNotExist()
+    {
+        $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])];
+
+        $this->scopeState->expects($this->once())
+            ->method('getState')
+            ->willReturn(State::USE_TEMPORARY_INDEX);
+
+        $this->scopeState->expects($this->at(1))->method('useRegularIndex');
+        $this->scopeState->expects($this->at(2))->method('useTemporaryIndex');
+
+        $this->indexScopeResolver->expects($this->exactly(2))->method('resolve')
+            ->withConsecutive(
+                [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)],
+                [$this->equalTo(FulltextIndexer::INDEXER_ID), $this->equalTo($dimensions)]
+            )
+            ->willReturnOnConsecutiveCalls(
+                'catalogsearch_fulltext_scope1_tmp1',
+                'catalogsearch_fulltext_scope1'
+            );
+
+        $this->connection->expects($this->exactly(2))->method('isTableExists')
+            ->withConsecutive(
+                [$this->equalTo('catalogsearch_fulltext_scope1_tmp1'), $this->equalTo(null)],
+                [$this->equalTo('catalogsearch_fulltext_scope1'), $this->equalTo(null)]
+            )
+            ->willReturnOnConsecutiveCalls(
+                true,
+                false
+            );
+
+        $this->connection->expects($this->never())->method('dropTable')->with('catalogsearch_fulltext_scope1', null);
+        $this->connection->expects($this->once())
+            ->method('renameTable')
+            ->with('catalogsearch_fulltext_scope1_tmp1', 'catalogsearch_fulltext_scope1');
+
+        $this->indexSwitcher->switchIndex($dimensions);
+    }
+
+    /**
+     * @expectedException \Magento\CatalogSearch\Model\Indexer\Scope\IndexTableNotExistException
+     * @expectedExceptionMessage Temporary table for index catalogsearch_fulltext doesn't exist
+     */
+    public function testSwitchWhenTemporaryIndexNotExist()
+    {
+        $dimensions = [$this->getMockBuilder(Dimension::class)->setConstructorArgs(['scope', '1'])];
+
+        $this->scopeState->expects($this->once())
+            ->method('getState')
+            ->willReturn(State::USE_TEMPORARY_INDEX);
+
+        $this->scopeState->expects($this->never())->method('useRegularIndex');
+        $this->scopeState->expects($this->never())->method('useTemporaryIndex');
+
+        $this->indexScopeResolver->expects($this->once())->method('resolve')
+            ->with(FulltextIndexer::INDEXER_ID, $dimensions)
+            ->willReturn('catalogsearch_fulltext_scope1_tmp1');
+
+        $this->connection->expects($this->once())
+            ->method('isTableExists')
+            ->with('catalogsearch_fulltext_scope1_tmp1', null)
+            ->willReturn(false);
+
+        $this->connection->expects($this->never())->method('dropTable')->with('catalogsearch_fulltext_scope1', null);
+        $this->connection->expects($this->never())->method('renameTable');
+
+        $this->indexSwitcher->switchIndex($dimensions);
+    }
+}
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php
index 4b04e7d66ab569ab3d2e6219cef4e2b69268d8e0..d44fef54683fc9e3c91c79d8844c2d620c750079 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Advanced/CollectionTest.php
@@ -74,7 +74,10 @@ class CollectionTest extends BaseCollectionTest
         $productLimitationMock = $this->getMock(
             \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class
         );
-        $productLimitationFactoryMock = $this->getMock(ProductLimitationFactory::class, ['create']);
+        $productLimitationFactoryMock = $this->getMockBuilder(ProductLimitationFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
         $productLimitationFactoryMock->method('create')
             ->willReturn($productLimitationMock);
 
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php
index 5e56a253563592a55b80b00d26432028d9f4c5fa..5732ed2c8aee1dfa2429757a9e8f67b1cc324f1f 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/ResourceModel/Fulltext/CollectionTest.php
@@ -80,7 +80,10 @@ class CollectionTest extends BaseCollectionTest
         $productLimitationMock = $this->getMock(
             \Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation::class
         );
-        $productLimitationFactoryMock = $this->getMock(ProductLimitationFactory::class, ['create']);
+        $productLimitationFactoryMock = $this->getMockBuilder(ProductLimitationFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
         $productLimitationFactoryMock->method('create')
             ->willReturn($productLimitationMock);
 
diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php
index 3bb270e990617d3a9e05ca574e788f5bb3f714aa..8f4bb7736cd37b9bf86d5591e792edea8909ef19 100644
--- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php
+++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/Indexer/IndexStructureTest.php
@@ -48,9 +48,8 @@ class IndexStructureTest extends \PHPUnit_Framework_TestCase
             ->method('getConnection')
             ->willReturn($this->connection);
         $this->indexScopeResolver = $this->getMockBuilder(
-            \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver::class
+            \Magento\Framework\Search\Request\IndexScopeResolverInterface::class
         )->setMethods(['resolve'])
-            ->disableOriginalConstructor()
             ->getMock();
 
         $objectManager = new ObjectManager($this);
@@ -64,11 +63,6 @@ class IndexStructureTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    /**
-     * @param string $table
-     * @param array $dimensions
-     * @param bool $isTableExist
-     */
     public function testDelete()
     {
         $index = 'index_name';
diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml
index 7e9451f9b83e7ada30455509b8fd686a0c251705..7116abf263b0dde081a0a7f42f585e807e47ee85 100644
--- a/app/code/Magento/CatalogSearch/etc/di.xml
+++ b/app/code/Magento/CatalogSearch/etc/di.xml
@@ -12,6 +12,7 @@
     <preference for="Magento\Framework\Search\Dynamic\DataProviderInterface" type="Magento\CatalogSearch\Model\Adapter\Mysql\Dynamic\DataProvider" />
     <preference for="Magento\Framework\Search\Adapter\OptionsInterface" type="Magento\CatalogSearch\Model\Adapter\Options" />
     <preference for="Magento\CatalogSearch\Model\Search\FilterMapper\FilterStrategyInterface" type="Magento\CatalogSearch\Model\Search\FilterMapper\FilterContext"/>
+    <preference for="\Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface" type="\Magento\CatalogSearch\Model\Indexer\IndexSwitcherProxy"/>
     <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory">
         <arguments>
             <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument>
@@ -20,6 +21,14 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\CatalogSearch\Model\Indexer\IndexSwitcherProxy">
+        <arguments>
+            <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument>
+            <argument name="handlers" xsi:type="array">
+                <item name="mysql" xsi:type="string">\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher</item>
+            </argument>
+        </arguments>
+    </type>
     <type name="Magento\CatalogSearch\Model\Indexer\IndexStructureFactory">
         <arguments>
             <argument name="configPath" xsi:type="const">Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH</argument>
@@ -246,4 +255,27 @@
             </argument>
         </arguments>
     </type>
+    <type name="\Magento\CatalogSearch\Model\Indexer\IndexerHandler">
+        <arguments>
+            <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument>
+        </arguments>
+    </type>
+    <type name="\Magento\CatalogSearch\Model\Indexer\IndexStructure">
+        <arguments>
+            <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument>
+        </arguments>
+    </type>
+    <type name="\Magento\CatalogSearch\Model\Indexer\Scope\IndexSwitcher">
+        <arguments>
+            <argument name="indexScopeResolver" xsi:type="object">\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy</argument>
+        </arguments>
+    </type>
+    <type name="\Magento\CatalogSearch\Model\Indexer\Scope\ScopeProxy">
+        <arguments>
+            <argument name="states" xsi:type="array">
+                <item name="use_temporary_table" xsi:type="string">\Magento\CatalogSearch\Model\Indexer\Scope\TemporaryResolver</item>
+                <item name="use_main_table" xsi:type="string">\Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver</item>
+            </argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
index 6f2b989e5e9bfaa9fa91643b1958be6a99c0fdc1..794034b446f72b3e1649352f74b74b49345c7a3f 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Indexer/Stock/Configurable.php
@@ -29,18 +29,25 @@ class Configurable extends \Magento\CatalogInventory\Model\ResourceModel\Indexer
         $connection = $this->getConnection();
         $idxTable = $usePrimaryTable ? $this->getMainTable() : $this->getIdxTable();
         $select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable);
+        $linkField = $metadata->getLinkField();
         $select->reset(
             \Magento\Framework\DB\Select::COLUMNS
         )->columns(
             ['e.entity_id', 'cis.website_id', 'cis.stock_id']
         )->joinLeft(
             ['l' => $this->getTable('catalog_product_super_link')],
-            'l.parent_id = e.' . $metadata->getLinkField(),
+            'l.parent_id = e.' . $linkField,
             []
         )->join(
             ['le' => $this->getTable('catalog_product_entity')],
             'le.entity_id = l.product_id',
             []
+        )->joinInner(
+            ['cpei' => $this->getTable('catalog_product_entity_int')],
+            'le.' . $linkField . ' = cpei.' . $linkField
+            . ' AND cpei.attribute_id = ' . $this->_getAttribute('status')->getId()
+            . ' AND cpei.value = ' . ProductStatus::STATUS_ENABLED,
+            []
         )->joinLeft(
             ['i' => $idxTable],
             'i.product_id = l.product_id AND cis.website_id = i.website_id AND cis.stock_id = i.stock_id',
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
index c182d9f8216c09a42868eb487dfa06b0e718b5c7..be44c110c2b6259484066ffc4c7cbe4400c68d9e 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
@@ -32,6 +32,7 @@ define([
             identificationProperty: 'id',
             'attribute_set_id': '',
             attributesTmp: [],
+            changedFlag: 'was_changed',
             listens: {
                 'insertDataFromGrid': 'processingInsertDataFromGrid',
                 'insertDataFromWizard': 'processingInsertDataFromWizard',
@@ -391,9 +392,9 @@ define([
                     'small_image': row['small_image'],
                     image: row.image,
                     'thumbnail': row.thumbnail,
-                    'attributes': attributesText,
-                    'was_changed': true
+                    'attributes': attributesText
                 };
+                product[this.changedFlag] = true;
                 product[this.canEditField] = row.editable;
                 product[this.newProductField] = row.newProduct;
                 tmpArray.push(product);
@@ -515,6 +516,7 @@ define([
                 tmpArray[rowIndex].status = 1;
             }
 
+            tmpArray[rowIndex][this.changedFlag] = true;
             this.unionInsertData(tmpArray);
         }
     });
diff --git a/app/code/Magento/Customer/Model/Indexer/Source.php b/app/code/Magento/Customer/Model/Indexer/Source.php
new file mode 100644
index 0000000000000000000000000000000000000000..60522227eb222fb058c3ddb36681a6276d635e94
--- /dev/null
+++ b/app/code/Magento/Customer/Model/Indexer/Source.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Customer\Model\Indexer;
+
+use Magento\Customer\Model\ResourceModel\Customer\Indexer\Collection;
+use Magento\Framework\App\ResourceConnection\SourceProviderInterface;
+use Traversable;
+
+/**
+ * Customers data batch generator for customer_grid indexer
+ */
+class Source implements \IteratorAggregate, \Countable, SourceProviderInterface
+{
+    /**
+     * @var Collection
+     */
+    private $customerCollection;
+
+    /**
+     * @var int
+     */
+    private $batchSize;
+
+    /**
+     * @param \Magento\Customer\Model\ResourceModel\Customer\Indexer\CollectionFactory $collection
+     * @param int $batchSize
+     */
+    public function __construct(
+        \Magento\Customer\Model\ResourceModel\Customer\Indexer\CollectionFactory $collectionFactory,
+        $batchSize = 10000
+    ) {
+        $this->customerCollection = $collectionFactory->create();
+        $this->batchSize = $batchSize;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getMainTable()
+    {
+        return $this->customerCollection->getMainTable();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getIdFieldName()
+    {
+        return $this->customerCollection->getIdFieldName();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addFieldToSelect($fieldName, $alias = null)
+    {
+        $this->customerCollection->addFieldToSelect($fieldName, $alias);
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSelect()
+    {
+        return $this->customerCollection->getSelect();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addFieldToFilter($attribute, $condition = null)
+    {
+        $this->customerCollection->addFieldToFilter($attribute, $condition);
+        return $this;
+    }
+
+    /**
+     * @return int
+     */
+    public function count()
+    {
+        return $this->customerCollection->getSize();
+    }
+
+    /**
+     * Retrieve an iterator
+     *
+     * @return Traversable
+     */
+    public function getIterator()
+    {
+        $this->customerCollection->setPageSize($this->batchSize);
+        $lastPage = $this->customerCollection->getLastPageNumber();
+        $pageNumber = 0;
+        do {
+            $this->customerCollection->clear();
+            $this->customerCollection->setCurPage($pageNumber);
+            foreach ($this->customerCollection->getItems() as $key => $value) {
+                yield $key => $value;
+            }
+            $pageNumber++;
+        } while ($pageNumber <= $lastPage);
+    }
+}
diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer/Indexer/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Customer/Indexer/Collection.php
new file mode 100644
index 0000000000000000000000000000000000000000..5b9716af5393adb9b91de1579533b6369283d795
--- /dev/null
+++ b/app/code/Magento/Customer/Model/ResourceModel/Customer/Indexer/Collection.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Customer\Model\ResourceModel\Customer\Indexer;
+
+/**
+ * Customers collection for customer_grid indexer
+ */
+class Collection extends \Magento\Customer\Model\ResourceModel\Customer\Collection
+{
+    /**
+     * @inheritdoc
+     */
+    protected function beforeAddLoadedItem(\Magento\Framework\DataObject $item)
+    {
+        return $item;
+    }
+}
diff --git a/app/code/Magento/Customer/etc/indexer.xml b/app/code/Magento/Customer/etc/indexer.xml
index b48592cafbb20b6b62ae2e4fd953ecce1e35e0af..5f644426d81696c643324365ee7e2cacd8fb64cb 100644
--- a/app/code/Magento/Customer/etc/indexer.xml
+++ b/app/code/Magento/Customer/etc/indexer.xml
@@ -11,7 +11,7 @@
         <title translate="true">Customer Grid</title>
         <description translate="true">Rebuild Customer grid index</description>
 
-        <fieldset name="customer" source="Magento\Customer\Model\ResourceModel\Customer\Collection"
+        <fieldset name="customer" source="Magento\Customer\Model\Indexer\Source"
                   provider="Magento\Customer\Model\Indexer\AttributeProvider">
             <field name="name" xsi:type="searchable" dataType="text" handler="CustomerNameHandler"/>
             <field name="email" xsi:type="searchable" dataType="varchar"/>
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php
index 0b761d8c191ab621bf75792a06e5e7e6ca98c1e1..6a09bea591cb8dca4eca1542f79971cbb92c7017 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php
@@ -391,6 +391,7 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract
      */
     public function testUpdateNegative($optionData, $message)
     {
+        $this->_markTestAsRestOnly();
         $productSku = 'simple';
         /** @var ProductRepository $productRepository */
         $productRepository = $this->objectManager->create(ProductRepository::class);
@@ -403,18 +404,9 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract
                 'resourcePath' => '/V1/products/options/' . $optionId,
                 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
             ],
-            'soap' => [
-                'service' => self::SERVICE_NAME,
-                'serviceVersion' => 'V1',
-                'operation' => self::SERVICE_NAME . 'Save',
-            ],
         ];
 
-        if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) {
-            $this->setExpectedException('SoapFault');
-        } else {
-            $this->setExpectedException('Exception', $message, 400);
-        }
+        $this->setExpectedException('Exception', $message, 400);
         $this->_webApiCall($serviceInfo, ['option' => $optionData]);
     }
 
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
index cd02c6d4d7500ef9adf7d17ac9f866bfc8ef0711..ed641ac0f85b872717b52fbadc46a4d12b7d218e 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
@@ -134,15 +134,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
         ];
     }
 
-    private function markAreaAsSecure()
-    {
-        /** @var \Magento\Framework\Registry $registry */
-        $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
-            ->get(\Magento\Framework\Registry::class);
-        $registry->unregister("isSecureArea");
-        $registry->register("isSecureArea", true);
-    }
-
     /**
      * Test removing association between product and website 1
      * @magentoApiDataFixture Magento/Catalog/_files/product_with_two_websites.php
@@ -169,9 +160,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
             $response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]["website_ids"],
             $websitesData["website_ids"]
         );
-        $this->deleteProduct($productBuilder[ProductInterface::SKU]);
-        $this->markAreaAsSecure();
-        $website->delete();
     }
 
     /**
@@ -198,9 +186,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
             $response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]["website_ids"],
             $websitesData["website_ids"]
         );
-        $this->deleteProduct($productBuilder[ProductInterface::SKU]);
-        $this->markAreaAsSecure();
-        $website->delete();
     }
 
     /**
@@ -222,7 +207,7 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
         $websitesData = [
             'website_ids' => [
                 1,
-                $website->getId(),
+                (int) $website->getId(),
             ]
         ];
         $productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = $websitesData;
@@ -231,9 +216,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
             $response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]["website_ids"],
             $websitesData["website_ids"]
         );
-        $this->deleteProduct($productBuilder[ProductInterface::SKU]);
-        $this->markAreaAsSecure();
-        $website->delete();
     }
 
     /**
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php
index bf890b2448e23f7a134574b9fb2fb2d7eca1151e..d23985bc38ef961b6b7881a02cc35b6811012cc8 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Bootstrap/DocBlock.php
@@ -40,6 +40,7 @@ class DocBlock
      *
      * Note: order of registering (and applying) annotations is important.
      * To allow config fixtures to deal with fixture stores, data fixtures should be processed first.
+     * ConfigFixture applied twice because data fixtures could clean config and clean custom settings
      *
      * @param \Magento\TestFramework\Application $application
      * @return array
@@ -68,6 +69,7 @@ class DocBlock
             new \Magento\TestFramework\Annotation\AppArea($application),
             new \Magento\TestFramework\Annotation\Cache($application),
             new \Magento\TestFramework\Annotation\AdminConfigFixture(),
+            new \Magento\TestFramework\Annotation\ConfigFixture(),
         ];
     }
 }
diff --git a/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php b/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php
index ad685845cde004bd54c8499671e9e22faa72d0e5..03fb9aa9ddae60d356158905a412500f12f8b616 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/ObjectManager.php
@@ -24,8 +24,6 @@ class ObjectManager extends \Magento\Framework\App\ObjectManager
      * @var array
      */
     protected $persistedInstances = [
-        \Magento\TestFramework\App\Config::class,
-        \Magento\Framework\App\Config\ScopeConfigInterface::class,
         \Magento\Framework\App\ResourceConnection::class,
         \Magento\Framework\Config\Scope::class,
         \Magento\Framework\ObjectManager\RelationsInterface::class,
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php
index 071c2499bb33262f1f1df9a6bcbd1f37340516ed..c2db58728d33d426a2e87736a8bbd76f212b1e91 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Helper/DataTest.php
@@ -248,6 +248,7 @@ class DataTest extends \PHPUnit_Framework_TestCase
      * @magentoDataFixture Magento/Customer/_files/customer.php
      * @magentoDataFixture Magento/Customer/_files/customer_address.php
      * @magentoDbIsolation enabled
+     * @magentoAppIsolation enabled
      * @dataProvider getTaxPriceDataProvider
      */
     public function testGetTaxPrice(
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php
index fa64de38e43114d74389f2c4ee27a3e0c0b7606d..426ee922f261426758e1ea2361ecf1d4716e9cb9 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites.php
@@ -9,25 +9,23 @@ use Magento\Catalog\Api\Data\ProductInterface;
 $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
 /** @var Magento\Store\Model\Website $website */
 $website = $objectManager->get(Magento\Store\Model\Website::class);
-$website->load('second_website', 'code');
 
-if (!$website->getId()) {
-    $website->setData(
-        [
-            'code' => 'second_website',
-            'name' => 'Test Website',
-        ]
-    );
+$website->setData(
+    [
+        'code' => 'second_website',
+        'name' => 'Test Website',
+    ]
+);
 
-    $website->save();
-}
+$website->save();
+
+$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores();
 
 /** @var $product \Magento\Catalog\Model\Product */
 $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
     ->create(ProductInterface::class);
 $product
     ->setTypeId('simple')
-    ->setId(1)
     ->setAttributeSetId(4)
     ->setWebsiteIds([1, $website->getId()])
     ->setName('Simple Product')
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites_rollback.php
new file mode 100644
index 0000000000000000000000000000000000000000..68f1106dcfe3098041c58726d72f2d7f949ddaea
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_two_websites_rollback.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+use Magento\Catalog\Api\Data\ProductInterface;
+
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+/** @var \Magento\Framework\Registry $registry */
+$registry = $objectManager
+    ->get(\Magento\Framework\Registry::class);
+$registry->unregister("isSecureArea");
+$registry->register("isSecureArea", true);
+
+/** @var Magento\Store\Model\Website $website */
+$website = $objectManager->create(\Magento\Store\Model\Website::class);
+$website->load('second_website');
+$website->delete();
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+
+try {
+    $firstProduct = $productRepository->get('unique-simple-azaza');
+    $firstProduct->delete();
+} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
+    //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php
index 6749d421e79981f0b3b917f21b975c9ad2c74e26..4dc026d6fad4d58a3533653bc0bc4cb070683313 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website.php
@@ -19,3 +19,5 @@ if (!$website->getId()) {
 
     $website->save();
 }
+
+$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website_rollback.php
new file mode 100644
index 0000000000000000000000000000000000000000..173793a1caa49bbc772916f23d6e24c2e6c87342
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_website_rollback.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry');
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+$website = $objectManager->get(Magento\Store\Model\Website::class);
+$website->load('test_website', 'code');
+
+if ($website->getId()) {
+    $website->delete();
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores();
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/IndexSwitcherMock.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/IndexSwitcherMock.php
new file mode 100644
index 0000000000000000000000000000000000000000..955ccce3bb59e5fc5b23ed57dbf917941ace1eb7
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/IndexSwitcherMock.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogSearch\Model\Indexer;
+
+
+use Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface;
+
+/**
+ * The proxy class around index switcher which allows to ensure that the IndexSwitcher was actually used
+ */
+class IndexSwitcherMock extends \PHPUnit_Framework_Assert implements IndexSwitcherInterface
+{
+    private $isSwitched = false;
+
+    /**
+     * @var \Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface
+     */
+    private $indexSwitcher;
+
+    /**
+     * @param \Magento\CatalogSearch\Model\Indexer\IndexSwitcherInterface $indexSwitcher
+     */
+    public function __construct(
+        IndexSwitcherInterface $indexSwitcher
+    ) {
+        $this->indexSwitcher = $indexSwitcher;
+    }
+
+    /**
+     * Switch current index with temporary index
+     *
+     * It will drop current index table and rename temporary index table to the current index table.
+     *
+     * @param array $dimensions
+     * @return void
+     */
+    public function switchIndex(array $dimensions)
+    {
+        $this->isSwitched |= true;
+        $this->indexSwitcher->switchIndex($dimensions);
+    }
+
+    /**
+     * @return bool
+     */
+    public function isSwitched()
+    {
+        return (bool) $this->isSwitched;
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/SwitcherUsedInFulltextTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/SwitcherUsedInFulltextTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..afcfef5fc4c8e085549c46568d47aa45041b7a6a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/SwitcherUsedInFulltextTest.php
@@ -0,0 +1,214 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogSearch\Model\Indexer;
+
+use Magento\Catalog\Model\Product;
+use Magento\CatalogSearch\Model\Indexer\Fulltext;
+use Magento\CatalogSearch\Model\Indexer\IndexSwitcherMock;
+use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection;
+use Magento\TestFramework\Helper\Bootstrap;
+
+/**
+ * @magentoDbIsolation disabled
+ * @magentoDataFixture Magento/CatalogSearch/_files/indexer_fulltext.php
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class SwitcherUsedInFulltextTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var IndexSwitcherInterface
+     */
+    private $indexSwitcher;
+
+    /**
+     * @var \Magento\Framework\Indexer\IndexerInterface
+     */
+    protected $indexer;
+
+    /**
+     * @var \Magento\CatalogSearch\Model\ResourceModel\Engine
+     */
+    protected $engine;
+
+    /**
+     * @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext
+     */
+    protected $resourceFulltext;
+
+    /**
+     * @var \Magento\CatalogSearch\Model\Fulltext
+     */
+    protected $fulltext;
+
+    /**
+     * @var \Magento\Search\Model\QueryFactory
+     */
+    protected $queryFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\Product
+     */
+    protected $productApple;
+
+    /**
+     * @var \Magento\Catalog\Model\Product
+     */
+    protected $productBanana;
+
+    /**
+     * @var \Magento\Catalog\Model\Product
+     */
+    protected $productOrange;
+
+    /**
+     * @var \Magento\Catalog\Model\Product
+     */
+    protected $productPapaya;
+
+    /**
+     * @var \Magento\Catalog\Model\Product
+     */
+    protected $productCherry;
+
+    /**
+     * @var  \Magento\Framework\Search\Request\Dimension
+     */
+    protected $dimension;
+
+    protected function setUp()
+    {
+        $objectManager = Bootstrap::getObjectManager();
+
+        $objectManager->configure(
+            [
+                ltrim(Fulltext::class, '\\') => [
+                    'arguments' => [
+                        'indexSwitcher' => [
+                            'instance' => ltrim(IndexSwitcherMock::class, '\\'),
+                        ],
+                    ],
+                ],
+            ]
+        );
+
+        /** @var \Magento\Framework\Indexer\IndexerInterface indexer */
+        $this->indexer = $objectManager->create(
+            \Magento\Indexer\Model\Indexer::class
+        );
+        $this->indexer->load('catalogsearch_fulltext');
+
+        $this->engine = $objectManager->get(
+            \Magento\CatalogSearch\Model\ResourceModel\Engine::class
+        );
+
+        $this->resourceFulltext = $objectManager->get(
+            \Magento\CatalogSearch\Model\ResourceModel\Fulltext::class
+        );
+
+        $this->queryFactory = $objectManager->get(
+            \Magento\Search\Model\QueryFactory::class
+        );
+
+        $this->dimension = $objectManager->create(
+            \Magento\Framework\Search\Request\Dimension::class,
+            ['name' => 'scope', 'value' => '1']
+        );
+
+        $this->indexSwitcher = Bootstrap::getObjectManager()->get(
+            IndexSwitcherMock::class
+        );
+
+        $this->productApple = $this->getProductBySku('fulltext-1');
+        $this->productBanana = $this->getProductBySku('fulltext-2');
+        $this->productOrange = $this->getProductBySku('fulltext-3');
+        $this->productPapaya = $this->getProductBySku('fulltext-4');
+        $this->productCherry = $this->getProductBySku('fulltext-5');
+    }
+
+    /**
+     * @magentoAppIsolation enabled
+     */
+    public function testReindexAll()
+    {
+        $this->indexer->reindexAll();
+
+        /** @var IndexSwitcherMock $indexSwitcher */
+        $indexSwitcher = Bootstrap::getObjectManager()->get(
+            IndexSwitcherMock::class
+        );
+        $this->assertTrue($indexSwitcher->isSwitched());
+    }
+
+    /**
+     * @magentoAppIsolation enabled
+     */
+    public function testReindexList()
+    {
+        $this->indexer->reindexList([$this->productApple->getId(), $this->productBanana->getId()]);
+
+        /** @var IndexSwitcherMock $indexSwitcher */
+        $indexSwitcher = Bootstrap::getObjectManager()->get(
+            IndexSwitcherMock::class
+        );
+        $this->assertFalse($indexSwitcher->isSwitched());
+    }
+
+    /**
+     * @magentoAppIsolation enabled
+     */
+    public function testReindexRow()
+    {
+        $this->indexer->reindexRow($this->productPapaya->getId());
+
+        /** @var IndexSwitcherMock $indexSwitcher */
+        $indexSwitcher = Bootstrap::getObjectManager()->get(
+            IndexSwitcherMock::class
+        );
+        $this->assertFalse($indexSwitcher->isSwitched());
+    }
+
+    /**
+     * Search the text and return result collection
+     *
+     * @param string $text
+     * @return Product[]
+     */
+    protected function search($text)
+    {
+        $this->resourceFulltext->resetSearchResults();
+        $query = $this->queryFactory->get();
+        $query->unsetData();
+        $query->setQueryText($text);
+        $query->saveIncrementalPopularity();
+        $products = [];
+        $collection = Bootstrap::getObjectManager()->create(
+            Collection::class,
+            [
+                'searchRequestName' => 'quick_search_container'
+            ]
+        );
+        $collection->addSearchFilter($text);
+        foreach ($collection as $product) {
+            $products[] = $product;
+        }
+        return $products;
+    }
+
+    /**
+     * Return product by SKU
+     *
+     * @param string $sku
+     * @return Product
+     */
+    protected function getProductBySku($sku)
+    {
+        /** @var Product $product */
+        $product = Bootstrap::getObjectManager()->get(
+            \Magento\Catalog\Model\Product::class
+        );
+        return $product->loadByAttribute('sku', $sku);
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php
index 67a6e416973493887912e2e62674108b1694c315..7f50638f469de0f8a5c392f97c74094582a432b9 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php
@@ -331,6 +331,10 @@ class AdapterTest extends \PHPUnit_Framework_TestCase
      *
      * @magentoConfigFixture current_store catalog/search/engine mysql
      * @dataProvider advancedSearchDataProvider
+     * @param string $nameQuery
+     * @param string $descriptionQuery
+     * @param array $rangeFilter
+     * @param int $expectedRecordsCount
      */
     public function testSimpleAdvancedSearch(
         $nameQuery,
@@ -445,6 +449,37 @@ class AdapterTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(1, $queryResponse->count());
     }
 
+    /**
+     * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_disabled_child.php
+     * @magentoConfigFixture current_store catalog/search/engine mysql
+     */
+    public function testAdvancedSearchCompositeProductWithDisabledChild()
+    {
+        /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
+        $attribute = $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
+            ->loadByCode(\Magento\Catalog\Model\Product::ENTITY, 'test_configurable');
+        /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection $selectOptions */
+        $selectOptions = $this->objectManager
+            ->create(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class)
+            ->setAttributeFilter($attribute->getId());
+
+        $firstOption = $selectOptions->getFirstItem();
+        $firstOptionId = $firstOption->getId();
+        $this->requestBuilder->bind('test_configurable', $firstOptionId);
+        $this->requestBuilder->setRequestName('filter_out_of_stock_child');
+
+        $queryResponse = $this->executeQuery();
+        $this->assertEquals(0, $queryResponse->count());
+
+        $secondOption = $selectOptions->getLastItem();
+        $secondOptionId = $secondOption->getId();
+        $this->requestBuilder->bind('test_configurable', $secondOptionId);
+        $this->requestBuilder->setRequestName('filter_out_of_stock_child');
+
+        $queryResponse = $this->executeQuery();
+        $this->assertEquals(0, $queryResponse->count());
+    }
+
     public function dateDataProvider()
     {
         return [
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php
index 590f3c8041c6d819fafa14ef7ddc4d1f2ebccbeb..37b8731fc1d4c377ffc26a16be2d4100997f7247 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php
@@ -15,7 +15,7 @@ use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
 use Magento\Eav\Api\Data\AttributeOptionInterface;
 use Magento\TestFramework\Helper\Bootstrap;
 
-\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize();
+Bootstrap::getInstance()->reinitialize();
 
 require __DIR__ . '/configurable_attribute.php';
 
@@ -33,7 +33,7 @@ $options = $attribute->getOptions();
 $attributeValues = [];
 $attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default');
 $associatedProductIds = [];
-$productIds = [10, 20];
+$productIds = [1010, 1020];
 array_shift($options); //remove the first option which is empty
 
 $isFirstOption = true;
@@ -108,29 +108,8 @@ $extensionConfigurableAttributes->setConfigurableProductLinks($associatedProduct
 
 $product->setExtensionAttributes($extensionConfigurableAttributes);
 
-// Remove any previously created product with the same id.
-/** @var \Magento\Framework\Registry $registry */
-$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
-$registry->unregister('isSecureArea');
-$registry->register('isSecureArea', true);
-try {
-    $productToDelete = $productRepository->getById(1);
-    $productRepository->delete($productToDelete);
-
-    /** @var \Magento\Quote\Model\ResourceModel\Quote\Item $itemResource */
-    $itemResource = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\ResourceModel\Quote\Item::class);
-    $itemResource->getConnection()->delete(
-        $itemResource->getMainTable(),
-        'product_id = ' . $productToDelete->getId()
-    );
-} catch (\Exception $e) {
-    // Nothing to remove
-}
-$registry->unregister('isSecureArea');
-$registry->register('isSecureArea', false);
-
 $product->setTypeId(Configurable::TYPE_CODE)
-    ->setId(1)
+    ->setId(1001)
     ->setAttributeSetId($attributeSetId)
     ->setWebsiteIds([1])
     ->setName('Configurable Product')
@@ -140,8 +119,3 @@ $product->setTypeId(Configurable::TYPE_CODE)
     ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]);
 
 $productRepository->save($product);
-//
-///** @var \Magento\Catalog\Model\Indexer\Product\Eav\Processor $eavIndexer */
-//$eavIndexer = Bootstrap::getObjectManager()
-//    ->get(\Magento\Catalog\Model\Indexer\Product\Eav\Processor::class);
-//$eavIndexer->reindexAll();
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php
index 8ba4e3abe21cc14a75a66270d22ea262dffeb788..cc585d177aedb9201faafcdef8bd57d5ea6dec73 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_rollback.php
@@ -16,7 +16,7 @@ $registry->register('isSecureArea', true);
 $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
     ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
 
-foreach (['simple_10', 'simple_20', 'configurable'] as $sku) {
+foreach (['simple_1010', 'simple_1020', 'configurable'] as $sku) {
     try {
         $product = $productRepository->get($sku, false, null, true);
 
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child.php
new file mode 100644
index 0000000000000000000000000000000000000000..39c9782ba712a7ed2204a61bdcd549863e202ed8
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\TestFramework\Helper\Bootstrap;
+
+require __DIR__ . '/product_configurable.php';
+
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = Bootstrap::getObjectManager()
+    ->create(ProductRepositoryInterface::class);
+
+$product = $productRepository->get('simple_1020');
+$product->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED);
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child_rollback.php
new file mode 100644
index 0000000000000000000000000000000000000000..58d1dcd79acde15c2d493625ca38c656ad286bb7
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable_with_disabled_child_rollback.php
@@ -0,0 +1,7 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+require __DIR__ . '/product_configurable_rollback.php';
diff --git a/lib/internal/Magento/Framework/Indexer/Action/Base.php b/lib/internal/Magento/Framework/Indexer/Action/Base.php
index e7edcae5d4391e27e06020eb60467b093fec05f3..d761f388456b65114ffe0e85698a1e441e82467e 100644
--- a/lib/internal/Magento/Framework/Indexer/Action/Base.php
+++ b/lib/internal/Magento/Framework/Indexer/Action/Base.php
@@ -37,11 +37,13 @@ class Base implements ActionInterface
 
     /**
      * @var AdapterInterface
+     * @deprecated
      */
     protected $connection;
 
     /**
      * @var SourceProviderInterface[]
+     * @deprecated
      */
     protected $sources;
 
@@ -52,6 +54,7 @@ class Base implements ActionInterface
 
     /**
      * @var HandlerInterface[]
+     * @deprecated
      */
     protected $handlers;
 
@@ -62,6 +65,7 @@ class Base implements ActionInterface
 
     /**
      * @var array
+     * @deprecated
      */
     protected $columnTypesMap = [
         'varchar'    => ['type' => Table::TYPE_TEXT, 'size' => 255],
@@ -71,11 +75,13 @@ class Base implements ActionInterface
 
     /**
      * @var array
+     * @deprecated
      */
     protected $filterColumns;
 
     /**
      * @var array
+     * @deprecated
      */
     protected $searchColumns;
 
@@ -96,6 +102,7 @@ class Base implements ActionInterface
 
     /**
      * @var String
+     * @deprecated
      */
     protected $string;
 
@@ -106,11 +113,13 @@ class Base implements ActionInterface
 
     /**
      * @var array
+     * @deprecated
      */
     protected $filterable = [];
 
     /**
      * @var array
+     * @deprecated
      */
     protected $searchable = [];
 
@@ -272,6 +281,7 @@ class Base implements ActionInterface
     protected function createResultCollection()
     {
         $select = $this->getPrimaryResource()->getSelect();
+        $select->reset(\Magento\Framework\DB\Select::COLUMNS);
         $select->columns($this->getPrimaryResource()->getIdFieldName());
         foreach ($this->data['fieldsets'] as $fieldset) {
             if (isset($fieldset['references'])) {
@@ -342,6 +352,8 @@ class Base implements ActionInterface
      *
      * @param array $field
      * @return void
+     *
+     * @deprecated
      */
     protected function saveFieldByType($field)
     {
diff --git a/lib/internal/Magento/Framework/Indexer/Action/Entity.php b/lib/internal/Magento/Framework/Indexer/Action/Entity.php
index e88d281fd4b819f2ff80e7bca44cd0593b300154..b8390b9d5a0022f26f7b87da980565ea4b092829 100644
--- a/lib/internal/Magento/Framework/Indexer/Action/Entity.php
+++ b/lib/internal/Magento/Framework/Indexer/Action/Entity.php
@@ -24,6 +24,6 @@ class Entity extends Base
     {
         return !count($ids)
             ? $this->createResultCollection()
-            : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getRowIdFieldName(), $ids);
+            : $this->createResultCollection()->addFieldToFilter($this->getPrimaryResource()->getIdFieldName(), $ids);
     }
 }
diff --git a/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php b/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php
index 31883fa8516ea112312eb897f3d0f8c0a93cfced..7d53e558287bd424afe723acf5320fb7f9a07185 100644
--- a/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php
+++ b/lib/internal/Magento/Framework/Indexer/SaveHandler/Batch.php
@@ -10,27 +10,23 @@ class Batch
     /**
      * @param \Traversable $documents
      * @param int $size
-     * @return array
+     * @return \Generator
      */
     public function getItems(\Traversable $documents, $size)
     {
-        if (count($documents) == 0) {
-            return [];
-        }
-
         $i = 0;
-        $batch = $items = [];
+        $batch = [];
+
         foreach ($documents as $documentName => $documentValue) {
             $batch[$documentName] = $documentValue;
-            if (++$i >= $size) {
-                $items[] = $batch;
+            if (++$i == $size) {
+                yield $batch;
                 $i = 0;
                 $batch = [];
             }
         }
         if (count($batch) > 0) {
-            $items[] = $batch;
+            yield $batch;
         }
-        return $items;
     }
 }
diff --git a/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php b/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php
index eaab236bbca5bd3dddf6386f7490417bc8be584a..8bcb4acaa11a314e3bd937468cd202fd17ec81b2 100644
--- a/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php
+++ b/lib/internal/Magento/Framework/Indexer/ScopeResolver/IndexScopeResolver.php
@@ -52,6 +52,7 @@ class IndexScopeResolver implements IndexScopeResolverInterface
                     $tableNameParts[] = $dimension->getName() . $dimension->getValue();
             }
         }
+
         return $this->resource->getTableName(implode('_', $tableNameParts));
     }
 
@@ -63,10 +64,12 @@ class IndexScopeResolver implements IndexScopeResolverInterface
      */
     private function getScopeId($dimension)
     {
-        if (is_numeric($dimension->getValue())) {
-            return $dimension->getValue();
-        } else {
-            return $this->scopeResolver->getScope($dimension->getValue())->getId();
+        $scopeId = $dimension->getValue();
+
+        if (!is_numeric($scopeId)) {
+            $scopeId = $this->scopeResolver->getScope($scopeId)->getId();
         }
+
+        return $scopeId;
     }
 }
diff --git a/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php b/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php
index 0df98125f9a08b7dd9a63e43fdf1070a5af693de..1571bba0a7d23ce224a7d403aa63f624f58bc313 100644
--- a/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php
+++ b/lib/internal/Magento/Framework/Indexer/Test/Unit/BatchTest.php
@@ -27,7 +27,8 @@ class BatchTest extends \PHPUnit_Framework_TestCase
     public function testGetItems(array $itemsData, $size, array $expected)
     {
         $items = new \ArrayObject($itemsData);
-        $this->assertSame($expected, $this->object->getItems($items, $size));
+        $data = $this->object->getItems($items, $size);
+        $this->assertSame($expected, iterator_to_array($data));
     }
 
     /**