Skip to content

Commit 8782b34

Browse files
committed
MAGETWO-90148: Overlapping reindex processes of catalogrule_rule
1 parent b16dfc6 commit 8782b34

14 files changed

+503
-84
lines changed

app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Magento\Framework\App\ObjectManager;
1313
use Magento\Framework\Pricing\PriceCurrencyInterface;
1414
use Magento\CatalogRule\Model\Indexer\IndexBuilder\ProductLoader;
15+
use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface as TableSwapper;
1516

1617
/**
1718
* @api
@@ -136,6 +137,11 @@ class IndexBuilder
136137
*/
137138
private $activeTableSwitcher;
138139

140+
/**
141+
* @var TableSwapper
142+
*/
143+
private $tableSwapper;
144+
139145
/**
140146
* @var ProductLoader
141147
*/
@@ -160,6 +166,7 @@ class IndexBuilder
160166
* @param RuleProductPricesPersistor|null $pricesPersistor
161167
* @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher
162168
* @param ProductLoader|null $productLoader
169+
* @param TableSwapper|null $tableSwapper
163170
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
164171
*/
165172
public function __construct(
@@ -180,7 +187,8 @@ public function __construct(
180187
ReindexRuleProductPrice $reindexRuleProductPrice = null,
181188
RuleProductPricesPersistor $pricesPersistor = null,
182189
\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null,
183-
ProductLoader $productLoader = null
190+
ProductLoader $productLoader = null,
191+
TableSwapper $tableSwapper = null
184192
) {
185193
$this->resource = $resource;
186194
$this->connection = $resource->getConnection();
@@ -218,6 +226,8 @@ public function __construct(
218226
$this->productLoader = $productLoader ?? ObjectManager::getInstance()->get(
219227
ProductLoader::class
220228
);
229+
$this->tableSwapper = $tableSwapper ??
230+
ObjectManager::getInstance()->get(TableSwapper::class);
221231
}
222232

223233
/**
@@ -296,22 +306,14 @@ public function reindexFull()
296306
*/
297307
protected function doReindexFull()
298308
{
299-
$this->connection->truncateTable(
300-
$this->getTable($this->activeTableSwitcher->getAdditionalTableName('catalogrule_product'))
301-
);
302-
$this->connection->truncateTable(
303-
$this->getTable($this->activeTableSwitcher->getAdditionalTableName('catalogrule_product_price'))
304-
);
305-
306309
foreach ($this->getAllRules() as $rule) {
307310
$this->reindexRuleProduct->execute($rule, $this->batchCount, true);
308311
}
309312

310313
$this->reindexRuleProductPrice->execute($this->batchCount, null, true);
311314
$this->reindexRuleGroupWebsite->execute(true);
312315

313-
$this->activeTableSwitcher->switchTable(
314-
$this->connection,
316+
$this->tableSwapper->swapIndexTables(
315317
[
316318
$this->getTable('catalogrule_product'),
317319
$this->getTable('catalogrule_product_price'),
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogRule\Model\Indexer;
9+
10+
use Magento\Framework\App\ResourceConnection;
11+
12+
/**
13+
* @inheritDoc
14+
*/
15+
class IndexerTableSwapper implements IndexerTableSwapperInterface
16+
{
17+
/**
18+
* Keys are original tables' names, values - created temporary tables.
19+
*
20+
* @var string[]
21+
*/
22+
private $temporaryTables = [];
23+
24+
/**
25+
* @var ResourceConnection
26+
*/
27+
private $resourceConnection;
28+
29+
/**
30+
* @param ResourceConnection $resource
31+
*/
32+
public function __construct(ResourceConnection $resource)
33+
{
34+
$this->resourceConnection = $resource;
35+
}
36+
37+
/**
38+
* Create temporary table based on given table to use instead of original.
39+
*
40+
* @param string $originalTableName
41+
*
42+
* @return string Created table name.
43+
*/
44+
private function createTemporaryTable(string $originalTableName): string
45+
{
46+
$temporaryTableName = $this->resourceConnection->getTableName(
47+
$originalTableName . '__temp' . $this->generateRandomSuffix()
48+
);
49+
50+
$this->resourceConnection->getConnection()->createTable(
51+
$this->resourceConnection->getConnection()->createTableByDdl(
52+
$this->resourceConnection->getTableName($originalTableName),
53+
$temporaryTableName
54+
)
55+
);
56+
57+
return $temporaryTableName;
58+
}
59+
60+
/**
61+
* Random suffix for temporary tables not to conflict with each other.
62+
*
63+
* @return string
64+
*/
65+
private function generateRandomSuffix(): string
66+
{
67+
return bin2hex(random_bytes(4));
68+
}
69+
70+
/**
71+
* @inheritDoc
72+
*/
73+
public function getWorkingTableName(string $originalTable): string
74+
{
75+
$originalTable = $this->resourceConnection->getTableName($originalTable);
76+
if (!array_key_exists($originalTable, $this->temporaryTables)) {
77+
$this->temporaryTables[$originalTable] = $this->createTemporaryTable($originalTable);
78+
}
79+
80+
return $this->temporaryTables[$originalTable];
81+
}
82+
83+
/**
84+
* @inheritDoc
85+
*/
86+
public function swapIndexTables(array $originalTablesNames)
87+
{
88+
$toRename = [];
89+
/** @var string[] $toDrop */
90+
$toDrop = [];
91+
/** @var string[] $temporaryTablesRenamed */
92+
$temporaryTablesRenamed = [];
93+
//Renaming temporary tables to original tables' names, dropping old
94+
//tables.
95+
foreach ($originalTablesNames as $tableName) {
96+
$tableName = $this->resourceConnection->getTableName($tableName);
97+
$temporaryOriginalName = $this->resourceConnection->getTableName(
98+
$tableName . $this->generateRandomSuffix()
99+
);
100+
$temporaryTableName = $this->getWorkingTableName($tableName);
101+
$toRename[] = [
102+
'oldName' => $tableName,
103+
'newName' => $temporaryOriginalName,
104+
];
105+
$toRename[] = [
106+
'oldName' => $temporaryTableName,
107+
'newName' => $tableName,
108+
];
109+
$toDrop[] = $temporaryOriginalName;
110+
$temporaryTablesRenamed[] = $tableName;
111+
}
112+
113+
//Swapping tables.
114+
$this->resourceConnection->getConnection()->renameTablesBatch($toRename);
115+
//Cleaning up.
116+
foreach ($temporaryTablesRenamed as $tableName) {
117+
unset($this->temporaryTables[$tableName]);
118+
}
119+
//Removing old ones.
120+
foreach ($toDrop as $tableName) {
121+
$this->resourceConnection->getConnection()->dropTable($tableName);
122+
}
123+
}
124+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogRule\Model\Indexer;
9+
10+
/**
11+
* Manage additional tables used while building new index to preserve
12+
* index tables until the process finishes.
13+
*/
14+
interface IndexerTableSwapperInterface
15+
{
16+
/**
17+
* Get working table name used to build index.
18+
*
19+
* @param string $originalTable
20+
*
21+
* @return string
22+
*/
23+
public function getWorkingTableName(string $originalTable): string;
24+
25+
/**
26+
* Swap working tables with actual tables to save new indexes.
27+
*
28+
* @param string[] $originalTablesNames
29+
*
30+
* @return void
31+
*/
32+
public function swapIndexTables(array $originalTablesNames);
33+
}

app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleGroupWebsite.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
namespace Magento\CatalogRule\Model\Indexer;
88

9+
use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface as TableSwapper;
10+
use Magento\Framework\App\ObjectManager;
11+
use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
12+
913
/**
1014
* Reindex information about rule relations with customer groups and websites.
1115
*/
@@ -27,23 +31,32 @@ class ReindexRuleGroupWebsite
2731
private $catalogRuleGroupWebsiteColumnsList = ['rule_id', 'customer_group_id', 'website_id'];
2832

2933
/**
30-
* @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
34+
* @var ActiveTableSwitcher
3135
*/
3236
private $activeTableSwitcher;
3337

38+
/**
39+
* @var TableSwapper
40+
*/
41+
private $tableSwapper;
42+
3443
/**
3544
* @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
3645
* @param \Magento\Framework\App\ResourceConnection $resource
37-
* @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher
46+
* @param ActiveTableSwitcher $activeTableSwitcher
47+
* @param TableSwapper|null $tableSwapper
3848
*/
3949
public function __construct(
4050
\Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
4151
\Magento\Framework\App\ResourceConnection $resource,
42-
\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher
52+
ActiveTableSwitcher $activeTableSwitcher,
53+
TableSwapper $tableSwapper = null
4354
) {
4455
$this->dateTime = $dateTime;
4556
$this->resource = $resource;
4657
$this->activeTableSwitcher = $activeTableSwitcher;
58+
$this->tableSwapper = $tableSwapper ??
59+
ObjectManager::getInstance()->get(TableSwapper::class);
4760
}
4861

4962
/**
@@ -61,10 +74,10 @@ public function execute($useAdditionalTable = false)
6174
$ruleProductTable = $this->resource->getTableName('catalogrule_product');
6275
if ($useAdditionalTable) {
6376
$indexTable = $this->resource->getTableName(
64-
$this->activeTableSwitcher->getAdditionalTableName('catalogrule_group_website')
77+
$this->tableSwapper->getWorkingTableName('catalogrule_group_website')
6578
);
6679
$ruleProductTable = $this->resource->getTableName(
67-
$this->activeTableSwitcher->getAdditionalTableName('catalogrule_product')
80+
$this->tableSwapper->getWorkingTableName('catalogrule_product')
6881
);
6982
}
7083

app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
namespace Magento\CatalogRule\Model\Indexer;
88

9+
use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface as TableSwapper;
10+
use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
11+
use Magento\Framework\App\ObjectManager;
12+
913
/**
1014
* Reindex rule relations with products.
1115
*/
@@ -17,20 +21,29 @@ class ReindexRuleProduct
1721
private $resource;
1822

1923
/**
20-
* @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
24+
* @var ActiveTableSwitcher
2125
*/
2226
private $activeTableSwitcher;
2327

28+
/**
29+
* @var TableSwapper
30+
*/
31+
private $tableSwapper;
32+
2433
/**
2534
* @param \Magento\Framework\App\ResourceConnection $resource
26-
* @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher
35+
* @param ActiveTableSwitcher $activeTableSwitcher
36+
* @param TableSwapper|null $tableSwapper
2737
*/
2838
public function __construct(
2939
\Magento\Framework\App\ResourceConnection $resource,
30-
\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher
40+
ActiveTableSwitcher $activeTableSwitcher,
41+
TableSwapper $tableSwapper = null
3142
) {
3243
$this->resource = $resource;
3344
$this->activeTableSwitcher = $activeTableSwitcher;
45+
$this->tableSwapper = $tableSwapper ??
46+
ObjectManager::getInstance()->get(TableSwapper::class);
3447
}
3548

3649
/**
@@ -65,7 +78,7 @@ public function execute(
6578
$indexTable = $this->resource->getTableName('catalogrule_product');
6679
if ($useAdditionalTable) {
6780
$indexTable = $this->resource->getTableName(
68-
$this->activeTableSwitcher->getAdditionalTableName('catalogrule_product')
81+
$this->tableSwapper->getWorkingTableName('catalogrule_product')
6982
);
7083
}
7184

app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
namespace Magento\CatalogRule\Model\Indexer;
88

9+
use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface as TableSwapper;
10+
use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
11+
use Magento\Framework\App\ObjectManager;
12+
913
/**
1014
* Persist product prices to index table.
1115
*/
@@ -22,23 +26,32 @@ class RuleProductPricesPersistor
2226
private $dateFormat;
2327

2428
/**
25-
* @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher
29+
* @var ActiveTableSwitcher
2630
*/
2731
private $activeTableSwitcher;
2832

33+
/**
34+
* @var TableSwapper
35+
*/
36+
private $tableSwapper;
37+
2938
/**
3039
* @param \Magento\Framework\Stdlib\DateTime $dateFormat
3140
* @param \Magento\Framework\App\ResourceConnection $resource
32-
* @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher
41+
* @param ActiveTableSwitcher $activeTableSwitcher
42+
* @param TableSwapper|null $tableSwapper
3343
*/
3444
public function __construct(
3545
\Magento\Framework\Stdlib\DateTime $dateFormat,
3646
\Magento\Framework\App\ResourceConnection $resource,
37-
\Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher
47+
ActiveTableSwitcher $activeTableSwitcher,
48+
TableSwapper $tableSwapper = null
3849
) {
3950
$this->dateFormat = $dateFormat;
4051
$this->resource = $resource;
4152
$this->activeTableSwitcher = $activeTableSwitcher;
53+
$this->tableSwapper = $tableSwapper ??
54+
ObjectManager::getInstance()->get(TableSwapper::class);
4255
}
4356

4457
/**
@@ -59,7 +72,7 @@ public function execute(array $priceData, $useAdditionalTable = false)
5972
$indexTable = $this->resource->getTableName('catalogrule_product_price');
6073
if ($useAdditionalTable) {
6174
$indexTable = $this->resource->getTableName(
62-
$this->activeTableSwitcher->getAdditionalTableName('catalogrule_product_price')
75+
$this->tableSwapper->getWorkingTableName('catalogrule_product_price')
6376
);
6477
}
6578

0 commit comments

Comments
 (0)