Skip to content

Commit 3fef6d1

Browse files
[performance] MCP-88: Refactor indexer blocking algorithm (#6513)
* MCP-88: Refactor indexer blocking algorithm
1 parent ea57677 commit 3fef6d1

File tree

5 files changed

+321
-25
lines changed

5 files changed

+321
-25
lines changed

app/code/Magento/Indexer/Model/Indexer/State.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,59 @@ class State extends \Magento\Framework\Model\AbstractModel implements StateInter
2323
*/
2424
protected $_eventObject = 'indexer_state';
2525

26+
/**
27+
* @var \Magento\Framework\Lock\LockManagerInterface
28+
*/
29+
private $lockManager;
30+
31+
/**
32+
* Prefix for lock mechanism
33+
*
34+
* @var string
35+
*/
36+
private $lockPrefix = 'INDEXER';
37+
38+
/**
39+
* DeploymentConfig
40+
*
41+
* @var \Magento\Framework\App\DeploymentConfig
42+
*/
43+
private $configReader;
44+
45+
/**
46+
* Parameter with path to indexer use_application_lock config
47+
*
48+
* @var string
49+
*/
50+
private $useApplicationLockConfig = 'indexer/use_application_lock';
51+
2652
/**
2753
* @param \Magento\Framework\Model\Context $context
2854
* @param \Magento\Framework\Registry $registry
2955
* @param \Magento\Indexer\Model\ResourceModel\Indexer\State $resource
3056
* @param \Magento\Indexer\Model\ResourceModel\Indexer\State\Collection $resourceCollection
3157
* @param array $data
58+
* @param \Magento\Framework\Lock\LockManagerInterface $lockManager
59+
* @param \Magento\Framework\App\DeploymentConfig $configReader
3260
*/
3361
public function __construct(
3462
\Magento\Framework\Model\Context $context,
3563
\Magento\Framework\Registry $registry,
3664
\Magento\Indexer\Model\ResourceModel\Indexer\State $resource,
3765
\Magento\Indexer\Model\ResourceModel\Indexer\State\Collection $resourceCollection,
38-
array $data = []
66+
array $data = [],
67+
\Magento\Framework\Lock\LockManagerInterface $lockManager = null,
68+
\Magento\Framework\App\DeploymentConfig $configReader = null
3969
) {
4070
if (!isset($data['status'])) {
4171
$data['status'] = self::STATUS_INVALID;
4272
}
73+
$this->lockManager = $lockManager ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
74+
\Magento\Framework\Lock\LockManagerInterface::class
75+
);
76+
$this->configReader = $configReader ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
77+
\Magento\Framework\App\DeploymentConfig::class
78+
);
4379
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
4480
}
4581

@@ -71,6 +107,15 @@ public function setIndexerId($value)
71107
*/
72108
public function getStatus()
73109
{
110+
if ($this->isUseApplicationLock()) {
111+
if (
112+
parent::getStatus() == StateInterface::STATUS_WORKING &&
113+
!$this->lockManager->isLocked($this->lockPrefix . $this->getIndexerId())
114+
) {
115+
return StateInterface::STATUS_INVALID;
116+
}
117+
}
118+
74119
return parent::getStatus();
75120
}
76121

@@ -118,6 +163,13 @@ public function loadByIndexer($indexerId)
118163
*/
119164
public function setStatus($status)
120165
{
166+
if ($this->isUseApplicationLock()) {
167+
if ($status == StateInterface::STATUS_WORKING) {
168+
$this->lockManager->lock($this->lockPrefix . $this->getIndexerId());
169+
} else {
170+
$this->lockManager->unlock($this->lockPrefix . $this->getIndexerId());
171+
}
172+
}
121173
return parent::setStatus($status);
122174
}
123175

@@ -131,4 +183,14 @@ public function beforeSave()
131183
$this->setUpdated(time());
132184
return parent::beforeSave();
133185
}
186+
187+
/**
188+
* The indexer application locking mechanism is used
189+
*
190+
* @return bool
191+
*/
192+
private function isUseApplicationLock()
193+
{
194+
return $this->configReader->get($this->useApplicationLockConfig) ?: false;
195+
}
134196
}

app/code/Magento/Indexer/Model/Mview/View/State.php

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,62 @@ class State extends \Magento\Framework\Model\AbstractModel implements \Magento\F
2424
*/
2525
protected $_eventObject = 'mview_state';
2626

27+
/**
28+
* @var \Magento\Framework\Lock\LockManagerInterface
29+
*/
30+
private $lockManager;
31+
32+
/**
33+
* Prefix for lock mechanism
34+
*
35+
* @var string
36+
*/
37+
private $lockPrefix = 'MVIEW';
38+
39+
/**
40+
* DeploymentConfig
41+
*
42+
* @var \Magento\Framework\App\DeploymentConfig
43+
*/
44+
private $configReader;
45+
46+
/**
47+
* Parameter with path to indexer use_application_lock config
48+
*
49+
* @var string
50+
*/
51+
private $useApplicationLockConfig = 'indexer/use_application_lock';
52+
2753
/**
2854
* @param \Magento\Framework\Model\Context $context
2955
* @param \Magento\Framework\Registry $registry
3056
* @param \Magento\Indexer\Model\ResourceModel\Mview\View\State $resource
3157
* @param \Magento\Indexer\Model\ResourceModel\Mview\View\State\Collection $resourceCollection
3258
* @param array $data
59+
* @param \Magento\Framework\Lock\LockManagerInterface $lockManager
60+
* @param \Magento\Framework\App\DeploymentConfig $configReader
3361
*/
3462
public function __construct(
3563
\Magento\Framework\Model\Context $context,
3664
\Magento\Framework\Registry $registry,
3765
\Magento\Indexer\Model\ResourceModel\Mview\View\State $resource,
3866
\Magento\Indexer\Model\ResourceModel\Mview\View\State\Collection $resourceCollection,
39-
array $data = []
67+
array $data = [],
68+
\Magento\Framework\Lock\LockManagerInterface $lockManager = null,
69+
\Magento\Framework\App\DeploymentConfig $configReader = null
4070
) {
4171
if (!isset($data['mode'])) {
4272
$data['mode'] = self::MODE_DISABLED;
4373
}
4474
if (!isset($data['status'])) {
4575
$data['status'] = self::STATUS_IDLE;
4676
}
77+
$this->lockManager = $lockManager ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
78+
\Magento\Framework\Lock\LockManagerInterface::class
79+
);
80+
$this->configReader = $configReader ?: \Magento\Framework\App\ObjectManager::getInstance()->get(
81+
\Magento\Framework\App\DeploymentConfig::class
82+
);
4783
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
4884
}
4985

@@ -112,7 +148,17 @@ public function setMode($mode)
112148
*/
113149
public function getStatus()
114150
{
115-
return $this->getData('status');
151+
$status = $this->getData('status');
152+
if ($this->isUseApplicationLock()) {
153+
if (
154+
$status == \Magento\Framework\Mview\View\StateInterface::STATUS_WORKING &&
155+
!$this->lockManager->isLocked($this->lockPrefix . $this->getViewId())
156+
) {
157+
return \Magento\Framework\Mview\View\StateInterface::STATUS_IDLE;
158+
}
159+
}
160+
161+
return $status;
116162
}
117163

118164
/**
@@ -123,6 +169,13 @@ public function getStatus()
123169
*/
124170
public function setStatus($status)
125171
{
172+
if ($this->isUseApplicationLock()) {
173+
if ($status == \Magento\Framework\Mview\View\StateInterface::STATUS_WORKING) {
174+
$this->lockManager->lock($this->lockPrefix . $this->getViewId());
175+
} else {
176+
$this->lockManager->unlock($this->lockPrefix . $this->getViewId());
177+
}
178+
}
126179
$this->setData('status', $status);
127180
return $this;
128181
}
@@ -170,4 +223,14 @@ public function setVersionId($versionId)
170223
$this->setData('version_id', $versionId);
171224
return $this;
172225
}
226+
227+
/**
228+
* The indexer application locking mechanism is used
229+
*
230+
* @return bool
231+
*/
232+
private function isUseApplicationLock()
233+
{
234+
return $this->configReader->get($this->useApplicationLockConfig) ?: false;
235+
}
173236
}

app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,7 @@ private function attachViewToIndexerMock($indexerMock, array $data)
4141
->willReturn(range(0, $data['view']['changelog']['list_size']-1));
4242

4343
/** @var State|MockObject $stateMock */
44-
$stateMock = $this->getMockBuilder(State::class)
45-
->disableOriginalConstructor()
46-
->setMethods(null)
47-
->getMock();
44+
$stateMock = $this->getStateMock();
4845

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

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

67+
/**
68+
* @return State
69+
*/
70+
private function getStateMock()
71+
{
72+
$contextMock = $this->createPartialMock(\Magento\Framework\Model\Context::class, ['getEventDispatcher']);
73+
$eventManagerMock = $this->getMockForAbstractClass(\Magento\Framework\Event\ManagerInterface::class);
74+
$contextMock->expects($this->any())->method('getEventDispatcher')->willReturn($eventManagerMock);
75+
$registryMock = $this->createMock(\Magento\Framework\Registry::class);
76+
$resourceMock = $this->createMock(\Magento\Indexer\Model\ResourceModel\Mview\View\State::class);
77+
$resourceCollectionMock = $this->createMock(
78+
\Magento\Indexer\Model\ResourceModel\Mview\View\State\Collection::class
79+
);
80+
$lockManagerMock = $this->createMock(\Magento\Framework\Lock\LockManagerInterface::class);
81+
$configReaderMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class);
82+
83+
return new State(
84+
$contextMock,
85+
$registryMock,
86+
$resourceMock,
87+
$resourceCollectionMock,
88+
[],
89+
$lockManagerMock,
90+
$configReaderMock
91+
);
92+
}
93+
7094
/**
7195
* @param array $indexers
7296
*

app/code/Magento/Indexer/Test/Unit/Model/Indexer/StateTest.php

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ class StateTest extends TestCase
4242
*/
4343
protected $_resourceCollectionMock;
4444

45+
/**
46+
* @var \Magento\Framework\Lock\LockManagerInterface|MockObject
47+
*/
48+
protected $lockManagerMock;
49+
50+
/**
51+
* @var \Magento\Framework\App\DeploymentConfig|MockObject
52+
*/
53+
protected $configReaderMock;
54+
4555
protected function setUp(): void
4656
{
4757
$this->_contextMock = $this->createPartialMock(Context::class, ['getEventDispatcher']);
@@ -52,12 +62,17 @@ protected function setUp(): void
5262
$this->_resourceCollectionMock = $this->createMock(
5363
Collection::class
5464
);
65+
$this->lockManagerMock = $this->createMock(\Magento\Framework\Lock\LockManagerInterface::class);
66+
$this->configReaderMock = $this->createMock(\Magento\Framework\App\DeploymentConfig::class);
5567

5668
$this->model = new State(
5769
$this->_contextMock,
5870
$this->_registryMock,
5971
$this->_resourceMock,
60-
$this->_resourceCollectionMock
72+
$this->_resourceCollectionMock,
73+
[],
74+
$this->lockManagerMock,
75+
$this->configReaderMock
6176
);
6277
}
6378

@@ -82,4 +97,67 @@ public function testSetStatus()
8297
$this->model->setStatus($setData);
8398
$this->assertEquals($setData, $this->model->getStatus());
8499
}
100+
101+
public function testSetterAndGetterWithoutApplicationLock()
102+
{
103+
$this->configReaderMock->expects($this->any())->method('get')->willReturn(false);
104+
105+
$this->lockManagerMock->expects($this->any())->method('isLocked')->willReturn(false);
106+
107+
$status = \Magento\Framework\Indexer\StateInterface::STATUS_WORKING;
108+
$this->model->setStatus($status);
109+
$this->assertEquals($status, $this->model->getStatus());
110+
111+
$date = time();
112+
$this->model->setUpdated($date);
113+
$this->assertEquals($date, $this->model->getUpdated());
114+
}
115+
116+
/**
117+
* @return array
118+
*/
119+
public function executeProvider()
120+
{
121+
return [
122+
[
123+
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_WORKING,
124+
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_WORKING,
125+
'lock' => 'lock',
126+
'isLocked' => true
127+
],
128+
[
129+
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_WORKING,
130+
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_INVALID,
131+
'lock' => 'lock',
132+
'isLocked' => false
133+
],
134+
[
135+
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_INVALID,
136+
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_INVALID,
137+
'lock' => 'unlock',
138+
'isLocked' => false
139+
],
140+
[
141+
'setStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_VALID,
142+
'getStatus' => \Magento\Framework\Indexer\StateInterface::STATUS_VALID,
143+
'lock' => 'unlock',
144+
'isLocked' => false
145+
]
146+
];
147+
}
148+
149+
/**
150+
* @param string $setStatus
151+
* @param string $getStatus
152+
* @param bool $isLocked
153+
* @dataProvider executeProvider
154+
*/
155+
public function testSetterAndGetterWithApplicationLock($setStatus, $getStatus, $lock, $isLocked)
156+
{
157+
$this->configReaderMock->expects($this->any())->method('get')->willReturn(true);
158+
$this->lockManagerMock->expects($this->any())->method('isLocked')->willReturn($isLocked);
159+
$this->lockManagerMock->expects($this->once())->method($lock);
160+
$this->model->setStatus($setStatus);
161+
$this->assertEquals($getStatus, $this->model->getStatus());
162+
}
85163
}

0 commit comments

Comments
 (0)