IndexerReindexCommand.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Indexer\Console\Command;
  7. use Magento\Framework\Console\Cli;
  8. use Magento\Framework\Exception\LocalizedException;
  9. use Magento\Framework\Indexer\Config\DependencyInfoProvider;
  10. use Magento\Framework\Indexer\IndexerInterface;
  11. use Magento\Framework\Indexer\IndexerRegistry;
  12. use Magento\Framework\Indexer\StateInterface;
  13. use Symfony\Component\Console\Input\InputInterface;
  14. use Symfony\Component\Console\Output\OutputInterface;
  15. use Magento\Framework\Indexer\ConfigInterface;
  16. use Magento\Framework\App\ObjectManagerFactory;
  17. /**
  18. * Command to run indexers
  19. */
  20. class IndexerReindexCommand extends AbstractIndexerManageCommand
  21. {
  22. /**
  23. * @var array
  24. */
  25. private $sharedIndexesComplete = [];
  26. /**
  27. * @var ConfigInterface
  28. */
  29. private $config;
  30. /**
  31. * @var IndexerRegistry
  32. */
  33. private $indexerRegistry;
  34. /**
  35. * @var DependencyInfoProvider|null
  36. */
  37. private $dependencyInfoProvider;
  38. /**
  39. * @param ObjectManagerFactory $objectManagerFactory
  40. * @param IndexerRegistry|null $indexerRegistry
  41. * @param DependencyInfoProvider|null $dependencyInfoProvider
  42. */
  43. public function __construct(
  44. ObjectManagerFactory $objectManagerFactory,
  45. IndexerRegistry $indexerRegistry = null,
  46. DependencyInfoProvider $dependencyInfoProvider = null
  47. ) {
  48. $this->indexerRegistry = $indexerRegistry;
  49. $this->dependencyInfoProvider = $dependencyInfoProvider;
  50. parent::__construct($objectManagerFactory);
  51. }
  52. /**
  53. * @inheritdoc
  54. */
  55. protected function configure()
  56. {
  57. $this->setName('indexer:reindex')
  58. ->setDescription('Reindexes Data')
  59. ->setDefinition($this->getInputList());
  60. parent::configure();
  61. }
  62. /**
  63. * @inheritdoc
  64. */
  65. protected function execute(InputInterface $input, OutputInterface $output)
  66. {
  67. $returnValue = Cli::RETURN_FAILURE;
  68. foreach ($this->getIndexers($input) as $indexer) {
  69. try {
  70. $this->validateIndexerStatus($indexer);
  71. $startTime = microtime(true);
  72. $indexerConfig = $this->getConfig()->getIndexer($indexer->getId());
  73. $sharedIndex = $indexerConfig['shared_index'];
  74. // Skip indexers having shared index that was already complete
  75. if (!in_array($sharedIndex, $this->sharedIndexesComplete)) {
  76. $indexer->reindexAll();
  77. if ($sharedIndex) {
  78. $this->validateSharedIndex($sharedIndex);
  79. }
  80. }
  81. $resultTime = microtime(true) - $startTime;
  82. $output->writeln(
  83. $indexer->getTitle() . ' index has been rebuilt successfully in ' . gmdate('H:i:s', $resultTime)
  84. );
  85. $returnValue = Cli::RETURN_SUCCESS;
  86. } catch (LocalizedException $e) {
  87. $output->writeln($e->getMessage());
  88. } catch (\Exception $e) {
  89. $output->writeln($indexer->getTitle() . ' indexer process unknown error:');
  90. $output->writeln($e->getMessage());
  91. }
  92. }
  93. return $returnValue;
  94. }
  95. /**
  96. * @inheritdoc
  97. *
  98. * Returns the ordered list of specified indexers and related indexers.
  99. */
  100. protected function getIndexers(InputInterface $input)
  101. {
  102. $indexers = parent::getIndexers($input);
  103. $allIndexers = $this->getAllIndexers();
  104. if (!array_diff_key($allIndexers, $indexers)) {
  105. return $indexers;
  106. }
  107. $relatedIndexers = [];
  108. $dependentIndexers = [];
  109. foreach ($indexers as $indexer) {
  110. $relatedIndexers = array_merge(
  111. $relatedIndexers,
  112. $this->getRelatedIndexerIds($indexer->getId())
  113. );
  114. $dependentIndexers = array_merge(
  115. $dependentIndexers,
  116. $this->getDependentIndexerIds($indexer->getId())
  117. );
  118. }
  119. $invalidRelatedIndexers = [];
  120. foreach (array_unique($relatedIndexers) as $relatedIndexer) {
  121. if ($allIndexers[$relatedIndexer]->isInvalid()) {
  122. $invalidRelatedIndexers[] = $relatedIndexer;
  123. }
  124. }
  125. return array_intersect_key(
  126. $allIndexers,
  127. array_flip(
  128. array_unique(
  129. array_merge(
  130. array_keys($indexers),
  131. $invalidRelatedIndexers,
  132. $dependentIndexers
  133. )
  134. )
  135. )
  136. );
  137. }
  138. /**
  139. * Return all indexer Ids on which the current indexer depends (directly or indirectly).
  140. *
  141. * @param string $indexerId
  142. * @return array
  143. */
  144. private function getRelatedIndexerIds(string $indexerId)
  145. {
  146. $relatedIndexerIds = [];
  147. foreach ($this->getDependencyInfoProvider()->getIndexerIdsToRunBefore($indexerId) as $relatedIndexerId) {
  148. $relatedIndexerIds = array_merge(
  149. $relatedIndexerIds,
  150. [$relatedIndexerId],
  151. $this->getRelatedIndexerIds($relatedIndexerId)
  152. );
  153. }
  154. return array_unique($relatedIndexerIds);
  155. }
  156. /**
  157. * Return all indexer Ids which depend on the current indexer (directly or indirectly).
  158. *
  159. * @param string $indexerId
  160. * @return array
  161. */
  162. private function getDependentIndexerIds(string $indexerId)
  163. {
  164. $dependentIndexerIds = [];
  165. foreach (array_keys($this->getConfig()->getIndexers()) as $id) {
  166. $dependencies = $this->getDependencyInfoProvider()->getIndexerIdsToRunBefore($id);
  167. if (array_search($indexerId, $dependencies) !== false) {
  168. $dependentIndexerIds = array_merge(
  169. $dependentIndexerIds,
  170. [$id],
  171. $this->getDependentIndexerIds($id)
  172. );
  173. }
  174. }
  175. return array_unique($dependentIndexerIds);
  176. }
  177. /**
  178. * Validate that indexer is not locked
  179. *
  180. * @param IndexerInterface $indexer
  181. * @return void
  182. * @throws LocalizedException
  183. */
  184. private function validateIndexerStatus(IndexerInterface $indexer)
  185. {
  186. if ($indexer->getStatus() == StateInterface::STATUS_WORKING) {
  187. throw new LocalizedException(
  188. __(
  189. '%1 index is locked by another reindex process. Skipping.',
  190. $indexer->getTitle()
  191. )
  192. );
  193. }
  194. }
  195. /**
  196. * Get indexer ids that have common shared index
  197. *
  198. * @param string $sharedIndex
  199. * @return array
  200. */
  201. private function getIndexerIdsBySharedIndex($sharedIndex)
  202. {
  203. $indexers = $this->getConfig()->getIndexers();
  204. $result = [];
  205. foreach ($indexers as $indexerConfig) {
  206. if ($indexerConfig['shared_index'] == $sharedIndex) {
  207. $result[] = $indexerConfig['indexer_id'];
  208. }
  209. }
  210. return $result;
  211. }
  212. /**
  213. * Validate indexers by shared index ID
  214. *
  215. * @param string $sharedIndex
  216. * @return $this
  217. */
  218. private function validateSharedIndex($sharedIndex)
  219. {
  220. if (empty($sharedIndex)) {
  221. throw new \InvalidArgumentException(
  222. 'The sharedIndex is an invalid shared index identifier. Verify the identifier and try again.'
  223. );
  224. }
  225. $indexerIds = $this->getIndexerIdsBySharedIndex($sharedIndex);
  226. if (empty($indexerIds)) {
  227. return $this;
  228. }
  229. foreach ($indexerIds as $indexerId) {
  230. $indexer = $this->getIndexerRegistry()->get($indexerId);
  231. /** @var \Magento\Indexer\Model\Indexer\State $state */
  232. $state = $indexer->getState();
  233. $state->setStatus(StateInterface::STATUS_VALID);
  234. $state->save();
  235. }
  236. $this->sharedIndexesComplete[] = $sharedIndex;
  237. return $this;
  238. }
  239. /**
  240. * Get config
  241. *
  242. * @return ConfigInterface
  243. * @deprecated 100.1.0
  244. */
  245. private function getConfig()
  246. {
  247. if (!$this->config) {
  248. $this->config = $this->getObjectManager()->get(ConfigInterface::class);
  249. }
  250. return $this->config;
  251. }
  252. /**
  253. * Get indexer registry
  254. *
  255. * @return IndexerRegistry
  256. * @deprecated 100.2.0
  257. */
  258. private function getIndexerRegistry()
  259. {
  260. if (!$this->indexerRegistry) {
  261. $this->indexerRegistry = $this->getObjectManager()->get(IndexerRegistry::class);
  262. }
  263. return $this->indexerRegistry;
  264. }
  265. /**
  266. * Get dependency info provider
  267. *
  268. * @return DependencyInfoProvider
  269. * @deprecated 100.2.0
  270. */
  271. private function getDependencyInfoProvider()
  272. {
  273. if (!$this->dependencyInfoProvider) {
  274. $this->dependencyInfoProvider = $this->getObjectManager()->get(DependencyInfoProvider::class);
  275. }
  276. return $this->dependencyInfoProvider;
  277. }
  278. }