diff --git a/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Rows.php b/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Rows.php index 5d81c1405efe0..c53277a58157d 100644 --- a/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Rows.php +++ b/vendor/magento/module-catalog/Model/Indexer/Category/Product/Action/Rows.php @@ -13,15 +13,17 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Indexer\IndexerRegistry; use Magento\Catalog\Model\Config; use Magento\Catalog\Model\Category; -use Magento\Framework\Indexer\IndexerRegistry; use Magento\Catalog\Model\Indexer\Product\Category as ProductCategoryIndexer; +use Magento\Catalog\Model\Indexer\Category\Product as CategoryProductIndexer; +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Indexer\Model\WorkingStateProvider; /** * Reindex multiple rows action. * - * @package Magento\Catalog\Model\Indexer\Category\Product\Action * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Rows extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction @@ -48,15 +50,23 @@ class Rows extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio */ private $indexerRegistry; + /** + * @var WorkingStateProvider + */ + private $workingStateProvider; + /** * @param ResourceConnection $resource * @param StoreManagerInterface $storeManager * @param Config $config * @param QueryGenerator|null $queryGenerator * @param MetadataPool|null $metadataPool + * @param TableMaintainer|null $tableMaintainer * @param CacheContext|null $cacheContext * @param EventManagerInterface|null $eventManager * @param IndexerRegistry|null $indexerRegistry + * @param WorkingStateProvider|null $workingStateProvider + * @SuppressWarnings(PHPMD.ExcessiveParameterList) Preserve compatibility with the parent class */ public function __construct( ResourceConnection $resource, @@ -64,14 +74,18 @@ public function __construct( Config $config, QueryGenerator $queryGenerator = null, MetadataPool $metadataPool = null, + ?TableMaintainer $tableMaintainer = null, CacheContext $cacheContext = null, EventManagerInterface $eventManager = null, - IndexerRegistry $indexerRegistry = null + IndexerRegistry $indexerRegistry = null, + ?WorkingStateProvider $workingStateProvider = null ) { - parent::__construct($resource, $storeManager, $config, $queryGenerator, $metadataPool); + parent::__construct($resource, $storeManager, $config, $queryGenerator, $metadataPool, $tableMaintainer); $this->cacheContext = $cacheContext ?: ObjectManager::getInstance()->get(CacheContext::class); $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(EventManagerInterface::class); $this->indexerRegistry = $indexerRegistry ?: ObjectManager::getInstance()->get(IndexerRegistry::class); + $this->workingStateProvider = $workingStateProvider ?: + ObjectManager::getInstance()->get(WorkingStateProvider::class); } /** @@ -97,44 +111,64 @@ public function execute(array $entityIds = [], $useTempTable = false) $this->limitationByCategories = array_unique($this->limitationByCategories); $this->useTempTable = $useTempTable; $indexer = $this->indexerRegistry->get(ProductCategoryIndexer::INDEXER_ID); - $workingState = $indexer->isWorking(); + $workingState = $this->isWorkingState(); - if ($useTempTable && !$workingState && $indexer->isScheduled()) { - foreach ($this->storeManager->getStores() as $store) { - $this->connection->truncateTable($this->getIndexTable($store->getId())); + if (!$indexer->isScheduled() + || ($indexer->isScheduled() && !$useTempTable) + || ($indexer->isScheduled() && $useTempTable && !$workingState)) { + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $this->connection->truncateTable($this->getIndexTable($store->getId())); + } + } else { + $this->removeEntries(); } - } else { - $this->removeEntries(); - } - $this->reindex(); - - if ($useTempTable && !$workingState && $indexer->isScheduled()) { - foreach ($this->storeManager->getStores() as $store) { - $removalCategoryIds = array_diff($this->limitationByCategories, [$this->getRootCategoryId($store)]); - $this->connection->delete( - $this->tableMaintainer->getMainTable($store->getId()), - ['category_id IN (?)' => $removalCategoryIds] - ); - $select = $this->connection->select() - ->from($this->tableMaintainer->getMainReplicaTable($store->getId())); - $this->connection->query( - $this->connection->insertFromSelect( - $select, + $this->reindex(); + + // get actual state + $workingState = $this->isWorkingState(); + + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $removalCategoryIds = array_diff($this->limitationByCategories, [$this->getRootCategoryId($store)]); + $this->connection->delete( $this->tableMaintainer->getMainTable($store->getId()), - [], - AdapterInterface::INSERT_ON_DUPLICATE - ) - ); + ['category_id IN (?)' => $removalCategoryIds] + ); + $select = $this->connection->select() + ->from($this->tableMaintainer->getMainReplicaTable($store->getId())); + $this->connection->query( + $this->connection->insertFromSelect( + $select, + $this->tableMaintainer->getMainTable($store->getId()), + [], + AdapterInterface::INSERT_ON_DUPLICATE + ) + ); + } } - } - $this->registerCategories($entityIds); - $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + $this->registerCategories($entityIds); + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + } return $this; } + /** + * Get state for current and shared indexer + * + * @return bool + */ + private function isWorkingState() : bool + { + $indexer = $this->indexerRegistry->get(ProductCategoryIndexer::INDEXER_ID); + $sharedIndexer = $this->indexerRegistry->get(CategoryProductIndexer::INDEXER_ID); + return $this->workingStateProvider->isWorking($indexer->getId()) + || $this->workingStateProvider->isWorking($sharedIndexer->getId()); + } + /** * Register categories assigned to products * diff --git a/vendor/magento/module-catalog/Model/Indexer/Product/Category/Action/Rows.php b/vendor/magento/module-catalog/Model/Indexer/Product/Category/Action/Rows.php index ec3d0d57330ec..b979491dba9e4 100644 --- a/vendor/magento/module-catalog/Model/Indexer/Product/Category/Action/Rows.php +++ b/vendor/magento/module-catalog/Model/Indexer/Product/Category/Action/Rows.php @@ -17,7 +17,10 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Catalog\Model\Indexer\Product\Category as ProductCategoryIndexer; use Magento\Catalog\Model\Indexer\Category\Product as CategoryProductIndexer; +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Indexer\Model\WorkingStateProvider; /** * Category rows indexer. @@ -48,15 +51,23 @@ class Rows extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio */ private $indexerRegistry; + /** + * @var WorkingStateProvider + */ + private $workingStateProvider; + /** * @param ResourceConnection $resource * @param StoreManagerInterface $storeManager * @param Config $config * @param QueryGenerator|null $queryGenerator * @param MetadataPool|null $metadataPool + * @param TableMaintainer|null $tableMaintainer * @param CacheContext|null $cacheContext * @param EventManagerInterface|null $eventManager * @param IndexerRegistry|null $indexerRegistry + * @param WorkingStateProvider|null $workingStateProvider + * @SuppressWarnings(PHPMD.ExcessiveParameterList) Preserve compatibility with the parent class */ public function __construct( ResourceConnection $resource, @@ -64,14 +75,18 @@ public function __construct( Config $config, QueryGenerator $queryGenerator = null, MetadataPool $metadataPool = null, + ?TableMaintainer $tableMaintainer = null, CacheContext $cacheContext = null, EventManagerInterface $eventManager = null, - IndexerRegistry $indexerRegistry = null + IndexerRegistry $indexerRegistry = null, + ?WorkingStateProvider $workingStateProvider = null ) { - parent::__construct($resource, $storeManager, $config, $queryGenerator, $metadataPool); + parent::__construct($resource, $storeManager, $config, $queryGenerator, $metadataPool, $tableMaintainer); $this->cacheContext = $cacheContext ?: ObjectManager::getInstance()->get(CacheContext::class); $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(EventManagerInterface::class); $this->indexerRegistry = $indexerRegistry ?: ObjectManager::getInstance()->get(IndexerRegistry::class); + $this->workingStateProvider = $workingStateProvider ?: + ObjectManager::getInstance()->get(WorkingStateProvider::class); } /** @@ -82,6 +97,7 @@ public function __construct( * @return $this * @throws \Exception if metadataPool doesn't contain metadata for ProductInterface * @throws \DomainException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute(array $entityIds = [], $useTempTable = false) { @@ -90,46 +106,68 @@ public function execute(array $entityIds = [], $useTempTable = false) $this->limitationByProducts = $idsToBeReIndexed; $this->useTempTable = $useTempTable; $indexer = $this->indexerRegistry->get(CategoryProductIndexer::INDEXER_ID); - $workingState = $indexer->isWorking(); + $workingState = $this->isWorkingState(); - $affectedCategories = $this->getCategoryIdsFromIndex($idsToBeReIndexed); + if (!$indexer->isScheduled() + || ($indexer->isScheduled() && !$useTempTable) + || ($indexer->isScheduled() && $useTempTable && !$workingState)) { - if ($useTempTable && !$workingState && $indexer->isScheduled()) { - foreach ($this->storeManager->getStores() as $store) { - $this->connection->truncateTable($this->getIndexTable($store->getId())); + $affectedCategories = $this->getCategoryIdsFromIndex($idsToBeReIndexed); + + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $this->connection->truncateTable($this->getIndexTable($store->getId())); + } + } else { + $this->removeEntries(); } - } else { - $this->removeEntries(); - } - $this->reindex(); - if ($useTempTable && !$workingState && $indexer->isScheduled()) { - foreach ($this->storeManager->getStores() as $store) { - $this->connection->delete( - $this->tableMaintainer->getMainTable($store->getId()), - ['product_id IN (?)' => $this->limitationByProducts] - ); - $select = $this->connection->select() - ->from($this->tableMaintainer->getMainReplicaTable($store->getId())); - $this->connection->query( - $this->connection->insertFromSelect( - $select, + $this->reindex(); + + // get actual state + $workingState = $this->isWorkingState(); + + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $this->connection->delete( $this->tableMaintainer->getMainTable($store->getId()), - [], - AdapterInterface::INSERT_ON_DUPLICATE - ) - ); + ['product_id IN (?)' => $this->limitationByProducts] + ); + $select = $this->connection->select() + ->from($this->tableMaintainer->getMainReplicaTable($store->getId())); + $this->connection->query( + $this->connection->insertFromSelect( + $select, + $this->tableMaintainer->getMainTable($store->getId()), + [], + AdapterInterface::INSERT_ON_DUPLICATE + ) + ); + } } - } - $affectedCategories = array_merge($affectedCategories, $this->getCategoryIdsFromIndex($idsToBeReIndexed)); + $affectedCategories = array_merge($affectedCategories, $this->getCategoryIdsFromIndex($idsToBeReIndexed)); - $this->registerProducts($idsToBeReIndexed); - $this->registerCategories($affectedCategories); - $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + $this->registerProducts($idsToBeReIndexed); + $this->registerCategories($affectedCategories); + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + } return $this; } + /** + * Get state for current and shared indexer + * + * @return bool + */ + private function isWorkingState() : bool + { + $indexer = $this->indexerRegistry->get(CategoryProductIndexer::INDEXER_ID); + $sharedIndexer = $this->indexerRegistry->get(ProductCategoryIndexer::INDEXER_ID); + return $this->workingStateProvider->isWorking($indexer->getId()) + || $this->workingStateProvider->isWorking($sharedIndexer->getId()); + } + /** * Get IDs of parent products by their child IDs. * diff --git a/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php b/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php index 27b50eea883b0..dfeb5b6bfea26 100644 --- a/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php +++ b/vendor/magento/module-catalog/Model/Indexer/Product/Price/Action/Rows.php @@ -5,12 +5,81 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Price\Action; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\TierPrice; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Stdlib\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Model\StoreManagerInterface; + /** * Class Rows reindex action for mass actions * */ class Rows extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction { + /** + * Default batch size + */ + private const BATCH_SIZE = 100; + + /** + * @var int + */ + private $batchSize; + + /** + * @param ScopeConfigInterface $config + * @param StoreManagerInterface $storeManager + * @param CurrencyFactory $currencyFactory + * @param TimezoneInterface $localeDate + * @param DateTime $dateTime + * @param Type $catalogProductType + * @param Factory $indexerPriceFactory + * @param DefaultPrice $defaultIndexerResource + * @param TierPrice|null $tierPriceIndexResource + * @param DimensionCollectionFactory|null $dimensionCollectionFactory + * @param TableMaintainer|null $tableMaintainer + * @param int|null $batchSize + * @SuppressWarnings(PHPMD.NPathComplexity) Added to backward compatibility with abstract class + * @SuppressWarnings(PHPMD.CyclomaticComplexity) Added to backward compatibility with abstract class + * @SuppressWarnings(PHPMD.ExcessiveParameterList) Added to backward compatibility with abstract class + */ + public function __construct( + ScopeConfigInterface $config, + StoreManagerInterface $storeManager, + CurrencyFactory $currencyFactory, + TimezoneInterface $localeDate, + DateTime $dateTime, + Type $catalogProductType, + Factory $indexerPriceFactory, + DefaultPrice $defaultIndexerResource, + TierPrice $tierPriceIndexResource = null, + DimensionCollectionFactory $dimensionCollectionFactory = null, + TableMaintainer $tableMaintainer = null, + ?int $batchSize = null + ) { + parent::__construct( + $config, + $storeManager, + $currencyFactory, + $localeDate, + $dateTime, + $catalogProductType, + $indexerPriceFactory, + $defaultIndexerResource, + $tierPriceIndexResource, + $dimensionCollectionFactory, + $tableMaintainer + ); + $this->batchSize = $batchSize ?? self::BATCH_SIZE; + } + /** * Execute Rows reindex * @@ -24,10 +93,28 @@ public function execute($ids) if (empty($ids)) { throw new \Magento\Framework\Exception\InputException(__('Bad value was supplied.')); } - try { - $this->_reindexRows($ids); - } catch (\Exception $e) { - throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); + $currentBatch = []; + $i = 0; + + foreach ($ids as $id) { + $currentBatch[] = $id; + if (++$i === $this->batchSize) { + try { + $this->_reindexRows($currentBatch); + } catch (\Exception $e) { + throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); + } + $i = 0; + $currentBatch = []; + } + } + + if (!empty($currentBatch)) { + try { + $this->_reindexRows($currentBatch); + } catch (\Exception $e) { + throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); + } } } } diff --git a/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php b/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php index af3625f6dcc2c..d02f33e51f3d4 100644 --- a/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php +++ b/vendor/magento/module-catalog-search/Model/Indexer/Fulltext.php @@ -12,6 +12,7 @@ use Magento\CatalogSearch\Model\ResourceModel\Fulltext as FulltextResource; use Magento\Framework\App\ObjectManager; use Magento\Framework\Indexer\DimensionProviderInterface; +use Magento\Framework\Indexer\SaveHandler\IndexerInterface; use Magento\Store\Model\StoreDimensionProvider; use Magento\Indexer\Model\ProcessManager; @@ -33,6 +34,11 @@ class Fulltext implements */ const INDEXER_ID = 'catalogsearch_fulltext'; + /** + * Default batch size + */ + private const BATCH_SIZE = 100; + /** * @var array index structure */ @@ -73,6 +79,11 @@ class Fulltext implements */ private $processManager; + /** + * @var int + */ + private $batchSize; + /** * @param FullFactory $fullActionFactory * @param IndexerHandlerFactory $indexerHandlerFactory @@ -82,6 +93,7 @@ class Fulltext implements * @param DimensionProviderInterface $dimensionProvider * @param array $data * @param ProcessManager $processManager + * @param int|null $batchSize * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( @@ -92,7 +104,8 @@ public function __construct( StateFactory $indexScopeStateFactory, DimensionProviderInterface $dimensionProvider, array $data, - ProcessManager $processManager = null + ProcessManager $processManager = null, + ?int $batchSize = null ) { $this->fullAction = $fullActionFactory->create(['data' => $data]); $this->indexerHandlerFactory = $indexerHandlerFactory; @@ -102,6 +115,7 @@ public function __construct( $this->indexScopeState = ObjectManager::getInstance()->get(State::class); $this->dimensionProvider = $dimensionProvider; $this->processManager = $processManager ?: ObjectManager::getInstance()->get(ProcessManager::class); + $this->batchSize = $batchSize ?? self::BATCH_SIZE; } /** @@ -148,13 +162,42 @@ public function executeByDimensions(array $dimensions, \Traversable $entityIds = } else { // internal implementation works only with array $entityIds = iterator_to_array($entityIds); - $productIds = array_unique( - array_merge($entityIds, $this->fulltextResource->getRelationsByChild($entityIds)) - ); - if ($saveHandler->isAvailable($dimensions)) { - $saveHandler->deleteIndex($dimensions, new \ArrayIterator($productIds)); - $saveHandler->saveIndex($dimensions, $this->fullAction->rebuildStoreIndex($storeId, $productIds)); + $currentBatch = []; + $i = 0; + + foreach ($entityIds as $entityId) { + $currentBatch[] = $entityId; + if (++$i === $this->batchSize) { + $this->processBatch($saveHandler, $dimensions, $currentBatch); + $i = 0; + $currentBatch = []; + } } + if (!empty($currentBatch)) { + $this->processBatch($saveHandler, $dimensions, $currentBatch); + } + } + } + + /** + * Process batch + * + * @param IndexerInterface $saveHandler + * @param array $dimensions + * @param array $entityIds + */ + private function processBatch( + IndexerInterface $saveHandler, + array $dimensions, + array $entityIds + ) : void { + $storeId = $dimensions[StoreDimensionProvider::DIMENSION_NAME]->getValue(); + $productIds = array_unique( + array_merge($entityIds, $this->fulltextResource->getRelationsByChild($entityIds)) + ); + if ($saveHandler->isAvailable($dimensions)) { + $saveHandler->deleteIndex($dimensions, new \ArrayIterator($productIds)); + $saveHandler->saveIndex($dimensions, $this->fullAction->rebuildStoreIndex($storeId, $productIds)); } } diff --git a/vendor/magento/module-indexer/Model/Indexer.php b/vendor/magento/module-indexer/Model/Indexer.php index 2821a46f29416..ac8b9590e58f4 100644 --- a/vendor/magento/module-indexer/Model/Indexer.php +++ b/vendor/magento/module-indexer/Model/Indexer.php @@ -13,6 +13,7 @@ use Magento\Framework\Indexer\IndexStructureInterface; use Magento\Framework\Indexer\StateInterface; use Magento\Framework\Indexer\StructureFactory; +use Magento\Framework\Indexer\IndexerInterfaceFactory; /** * Indexer model. @@ -61,6 +62,16 @@ class Indexer extends \Magento\Framework\DataObject implements IndexerInterface */ protected $indexersFactory; + /** + * @var WorkingStateProvider + */ + private $workingStateProvider; + + /** + * @var IndexerInterfaceFactory + */ + private $indexerFactory; + /** * @param ConfigInterface $config * @param ActionFactory $actionFactory @@ -68,6 +79,8 @@ class Indexer extends \Magento\Framework\DataObject implements IndexerInterface * @param \Magento\Framework\Mview\ViewInterface $view * @param Indexer\StateFactory $stateFactory * @param Indexer\CollectionFactory $indexersFactory + * @param WorkingStateProvider $workingStateProvider + * @param IndexerInterfaceFactory $indexerFactory * @param array $data */ public function __construct( @@ -77,6 +90,8 @@ public function __construct( \Magento\Framework\Mview\ViewInterface $view, Indexer\StateFactory $stateFactory, Indexer\CollectionFactory $indexersFactory, + WorkingStateProvider $workingStateProvider, + IndexerInterfaceFactory $indexerFactory, array $data = [] ) { $this->config = $config; @@ -85,6 +100,8 @@ public function __construct( $this->view = $view; $this->stateFactory = $stateFactory; $this->indexersFactory = $indexersFactory; + $this->workingStateProvider = $workingStateProvider; + $this->indexerFactory = $indexerFactory; parent::__construct($data); } @@ -405,10 +422,20 @@ protected function getStructureInstance() */ public function reindexAll() { - if ($this->getState()->getStatus() != StateInterface::STATUS_WORKING) { + if (!$this->workingStateProvider->isWorking($this->getId())) { $state = $this->getState(); $state->setStatus(StateInterface::STATUS_WORKING); $state->save(); + + $sharedIndexers = []; + $indexerConfig = $this->config->getIndexer($this->getId()); + if ($indexerConfig['shared_index'] !== null) { + $sharedIndexers = $this->getSharedIndexers($indexerConfig['shared_index']); + } + if (!empty($sharedIndexers)) { + $this->suspendSharedViews($sharedIndexers); + } + if ($this->getView()->isEnabled()) { $this->getView()->suspend(); } @@ -416,16 +443,73 @@ public function reindexAll() $this->getActionInstance()->executeFull(); $state->setStatus(StateInterface::STATUS_VALID); $state->save(); + if (!empty($sharedIndexers)) { + $this->resumeSharedViews($sharedIndexers); + } $this->getView()->resume(); } catch (\Throwable $exception) { $state->setStatus(StateInterface::STATUS_INVALID); $state->save(); + if (!empty($sharedIndexers)) { + $this->resumeSharedViews($sharedIndexers); + } $this->getView()->resume(); throw $exception; } } } + /** + * Get indexer ids that uses same index + * + * @param string $sharedIndex + * @return array + */ + private function getSharedIndexers(string $sharedIndex) : array + { + $result = []; + foreach (array_keys($this->config->getIndexers()) as $indexerId) { + if ($indexerId === $this->getId()) { + continue; + } + $indexerConfig = $this->config->getIndexer($indexerId); + if ($indexerConfig['shared_index'] === $sharedIndex) { + $indexer = $this->indexerFactory->create(); + $indexer->load($indexerId); + $result[] = $indexer; + } + } + return $result; + } + + /** + * Suspend views of shared indexers + * + * @param array $sharedIndexers + * @return void + */ + private function suspendSharedViews(array $sharedIndexers) : void + { + foreach ($sharedIndexers as $indexer) { + if ($indexer->getView()->isEnabled()) { + $indexer->getView()->suspend(); + } + } + } + + /** + * Suspend views of shared indexers + * + * @param array $sharedIndexers + * @return void + */ + private function resumeSharedViews(array $sharedIndexers) : void + { + foreach ($sharedIndexers as $indexer) { + $indexer->getView()->resume(); + } + } + /** * Regenerate one row in index by ID * diff --git a/vendor/magento/module-indexer/Model/Processor.php b/vendor/magento/module-indexer/Model/Processor.php index ea84cf81a2a2c..78b8fa070b155 100644 --- a/vendor/magento/module-indexer/Model/Processor.php +++ b/vendor/magento/module-indexer/Model/Processor.php @@ -5,16 +5,23 @@ */ namespace Magento\Indexer\Model; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Indexer\ConfigInterface; use Magento\Framework\Indexer\IndexerInterface; use Magento\Framework\Indexer\IndexerInterfaceFactory; -use Magento\Framework\Indexer\StateInterface; +use Magento\Framework\Mview\ProcessorInterface; +use Magento\Indexer\Model\Processor\MakeSharedIndexValid; /** * Indexer processor */ class Processor { + /** + * @var array + */ + private $sharedIndexesComplete = []; + /** * @var ConfigInterface */ @@ -31,26 +38,34 @@ class Processor protected $indexersFactory; /** - * @var \Magento\Framework\Mview\ProcessorInterface + * @var ProcessorInterface */ protected $mviewProcessor; + /** + * @var MakeSharedIndexValid + */ + protected $makeSharedValid; + /** * @param ConfigInterface $config * @param IndexerInterfaceFactory $indexerFactory * @param Indexer\CollectionFactory $indexersFactory - * @param \Magento\Framework\Mview\ProcessorInterface $mviewProcessor + * @param ProcessorInterface $mviewProcessor + * @param MakeSharedIndexValid|null $makeSharedValid */ public function __construct( ConfigInterface $config, IndexerInterfaceFactory $indexerFactory, Indexer\CollectionFactory $indexersFactory, - \Magento\Framework\Mview\ProcessorInterface $mviewProcessor + ProcessorInterface $mviewProcessor, + MakeSharedIndexValid $makeSharedValid = null ) { $this->config = $config; $this->indexerFactory = $indexerFactory; $this->indexersFactory = $indexersFactory; $this->mviewProcessor = $mviewProcessor; + $this->makeSharedValid = $makeSharedValid ?: ObjectManager::getInstance()->get(MakeSharedIndexValid::class); } /** @@ -60,27 +75,21 @@ public function __construct( */ public function reindexAllInvalid() { - $sharedIndexesComplete = []; foreach (array_keys($this->config->getIndexers()) as $indexerId) { /** @var Indexer $indexer */ $indexer = $this->indexerFactory->create(); $indexer->load($indexerId); $indexerConfig = $this->config->getIndexer($indexerId); + if ($indexer->isInvalid()) { // Skip indexers having shared index that was already complete $sharedIndex = $indexerConfig['shared_index'] ?? null; - if (!in_array($sharedIndex, $sharedIndexesComplete)) { + if (!in_array($sharedIndex, $this->sharedIndexesComplete)) { $indexer->reindexAll(); - } else { - /** @var \Magento\Indexer\Model\Indexer\State $state */ - $state = $indexer->getState(); - $state->setStatus(StateInterface::STATUS_WORKING); - $state->save(); - $state->setStatus(StateInterface::STATUS_VALID); - $state->save(); - } - if ($sharedIndex) { - $sharedIndexesComplete[] = $indexerConfig['shared_index']; + + if (!empty($sharedIndex) && $this->makeSharedValid->execute($sharedIndex)) { + $this->sharedIndexesComplete[] = $sharedIndex; + } } } } diff --git a/vendor/magento/module-indexer/Model/Processor/MakeSharedIndexValid.php b/vendor/magento/module-indexer/Model/Processor/MakeSharedIndexValid.php new file mode 100644 index 0000000000000..14c76bac86199 --- /dev/null +++ b/vendor/magento/module-indexer/Model/Processor/MakeSharedIndexValid.php @@ -0,0 +1,95 @@ +config = $config; + $this->indexerRegistry = $indexerRegistry; + } + + /** + * Validate indexers by shared index ID + * + * @param string $sharedIndex + * @return bool + * @throws \Exception + */ + public function execute(string $sharedIndex): bool + { + if (empty($sharedIndex)) { + throw new \InvalidArgumentException( + "The '{$sharedIndex}' is an invalid shared index identifier. Verify the identifier and try again." + ); + } + + $indexerIds = $this->getIndexerIdsBySharedIndex($sharedIndex); + if (empty($indexerIds)) { + return false; + } + + foreach ($indexerIds as $indexerId) { + $indexer = $this->indexerRegistry->get($indexerId); + /** @var State $state */ + $state = $indexer->getState(); + $state->setStatus(StateInterface::STATUS_WORKING); + $state->save(); + $state->setStatus(StateInterface::STATUS_VALID); + $state->save(); + } + + return true; + } + + /** + * Get indexer ids that have common shared index + * + * @param string $sharedIndex + * @return array + */ + private function getIndexerIdsBySharedIndex(string $sharedIndex): array + { + $indexers = $this->config->getIndexers(); + + $result = []; + foreach ($indexers as $indexerConfig) { + if ($indexerConfig['shared_index'] == $sharedIndex) { + $result[] = $indexerConfig['indexer_id']; + } + } + + return $result; + } +} diff --git a/vendor/magento/module-indexer/Model/WorkingStateProvider.php b/vendor/magento/module-indexer/Model/WorkingStateProvider.php new file mode 100644 index 0000000000000..37dd1025f3b13 --- /dev/null +++ b/vendor/magento/module-indexer/Model/WorkingStateProvider.php @@ -0,0 +1,45 @@ +stateFactory = $stateFactory; + } + + /** + * Execute user functions + * + * @param string $indexerId + * @return bool + */ + public function isWorking(string $indexerId) : bool + { + $state = $this->stateFactory->create(); + $state->loadByIndexer($indexerId); + + return $state->getStatus() === StateInterface::STATUS_WORKING; + } +}