Skip to content

Commit

Permalink
[performance] MCP-88: Refactor indexer blocking algorithm (#6513)
Browse files Browse the repository at this point in the history
* MCP-88: Refactor indexer blocking algorithm
  • Loading branch information
magento-mts-svc authored Jan 14, 2021
1 parent ea57677 commit 3fef6d1
Show file tree
Hide file tree
Showing 5 changed files with 321 additions and 25 deletions.
64 changes: 63 additions & 1 deletion app/code/Magento/Indexer/Model/Indexer/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,59 @@ class State extends \Magento\Framework\Model\AbstractModel implements StateInter
*/
protected $_eventObject = 'indexer_state';

/**
* @var \Magento\Framework\Lock\LockManagerInterface
*/
private $lockManager;

/**
* Prefix for lock mechanism
*
* @var string
*/
private $lockPrefix = 'INDEXER';

/**
* DeploymentConfig
*
* @var \Magento\Framework\App\DeploymentConfig
*/
private $configReader;

/**
* Parameter with path to indexer use_application_lock config
*
* @var string
*/
private $useApplicationLockConfig = 'indexer/use_application_lock';

/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Indexer\Model\ResourceModel\Indexer\State $resource
* @param \Magento\Indexer\Model\ResourceModel\Indexer\State\Collection $resourceCollection
* @param array $data
* @param \Magento\Framework\Lock\LockManagerInterface $lockManager
* @param \Magento\Framework\App\DeploymentConfig $configReader
*/
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Indexer\Model\ResourceModel\Indexer\State $resource,
\Magento\Indexer\Model\ResourceModel\Indexer\State\Collection $resourceCollection,
array $data = []
array $data = [],
\Magento\Framework\Lock\LockManagerInterface $lockManager = null,
\Magento\Framework\App\DeploymentConfig $configReader = null
) {
if (!isset($data['status'])) {
$data['status'] = self::STATUS_INVALID;
}
$this->lockManager = $lockManager ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
\Magento\Framework\Lock\LockManagerInterface::class
);
$this->configReader = $configReader ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
\Magento\Framework\App\DeploymentConfig::class
);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}

Expand Down Expand Up @@ -71,6 +107,15 @@ public function setIndexerId($value)
*/
public function getStatus()
{
if ($this->isUseApplicationLock()) {
if (
parent::getStatus() == StateInterface::STATUS_WORKING &&
!$this->lockManager->isLocked($this->lockPrefix . $this->getIndexerId())
) {
return StateInterface::STATUS_INVALID;
}
}

return parent::getStatus();
}

Expand Down Expand Up @@ -118,6 +163,13 @@ public function loadByIndexer($indexerId)
*/
public function setStatus($status)
{
if ($this->isUseApplicationLock()) {
if ($status == StateInterface::STATUS_WORKING) {
$this->lockManager->lock($this->lockPrefix . $this->getIndexerId());
} else {
$this->lockManager->unlock($this->lockPrefix . $this->getIndexerId());
}
}
return parent::setStatus($status);
}

Expand All @@ -131,4 +183,14 @@ public function beforeSave()
$this->setUpdated(time());
return parent::beforeSave();
}

/**
* The indexer application locking mechanism is used
*
* @return bool
*/
private function isUseApplicationLock()
{
return $this->configReader->get($this->useApplicationLockConfig) ?: false;
}
}
67 changes: 65 additions & 2 deletions app/code/Magento/Indexer/Model/Mview/View/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,62 @@ class State extends \Magento\Framework\Model\AbstractModel implements \Magento\F
*/
protected $_eventObject = 'mview_state';

/**
* @var \Magento\Framework\Lock\LockManagerInterface
*/
private $lockManager;

/**
* Prefix for lock mechanism
*
* @var string
*/
private $lockPrefix = 'MVIEW';

/**
* DeploymentConfig
*
* @var \Magento\Framework\App\DeploymentConfig
*/
private $configReader;

/**
* Parameter with path to indexer use_application_lock config
*
* @var string
*/
private $useApplicationLockConfig = 'indexer/use_application_lock';

/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Indexer\Model\ResourceModel\Mview\View\State $resource
* @param \Magento\Indexer\Model\ResourceModel\Mview\View\State\Collection $resourceCollection
* @param array $data
* @param \Magento\Framework\Lock\LockManagerInterface $lockManager
* @param \Magento\Framework\App\DeploymentConfig $configReader
*/
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Indexer\Model\ResourceModel\Mview\View\State $resource,
\Magento\Indexer\Model\ResourceModel\Mview\View\State\Collection $resourceCollection,
array $data = []
array $data = [],
\Magento\Framework\Lock\LockManagerInterface $lockManager = null,
\Magento\Framework\App\DeploymentConfig $configReader = null
) {
if (!isset($data['mode'])) {
$data['mode'] = self::MODE_DISABLED;
}
if (!isset($data['status'])) {
$data['status'] = self::STATUS_IDLE;
}
$this->lockManager = $lockManager ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
\Magento\Framework\Lock\LockManagerInterface::class
);
$this->configReader = $configReader ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
\Magento\Framework\App\DeploymentConfig::class
);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}

Expand Down Expand Up @@ -112,7 +148,17 @@ public function setMode($mode)
*/
public function getStatus()
{
return $this->getData('status');
$status = $this->getData('status');
if ($this->isUseApplicationLock()) {
if (
$status == \Magento\Framework\Mview\View\StateInterface::STATUS_WORKING &&
!$this->lockManager->isLocked($this->lockPrefix . $this->getViewId())
) {
return \Magento\Framework\Mview\View\StateInterface::STATUS_IDLE;
}
}

return $status;
}

/**
Expand All @@ -123,6 +169,13 @@ public function getStatus()
*/
public function setStatus($status)
{
if ($this->isUseApplicationLock()) {
if ($status == \Magento\Framework\Mview\View\StateInterface::STATUS_WORKING) {
$this->lockManager->lock($this->lockPrefix . $this->getViewId());
} else {
$this->lockManager->unlock($this->lockPrefix . $this->getViewId());
}
}
$this->setData('status', $status);
return $this;
}
Expand Down Expand Up @@ -170,4 +223,14 @@ public function setVersionId($versionId)
$this->setData('version_id', $versionId);
return $this;
}

/**
* The indexer application locking mechanism is used
*
* @return bool
*/
private function isUseApplicationLock()
{
return $this->configReader->get($this->useApplicationLockConfig) ?: false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ private function attachViewToIndexerMock($indexerMock, array $data)
->willReturn(range(0, $data['view']['changelog']['list_size']-1));

/** @var State|MockObject $stateMock */
$stateMock = $this->getMockBuilder(State::class)
->disableOriginalConstructor()
->setMethods(null)
->getMock();
$stateMock = $this->getStateMock();

$stateMock->addData($data['view']['state']);

Expand All @@ -67,6 +64,33 @@ private function attachViewToIndexerMock($indexerMock, array $data)
return $indexerMock;
}

/**
* @return State
*/
private function getStateMock()
{
$contextMock = $this->createPartialMock(\Magento\Framework\Model\Context::class, ['getEventDispatcher']);
$eventManagerMock = $this->getMockForAbstractClass(\Magento\Framework\Event\ManagerInterface::class);
$contextMock->expects($this->any())->method('getEventDispatcher')->willReturn($eventManagerMock);
$registryMock = $this->createMock(\Magento\Framework\Registry::class);
$resourceMock = $this->createMock(\Magento\Indexer\Model\ResourceModel\Mview\View\State::class);
$resourceCollectionMock = $this->createMock(
\Magento\Indexer\Model\ResourceModel\Mview\View\State\Collection::class
);
$lockManagerMock = $this->createMock(\Magento\Framework\Lock\LockManagerInterface::class);
$configReaderMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class);

return new State(
$contextMock,
$registryMock,
$resourceMock,
$resourceCollectionMock,
[],
$lockManagerMock,
$configReaderMock
);
}

/**
* @param array $indexers
*
Expand Down
80 changes: 79 additions & 1 deletion app/code/Magento/Indexer/Test/Unit/Model/Indexer/StateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ class StateTest extends TestCase
*/
protected $_resourceCollectionMock;

/**
* @var \Magento\Framework\Lock\LockManagerInterface|MockObject
*/
protected $lockManagerMock;

/**
* @var \Magento\Framework\App\DeploymentConfig|MockObject
*/
protected $configReaderMock;

protected function setUp(): void
{
$this->_contextMock = $this->createPartialMock(Context::class, ['getEventDispatcher']);
Expand All @@ -52,12 +62,17 @@ protected function setUp(): void
$this->_resourceCollectionMock = $this->createMock(
Collection::class
);
$this->lockManagerMock = $this->createMock(\Magento\Framework\Lock\LockManagerInterface::class);
$this->configReaderMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class);

$this->model = new State(
$this->_contextMock,
$this->_registryMock,
$this->_resourceMock,
$this->_resourceCollectionMock
$this->_resourceCollectionMock,
[],
$this->lockManagerMock,
$this->configReaderMock
);
}

Expand All @@ -82,4 +97,67 @@ public function testSetStatus()
$this->model->setStatus($setData);
$this->assertEquals($setData, $this->model->getStatus());
}

public function testSetterAndGetterWithoutApplicationLock()
{
$this->configReaderMock->expects($this->any())->method('get')->willReturn(false);

$this->lockManagerMock->expects($this->any())->method('isLocked')->willReturn(false);

$status = \Magento\Framework\Indexer\StateInterface::STATUS_WORKING;
$this->model->setStatus($status);
$this->assertEquals($status, $this->model->getStatus());

$date = time();
$this->model->setUpdated($date);
$this->assertEquals($date, $this->model->getUpdated());
}

/**
* @return array
*/
public function executeProvider()
{
return [
[
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_WORKING,
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_WORKING,
'lock' => 'lock',
'isLocked' => true
],
[
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_WORKING,
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_INVALID,
'lock' => 'lock',
'isLocked' => false
],
[
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_INVALID,
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_INVALID,
'lock' => 'unlock',
'isLocked' => false
],
[
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_VALID,
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_VALID,
'lock' => 'unlock',
'isLocked' => false
]
];
}

/**
* @param string $setStatus
* @param string $getStatus
* @param bool $isLocked
* @dataProvider executeProvider
*/
public function testSetterAndGetterWithApplicationLock($setStatus, $getStatus, $lock, $isLocked)
{
$this->configReaderMock->expects($this->any())->method('get')->willReturn(true);
$this->lockManagerMock->expects($this->any())->method('isLocked')->willReturn($isLocked);
$this->lockManagerMock->expects($this->once())->method($lock);
$this->model->setStatus($setStatus);
$this->assertEquals($getStatus, $this->model->getStatus());
}
}
Loading

0 comments on commit 3fef6d1

Please sign in to comment.