Skip to content

Commit 3915c51

Browse files
committed
ACP2E-807: DELETE query executed in the product attribute indexing causes performance issues
1 parent 5d9fe40 commit 3915c51

File tree

3 files changed

+206
-13
lines changed

3 files changed

+206
-13
lines changed

app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ protected function syncData($indexer, $destinationTable, $ids)
142142
$connection->beginTransaction();
143143
try {
144144
// remove old index
145-
$where = $connection->quoteInto('entity_id IN(?)', $ids);
145+
$where = $connection->quoteInto('entity_id IN (?)', $ids, 'INT');
146146
$connection->delete($destinationTable, $where);
147147
// insert new index
148148
$indexer->insertFromTable($indexer->getIdxTable(), $destinationTable);

app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/RowTest.php

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,122 @@
88
namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Eav\Action;
99

1010
use Magento\Catalog\Model\Indexer\Product\Eav\Action\Row;
11-
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
11+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Decimal;
12+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory;
13+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source;
14+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory;
15+
use Magento\Framework\App\Config\ScopeConfigInterface;
16+
use Magento\Framework\DB\Adapter\AdapterInterface;
17+
use Magento\Framework\Exception\InputException;
18+
use Magento\Store\Model\ScopeInterface;
19+
use PHPUnit\Framework\MockObject\MockObject;
1220
use PHPUnit\Framework\TestCase;
1321

1422
class RowTest extends TestCase
1523
{
24+
/**
25+
* @var DecimalFactory|MockObject
26+
*/
27+
private $eavDecimalFactoryMock;
28+
29+
/**
30+
* @var SourceFactory|MockObject
31+
*/
32+
private $eavSourceFactoryMock;
33+
34+
/**
35+
* @var ScopeConfigInterface|MockObject
36+
*/
37+
private $scopeConfigMock;
38+
1639
/**
1740
* @var Row
1841
*/
19-
protected $_model;
42+
private $model;
2043

2144
protected function setUp(): void
2245
{
23-
$objectManager = new ObjectManager($this);
24-
$this->_model = $objectManager->getObject(Row::class);
46+
$this->eavDecimalFactoryMock = $this->createMock(DecimalFactory::class);
47+
$this->eavSourceFactoryMock = $this->createMock(SourceFactory::class);
48+
$this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
49+
$this->model = new Row(
50+
$this->eavDecimalFactoryMock,
51+
$this->eavSourceFactoryMock,
52+
$this->scopeConfigMock
53+
);
2554
}
2655

2756
public function testEmptyId()
2857
{
29-
$this->expectException('Magento\Framework\Exception\InputException');
58+
$this->expectException(InputException::class);
3059
$this->expectExceptionMessage('We can\'t rebuild the index for an undefined product.');
31-
$this->_model->execute(null);
60+
$this->model->execute(null);
61+
}
62+
63+
public function testExecute(): void
64+
{
65+
$this->scopeConfigMock->expects($this->once())
66+
->method('getValue')
67+
->with(Row::ENABLE_EAV_INDEXER, ScopeInterface::SCOPE_STORE)
68+
->willReturn(true);
69+
70+
$eavDecimalMock = $this->createMock(Decimal::class);
71+
$this->eavDecimalFactoryMock->expects($this->once())
72+
->method('create')
73+
->willReturn($eavDecimalMock);
74+
$eavSourceMock = $this->createMock(Source::class);
75+
$this->eavSourceFactoryMock->expects($this->once())
76+
->method('create')
77+
->willReturn($eavSourceMock);
78+
79+
foreach ([$eavDecimalMock, $eavSourceMock] as $indexerMock) {
80+
$indexerMock->expects($this->atLeastOnce())
81+
->method('getRelationsByChild')
82+
->with([15])
83+
->willReturn([]);
84+
$indexerMock->expects($this->atLeastOnce())
85+
->method('getRelationsByParent')
86+
->with([15])
87+
->willReturn([]);
88+
$indexerMock->expects($this->once())
89+
->method('reindexEntities')
90+
->with([15])
91+
->willReturnSelf();
92+
$mainTable = 'main_table_name';
93+
$indexerMock->expects($this->atLeastOnce())
94+
->method('getMainTable')
95+
->willReturn($mainTable);
96+
97+
$connectionMock = $this->createMock(AdapterInterface::class);
98+
$indexerMock->expects($this->atLeastOnce())
99+
->method('getConnection')
100+
->willReturn($connectionMock);
101+
$connectionMock->expects($this->once())
102+
->method('beginTransaction')
103+
->willReturnSelf();
104+
$connectionMock->expects($this->once())
105+
->method('quoteInto')
106+
->with('entity_id IN (?)', [15], 'INT')
107+
->willReturn('entity_id IN (15)');
108+
$connectionMock->expects($this->once())
109+
->method('delete')
110+
->with($mainTable, 'entity_id IN (15)')
111+
->willReturn(3);
112+
$idxTable = 'idx_table_name';
113+
$indexerMock->expects($this->atLeastOnce())
114+
->method('getIdxTable')
115+
->with()
116+
->willReturn($idxTable);
117+
$indexerMock->expects($this->once())
118+
->method('insertFromTable')
119+
->with($idxTable, $mainTable)
120+
->willReturnSelf();
121+
$connectionMock->expects($this->once())
122+
->method('commit')
123+
->willReturnSelf();
124+
}
125+
126+
$id = 15;
127+
$this->model->execute($id);
32128
}
33129
}

app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/RowsTest.php

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,124 @@
77

88
namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Eav\Action;
99

10+
use Magento\Catalog\Model\Indexer\Product\Eav\Action\Row;
1011
use Magento\Catalog\Model\Indexer\Product\Eav\Action\Rows;
11-
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
12+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Decimal;
13+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory;
14+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source;
15+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory;
16+
use Magento\Framework\App\Config\ScopeConfigInterface;
17+
use Magento\Framework\DB\Adapter\AdapterInterface;
18+
use Magento\Framework\Exception\InputException;
19+
use Magento\Store\Model\ScopeInterface;
20+
use PHPUnit\Framework\MockObject\MockObject;
1221
use PHPUnit\Framework\TestCase;
1322

1423
class RowsTest extends TestCase
1524
{
25+
/**
26+
* @var DecimalFactory|MockObject
27+
*/
28+
private $eavDecimalFactoryMock;
29+
30+
/**
31+
* @var SourceFactory|MockObject
32+
*/
33+
private $eavSourceFactoryMock;
34+
35+
/**
36+
* @var ScopeConfigInterface|MockObject
37+
*/
38+
private $scopeConfigMock;
39+
1640
/**
1741
* @var Rows
1842
*/
19-
protected $_model;
43+
private $model;
2044

2145
protected function setUp(): void
2246
{
23-
$objectManager = new ObjectManager($this);
24-
$this->_model = $objectManager->getObject(Rows::class);
47+
$this->eavDecimalFactoryMock = $this->createMock(DecimalFactory::class);
48+
$this->eavSourceFactoryMock = $this->createMock(SourceFactory::class);
49+
$this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
50+
$this->model = new Rows(
51+
$this->eavDecimalFactoryMock,
52+
$this->eavSourceFactoryMock,
53+
$this->scopeConfigMock
54+
);
2555
}
2656

2757
public function testEmptyIds()
2858
{
29-
$this->expectException('Magento\Framework\Exception\InputException');
59+
$this->expectException(InputException::class);
3060
$this->expectExceptionMessage('Bad value was supplied.');
31-
$this->_model->execute(null);
61+
$this->model->execute(null);
62+
}
63+
64+
public function testExecute(): void
65+
{
66+
$this->scopeConfigMock->expects($this->once())
67+
->method('getValue')
68+
->with(Row::ENABLE_EAV_INDEXER, ScopeInterface::SCOPE_STORE)
69+
->willReturn(true);
70+
71+
$eavDecimalMock = $this->createMock(Decimal::class);
72+
$this->eavDecimalFactoryMock->expects($this->once())
73+
->method('create')
74+
->willReturn($eavDecimalMock);
75+
$eavSourceMock = $this->createMock(Source::class);
76+
$this->eavSourceFactoryMock->expects($this->once())
77+
->method('create')
78+
->willReturn($eavSourceMock);
79+
80+
foreach ([$eavDecimalMock, $eavSourceMock] as $indexerMock) {
81+
$indexerMock->expects($this->atLeastOnce())
82+
->method('getRelationsByChild')
83+
->with([2, 4, 5])
84+
->willReturn([]);
85+
$indexerMock->expects($this->atLeastOnce())
86+
->method('getRelationsByParent')
87+
->with([2, 4, 5])
88+
->willReturn([]);
89+
$indexerMock->expects($this->once())
90+
->method('reindexEntities')
91+
->with([2, 4, 5])
92+
->willReturnSelf();
93+
$mainTable = 'main_table_name';
94+
$indexerMock->expects($this->atLeastOnce())
95+
->method('getMainTable')
96+
->willReturn($mainTable);
97+
98+
$connectionMock = $this->createMock(AdapterInterface::class);
99+
$indexerMock->expects($this->atLeastOnce())
100+
->method('getConnection')
101+
->willReturn($connectionMock);
102+
$connectionMock->expects($this->once())
103+
->method('beginTransaction')
104+
->willReturnSelf();
105+
$connectionMock->expects($this->once())
106+
->method('quoteInto')
107+
->with('entity_id IN (?)', [2, 4, 5], 'INT')
108+
->willReturn('entity_id IN (2, 4, 5)');
109+
$connectionMock->expects($this->once())
110+
->method('delete')
111+
->with($mainTable, 'entity_id IN (2, 4, 5)')
112+
->willReturn(3);
113+
$idxTable = 'idx_table_name';
114+
$indexerMock->expects($this->atLeastOnce())
115+
->method('getIdxTable')
116+
->with()
117+
->willReturn($idxTable);
118+
$indexerMock->expects($this->once())
119+
->method('insertFromTable')
120+
->with($idxTable, $mainTable)
121+
->willReturnSelf();
122+
$connectionMock->expects($this->once())
123+
->method('commit')
124+
->willReturnSelf();
125+
}
126+
127+
$ids = [2, 4, 5];
128+
$this->model->execute($ids);
32129
}
33130
}

0 commit comments

Comments
 (0)