Skip to content

Commit

Permalink
Merge branch '2.4-develop' of https://github.com/mage-os/mirror-magento2
Browse files Browse the repository at this point in the history
 into 2.4-develop
  • Loading branch information
mage-os-ci committed Feb 16, 2024
2 parents 94442db + 3428ebc commit a2ee68f
Show file tree
Hide file tree
Showing 15 changed files with 1,063 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public function render(\Magento\Framework\DataObject $row)
$class = 'grid-severity-minor';
$text = __('Processing');
break;
case \Magento\Framework\Indexer\StateInterface::STATUS_SUSPENDED:
$class = 'grid-severity-minor';
$text = __('Suspended');
break;
}
return '<span class="' . $class . '"><span>' . $text . '</span></span>';
}
Expand Down
161 changes: 161 additions & 0 deletions app/code/Magento/Indexer/Console/Command/IndexerSetStatusCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Indexer\Console\Command;

use Magento\Framework\App\ObjectManagerFactory;
use Magento\Framework\Console\Cli;
use Magento\Framework\Exception\AlreadyExistsException;
use Magento\Framework\Indexer\IndexerInterface;
use Magento\Framework\Indexer\StateInterface;
use Magento\Indexer\Model\ResourceModel\Indexer\State;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Command for setting index status for indexers.
*/
class IndexerSetStatusCommand extends AbstractIndexerManageCommand
{
/**#@+
* Names of input arguments or options
*/
private const INPUT_KEY_STATUS = 'status';
/**#@- */

/**
* @var State
*/
private State $stateResourceModel;

/**
* @param State $stateResourceModel
* @param ObjectManagerFactory $objectManagerFactory
*/
public function __construct(
State $stateResourceModel,
ObjectManagerFactory $objectManagerFactory
) {
$this->stateResourceModel = $stateResourceModel;
parent::__construct($objectManagerFactory);
}

/**
* @inheritdoc
*/
protected function configure()
{
$this->setName('indexer:set-status')
->setDescription('Sets the specified indexer status')
->setDefinition($this->getInputList());

parent::configure();
}

/**
* @inheritdoc
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$errors = $this->validate($input);
if ($errors) {
throw new \InvalidArgumentException(implode("\n", $errors));
}

$newStatus = $input->getArgument(self::INPUT_KEY_STATUS);
$indexers = $this->getIndexers($input);
$returnValue = Cli::RETURN_SUCCESS;

foreach ($indexers as $indexer) {
try {
$this->updateIndexerStatus($indexer, $newStatus, $output);
} catch (\Exception $e) {
$output->writeln($e->getMessage());
$returnValue = Cli::RETURN_FAILURE;
}
}

return $returnValue;
}

/**
* Gets list of arguments for the command.
*
* @return InputOption[]
*/
public function getInputList(): array
{
$modeOptions[] = new InputArgument(
self::INPUT_KEY_STATUS,
InputArgument::REQUIRED,
'Indexer status type [' . StateInterface::STATUS_INVALID
. '|' . StateInterface::STATUS_SUSPENDED . '|' . StateInterface::STATUS_VALID . ']'
);

return array_merge($modeOptions, parent::getInputList());
}

/**
* Checks if all CLI command options are provided.
*
* @param InputInterface $input
* @return string[]
*/
private function validate(InputInterface $input): array
{
$errors = [];
$acceptedValues = [
StateInterface::STATUS_INVALID,
StateInterface::STATUS_SUSPENDED,
StateInterface::STATUS_VALID
];
$inputStatus = $input->getArgument(self::INPUT_KEY_STATUS);

if (!in_array($inputStatus, $acceptedValues, true)) {
$acceptedValuesString = '"' . implode('", "', $acceptedValues) . '"';
$errors[] = sprintf(
'Invalid status "%s". Accepted values are %s.',
$inputStatus,
$acceptedValuesString
);
}

return $errors;
}

/**
* Updates the status of a specified indexer.
*
* @param IndexerInterface $indexer
* @param string $newStatus
* @param OutputInterface $output
* @return void
* @throws AlreadyExistsException
*/
private function updateIndexerStatus(IndexerInterface $indexer, string $newStatus, OutputInterface $output): void
{
$state = $indexer->getState();
$previousStatus = $state->getStatus();
$this->stateResourceModel->save($state->setStatus($newStatus));
$currentStatus = $state->getStatus();

if ($previousStatus !== $currentStatus) {
$output->writeln(
sprintf(
"Index status for Indexer '%s' was changed from '%s' to '%s'.",
$indexer->getTitle(),
$previousStatus,
$currentStatus
)
);
} else {
$output->writeln(sprintf("Index status for Indexer '%s' has not been changed.", $indexer->getTitle()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ private function getStatus(Indexer\IndexerInterface $indexer)
case \Magento\Framework\Indexer\StateInterface::STATUS_WORKING:
$status = 'Processing';
break;
case \Magento\Framework\Indexer\StateInterface::STATUS_SUSPENDED:
$status = 'Suspended';
break;
}
return $status;
}
Expand Down
14 changes: 13 additions & 1 deletion app/code/Magento/Indexer/Model/Indexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace Magento\Indexer\Model;

use Magento\Framework\DataObject;
use Magento\Framework\Indexer\ActionFactory;
use Magento\Framework\Indexer\ActionInterface;
use Magento\Framework\Indexer\ConfigInterface;
Expand All @@ -14,13 +15,14 @@
use Magento\Framework\Indexer\StateInterface;
use Magento\Framework\Indexer\StructureFactory;
use Magento\Framework\Indexer\IndexerInterfaceFactory;
use Magento\Framework\Indexer\SuspendableIndexerInterface;

/**
* Indexer model.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Indexer extends \Magento\Framework\DataObject implements IndexerInterface
class Indexer extends DataObject implements IndexerInterface, SuspendableIndexerInterface
{
/**
* @var string
Expand Down Expand Up @@ -332,6 +334,16 @@ public function isInvalid()
return $this->getState()->getStatus() == StateInterface::STATUS_INVALID;
}

/**
* Checks whether indexer is suspended.
*
* @return bool
*/
public function isSuspended(): bool
{
return $this->getState()->getStatus() === StateInterface::STATUS_SUSPENDED;
}

/**
* Check whether indexer is working
*
Expand Down
48 changes: 46 additions & 2 deletions app/code/Magento/Indexer/Model/Processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

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\IndexerRegistry;
use Magento\Framework\Indexer\StateInterface;
use Magento\Framework\Mview\ProcessorInterface;
use Magento\Indexer\Model\Processor\MakeSharedIndexValid;

Expand Down Expand Up @@ -47,25 +51,33 @@ class Processor
*/
protected $makeSharedValid;

/**
* @var IndexerRegistry
*/
private IndexerRegistry $indexerRegistry;

/**
* @param ConfigInterface $config
* @param IndexerInterfaceFactory $indexerFactory
* @param Indexer\CollectionFactory $indexersFactory
* @param ProcessorInterface $mviewProcessor
* @param MakeSharedIndexValid|null $makeSharedValid
* @param IndexerRegistry|null $indexerRegistry
*/
public function __construct(
ConfigInterface $config,
IndexerInterfaceFactory $indexerFactory,
Indexer\CollectionFactory $indexersFactory,
ProcessorInterface $mviewProcessor,
?MakeSharedIndexValid $makeSharedValid = null
?MakeSharedIndexValid $makeSharedValid = null,
?IndexerRegistry $indexerRegistry = null
) {
$this->config = $config;
$this->indexerFactory = $indexerFactory;
$this->indexersFactory = $indexersFactory;
$this->mviewProcessor = $mviewProcessor;
$this->makeSharedValid = $makeSharedValid ?: ObjectManager::getInstance()->get(MakeSharedIndexValid::class);
$this->indexerRegistry = $indexerRegistry ?: ObjectManager::getInstance()->get(IndexerRegistry::class);
}

/**
Expand All @@ -81,7 +93,9 @@ public function reindexAllInvalid()
$indexer->load($indexerId);
$indexerConfig = $this->config->getIndexer($indexerId);

if ($indexer->isInvalid()) {
if ($indexer->isInvalid() && !$indexer->isSuspended()
&& !$this->isSharedIndexSuspended($indexerConfig['shared_index'])
) {
// Skip indexers having shared index that was already complete
$sharedIndex = $indexerConfig['shared_index'] ?? null;
if (!in_array($sharedIndex, $this->sharedIndexesComplete)) {
Expand All @@ -97,6 +111,36 @@ public function reindexAllInvalid()
}
}

/**
* Checks if any indexers within a group that share a common 'shared_index' ID are suspended.
*
* @param string|null $sharedIndexId
* @return bool
*/
private function isSharedIndexSuspended(?string $sharedIndexId): bool
{
if ($sharedIndexId === null) {
return false;
}

$indexers = $this->config->getIndexers();

foreach ($indexers as $indexerId => $config) {
// Check if the indexer shares the same 'shared_index'
if (isset($config['shared_index']) && $config['shared_index'] === $sharedIndexId) {
$indexer = $this->indexerRegistry->get($indexerId);

// If any indexer that shares the 'shared_index' is suspended, return true
if ($indexer->getStatus() === StateInterface::STATUS_SUSPENDED) {
return true;
}
}
}

// If none of the shared indexers are suspended, return false
return false;
}

/**
* Regenerate indexes for all indexers
*
Expand Down
10 changes: 9 additions & 1 deletion app/code/Magento/Indexer/Model/ResourceModel/Indexer/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,16 @@ protected function prepareDataForUpdate($object)
$data = parent::prepareDataForUpdate($object);

if (isset($data['status']) && StateInterface::STATUS_VALID === $data['status']) {
$condition = $this->getConnection()->quoteInto(
'status IN (?)',
[
StateInterface::STATUS_WORKING,
StateInterface::STATUS_SUSPENDED,
StateInterface::STATUS_INVALID
]
);
$data['status'] = $this->getConnection()->getCheckSql(
$this->getConnection()->quoteInto('status = ?', StateInterface::STATUS_WORKING),
$condition,
$this->getConnection()->quote($data['status']),
'status'
);
Expand Down
Loading

0 comments on commit a2ee68f

Please sign in to comment.