Skip to content

Commit 5a4adf9

Browse files
Merge pull request #49 from aligent/fix/changelog_temp_table
Use random suffix for temp table to avoid conflicts
2 parents 8f73e42 + 3cf2a53 commit 5a4adf9

File tree

3 files changed

+105
-30
lines changed

3 files changed

+105
-30
lines changed

src/index/Model/Changelog/InsertRecords.php

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@
66
use Aligent\FredhopperIndexer\Model\DataHandler;
77
use Aligent\FredhopperIndexer\Model\ResourceModel\Changelog as ChangelogResource;
88
use Magento\Framework\App\ResourceConnection;
9+
use Magento\Framework\Exception\LocalizedException;
910

1011
class InsertRecords
1112
{
1213
/**
1314
* @param ResourceConnection $resourceConnection
1415
* @param ChangelogResource $changelogResource
16+
* @param TempTable $tempTable
1517
*/
1618
public function __construct(
1719
private readonly ResourceConnection $resourceConnection,
18-
private readonly ChangelogResource $changelogResource
20+
private readonly ChangelogResource $changelogResource,
21+
private readonly TempTable $tempTable
1922
) {
2023
}
2124

@@ -24,30 +27,36 @@ public function __construct(
2427
*
2528
* @return void
2629
* @throws \Zend_Db_Select_Exception
30+
* @throws LocalizedException
2731
*/
2832
public function execute(): void
2933
{
34+
$tempTableName = $this->tempTable->getTempTableName();
3035
$addedProductIds = $this->getAddedOrDeletedProductsByType(
3136
true,
32-
DataHandler::TYPE_PRODUCT
37+
DataHandler::TYPE_PRODUCT,
38+
$tempTableName
3339
);
3440
$addedVariantIds = $this->getAddedOrDeletedProductsByType(
3541
true,
36-
DataHandler::TYPE_VARIANT
42+
DataHandler::TYPE_VARIANT,
43+
$tempTableName
3744
);
3845
$this->changelogResource->insertAdditionOperations($addedProductIds, $addedVariantIds);
3946

40-
$updatedProductIds = $this->getUpdatedProductsByType(DataHandler::TYPE_PRODUCT);
41-
$updatedVariantIds = $this->getUpdatedProductsByType(DataHandler::TYPE_VARIANT);
47+
$updatedProductIds = $this->getUpdatedProductsByType(DataHandler::TYPE_PRODUCT, $tempTableName);
48+
$updatedVariantIds = $this->getUpdatedProductsByType(DataHandler::TYPE_VARIANT, $tempTableName);
4249
$this->changelogResource->insertUpdateOperations($updatedProductIds, $updatedVariantIds);
4350

4451
$deletedProductIds = $this->getAddedOrDeletedProductsByType(
4552
false,
46-
DataHandler::TYPE_PRODUCT
53+
DataHandler::TYPE_PRODUCT,
54+
$tempTableName
4755
);
4856
$deletedVariantIds = $this->getAddedOrDeletedProductsByType(
4957
false,
50-
DataHandler::TYPE_VARIANT
58+
DataHandler::TYPE_VARIANT,
59+
$tempTableName
5160
);
5261
$this->changelogResource->insertDeleteOperations($deletedProductIds, $deletedVariantIds);
5362
}
@@ -57,21 +66,23 @@ public function execute(): void
5766
*
5867
* @param bool $isAddition
5968
* @param string $productType
69+
* @param string $tempTableName
6070
* @return array
6171
*/
6272
private function getAddedOrDeletedProductsByType(
6373
bool $isAddition,
64-
string $productType
74+
string $productType,
75+
string $tempTableName
6576
): array {
6677
$connection = $this->resourceConnection->getConnection();
6778
$select = $connection->select();
6879

6980
$select->from(
70-
['main_table' => ($isAddition ? DataHandler::INDEX_TABLE_NAME : TempTable::TEMP_TABLE_NAME)],
81+
['main_table' => ($isAddition ? DataHandler::INDEX_TABLE_NAME : $tempTableName)],
7182
['product_id']
7283
);
7384
$select->joinLeft(
74-
['temp_table' => ($isAddition ? TempTable::TEMP_TABLE_NAME : DataHandler::INDEX_TABLE_NAME)],
85+
['temp_table' => ($isAddition ? $tempTableName : DataHandler::INDEX_TABLE_NAME)],
7586
'temp_table.product_id = main_table.product_id AND '.
7687
'temp_table.product_type = main_table.product_type',
7788
[]
@@ -87,16 +98,17 @@ private function getAddedOrDeletedProductsByType(
8798
* Determine which products have been updated between the main and temporary table
8899
*
89100
* @param string $productType
101+
* @param string $tempTableName
90102
* @return array
91103
* @throws \Zend_Db_Select_Exception
92104
*/
93-
private function getUpdatedProductsByType(string $productType): array
105+
private function getUpdatedProductsByType(string $productType, string $tempTableName): array
94106
{
95107
// get all product ids and variant ids that exist in both tables
96108
// we do not want to consider products that are being added or deleted completely
97109
$connection = $this->resourceConnection->getConnection();
98110
$existingProductsSelect = $connection->select();
99-
$existingProductsSelect->from(['temp_table' => TempTable::TEMP_TABLE_NAME], ['product_id']);
111+
$existingProductsSelect->from(['temp_table' => $tempTableName], ['product_id']);
100112
$existingProductsSelect->joinInner(
101113
['main_table' => DataHandler::INDEX_TABLE_NAME],
102114
'main_table.product_id = temp_table.product_id AND ' .
@@ -114,7 +126,7 @@ private function getUpdatedProductsByType(string $productType): array
114126
['product_id']
115127
);
116128
$existingProductsTempMissingSelect->joinLeft(
117-
['temp_table' => TempTable::TEMP_TABLE_NAME],
129+
['temp_table' => $tempTableName],
118130
'main_table.product_id = temp_table.product_id AND ' .
119131
'main_table.product_type = temp_table.product_type AND ' .
120132
'main_table.store_id = temp_table.store_id',
@@ -127,7 +139,7 @@ private function getUpdatedProductsByType(string $productType): array
127139
// records that are in the old table, but not in the new table
128140
$existingProductsMainMissingSelect = $connection->select();
129141
$existingProductsMainMissingSelect->from(
130-
['temp_table' => TempTable::TEMP_TABLE_NAME],
142+
['temp_table' => $tempTableName],
131143
['product_id']
132144
);
133145
$existingProductsMainMissingSelect->joinLeft(
@@ -148,7 +160,7 @@ private function getUpdatedProductsByType(string $productType): array
148160
['product_id']
149161
);
150162
$existingProductsDifferenceSelect->joinInner(
151-
['temp_table' => TempTable::TEMP_TABLE_NAME],
163+
['temp_table' => $tempTableName],
152164
'main_table.product_id = temp_table.product_id AND ' .
153165
'main_table.product_type = temp_table.product_type AND ' .
154166
'main_table.store_id = temp_table.store_id AND '.

src/index/Model/Changelog/TempTable.php

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,82 @@
55

66
use Aligent\FredhopperIndexer\Model\DataHandler;
77
use Magento\Framework\App\ResourceConnection;
8+
use Magento\Framework\Exception\LocalizedException;
9+
use Random\RandomException;
810

911
class TempTable
1012
{
11-
public const string TEMP_TABLE_NAME = DataHandler::INDEX_TABLE_NAME . '_temp';
13+
private const string TEMP_TABLE_PREFIX = DataHandler::INDEX_TABLE_NAME . '_temp_';
1214

15+
private string $tempTableName;
16+
17+
/**
18+
* @param ResourceConnection $resourceConnection
19+
*/
1320
public function __construct(
1421
private readonly ResourceConnection $resourceConnection
1522
) {
1623
}
1724

25+
/**
26+
* Gets the current temporary table name
27+
*
28+
* @return string
29+
* @throws LocalizedException
30+
*/
31+
public function getTempTableName(): string
32+
{
33+
if (!isset($this->tempTableName)) {
34+
throw new LocalizedException(__(__METHOD__ . ': temp table name not set'));
35+
}
36+
return $this->tempTableName;
37+
}
38+
39+
/**
40+
* Sets the current temporary table to a unique value
41+
*
42+
* @return void
43+
* @throws LocalizedException
44+
*/
45+
public function generateTempTableName(): void
46+
{
47+
if (isset($this->tempTableName)) {
48+
throw new LocalizedException(__(__METHOD__ . ': temp table name already set'));
49+
}
50+
try {
51+
$tempTableName = self::TEMP_TABLE_PREFIX . bin2hex(random_bytes(4));
52+
} catch (RandomException) {
53+
$tempTableName = uniqid(self::TEMP_TABLE_PREFIX, true);
54+
}
55+
$this->tempTableName = $tempTableName;
56+
}
57+
1858
/**
1959
* Creates a temporary copy of the index table for use in generating changelog records
2060
*
2161
* @return void
62+
* @throws LocalizedException
2263
*/
2364
public function create(): void
2465
{
66+
if (!isset($this->tempTableName)) {
67+
throw new LocalizedException(__(__METHOD__ . ': temp table name not set'));
68+
}
2569
$connection = $this->resourceConnection->getConnection();
26-
$connection->createTemporaryTableLike(self::TEMP_TABLE_NAME, DataHandler::INDEX_TABLE_NAME);
70+
$table = $connection->createTableByDdl(DataHandler::INDEX_TABLE_NAME, $this->tempTableName);
71+
if ($connection->isTableExists($this->tempTableName)) {
72+
throw new LocalizedException(__(__METHOD__ . ': temp table already exists'));
73+
}
74+
try {
75+
$connection->createTable($table);
76+
} catch (\Exception $e) {
77+
throw new LocalizedException(__(__METHOD__ . ': ' . $e->getMessage()), $e);
78+
}
2779
$copySelect = $connection->select();
2880
$copySelect->from(DataHandler::INDEX_TABLE_NAME);
2981
$copyInsert = $connection->insertFromSelect(
3082
$copySelect,
31-
self::TEMP_TABLE_NAME
83+
$this->tempTableName
3284
);
3385
$connection->query($copyInsert);
3486
}
@@ -37,9 +89,14 @@ public function create(): void
3789
* Drops the temporary table if it exists
3890
*
3991
* @return void
92+
* @throws LocalizedException
4093
*/
4194
public function drop(): void
4295
{
43-
$this->resourceConnection->getConnection()->dropTemporaryTable(self::TEMP_TABLE_NAME);
96+
if (!isset($this->tempTableName)) {
97+
throw new LocalizedException(__(__METHOD__ . ': temp table name not set'));
98+
}
99+
$this->resourceConnection->getConnection()->dropTable($this->tempTableName);
100+
unset($this->tempTableName);
44101
}
45102
}

src/index/Model/ProductIndexer.php

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,25 +70,31 @@ public function executeFull(): void
7070
/**
7171
* @inheritDoc
7272
*
73-
* @throws LocalizedException
7473
*/
7574
public function executeList(array $ids): void
7675
{
77-
// create temporary table to handle changelogs
78-
$this->tempTable->create();
79-
foreach ($this->dimensionProvider->getIterator() as $dimension) {
76+
try {
77+
// create temporary table to handle changelogs
78+
$this->tempTable->generateTempTableName();
79+
$this->tempTable->create();
80+
// try block here is nested to ensure that if the table was created, it gets dropped at the end
8081
try {
81-
$this->executeByDimensions($dimension, new \ArrayIterator($ids));
82-
} catch (FileSystemException|RuntimeException) {
83-
continue;
82+
foreach ($this->dimensionProvider->getIterator() as $dimension) {
83+
try {
84+
$this->executeByDimensions($dimension, new \ArrayIterator($ids));
85+
} catch (FileSystemException|RuntimeException) {
86+
continue;
87+
}
88+
}
89+
$this->insertChangelogRecords->execute();
90+
} finally {
91+
// we want to ensure that the "temporary" table is always dropped
92+
$this->tempTable->drop();
8493
}
85-
}
86-
try {
87-
$this->insertChangelogRecords->execute();
8894
} catch (\Exception $e) {
8995
$this->logger->critical($e->getMessage(), ['exception' => $e]);
9096
}
91-
$this->tempTable->drop();
97+
9298
}
9399

94100
/**

0 commit comments

Comments
 (0)