Skip to content
This repository was archived by the owner on Apr 29, 2019. It is now read-only.

Commit 8b0baf4

Browse files
mslabkoduhon
authored andcommitted
MAGETWO-87614: Deadlock on product creation
1 parent 3aa0a16 commit 8b0baf4

File tree

23 files changed

+9045
-8745
lines changed

23 files changed

+9045
-8745
lines changed

app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,13 @@ protected function processNewAndExistingImages($product, array &$images)
245245
if (empty($image['removed'])) {
246246
$data = $this->processNewImage($product, $image);
247247

248-
$this->resourceModel->deleteGalleryValueInStore(
249-
$image['value_id'],
250-
$product->getData($this->metadata->getLinkField()),
251-
$product->getStoreId()
252-
);
253-
248+
if (!$product->isObjectNew()) {
249+
$this->resourceModel->deleteGalleryValueInStore(
250+
$image['value_id'],
251+
$product->getData($this->metadata->getLinkField()),
252+
$product->getStoreId()
253+
);
254+
}
254255
// Add per store labels, position, disabled
255256
$data['value_id'] = $image['value_id'];
256257
$data['label'] = isset($image['label']) ? $image['label'] : '';

app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ protected function _prepareLoadSelect(array $selects)
180180
protected function _saveAttributeValue($object, $attribute, $value)
181181
{
182182
$connection = $this->getConnection();
183-
$storeId = (int) $this->_storeManager->getStore($object->getStoreId())->getId();
183+
$hasSingleStore = $this->_storeManager->hasSingleStore();
184+
$storeId = $hasSingleStore
185+
? $this->getDefaultStoreId()
186+
: (int) $this->_storeManager->getStore($object->getStoreId())->getId();
184187
$table = $attribute->getBackend()->getTable();
185188

186189
/**
@@ -189,15 +192,18 @@ protected function _saveAttributeValue($object, $attribute, $value)
189192
* In this case we clear all not default values
190193
*/
191194
$entityIdField = $this->getLinkField();
192-
if ($this->_storeManager->hasSingleStore()) {
193-
$storeId = $this->getDefaultStoreId();
195+
$conditions = [
196+
'attribute_id = ?' => $attribute->getAttributeId(),
197+
"{$entityIdField} = ?" => $object->getData($entityIdField),
198+
'store_id <> ?' => $storeId
199+
];
200+
if ($hasSingleStore
201+
&& !$object->isObjectNew()
202+
&& $this->isAttributePresentForNonDefaultStore($attribute, $conditions)
203+
) {
194204
$connection->delete(
195205
$table,
196-
[
197-
'attribute_id = ?' => $attribute->getAttributeId(),
198-
"{$entityIdField} = ?" => $object->getData($entityIdField),
199-
'store_id <> ?' => $storeId
200-
]
206+
$conditions
201207
);
202208
}
203209

@@ -236,6 +242,27 @@ protected function _saveAttributeValue($object, $attribute, $value)
236242
return $this;
237243
}
238244

245+
/**
246+
* Check if attribute present for non default Store View.
247+
* Prevent "delete" query locking in a case when nothing to delete
248+
*
249+
* @param AbstractAttribute $attribute
250+
* @param array $conditions
251+
*
252+
* @return boolean
253+
*/
254+
private function isAttributePresentForNonDefaultStore($attribute, $conditions)
255+
{
256+
$connection = $this->getConnection();
257+
$select = $connection->select()->from($attribute->getBackend()->getTable());
258+
foreach ($conditions as $condition => $conditionValue) {
259+
$select->where($condition, $conditionValue);
260+
}
261+
$select->limit(1);
262+
263+
return !empty($connection->fetchRow($select));
264+
}
265+
239266
/**
240267
* Insert entity attribute value
241268
*

app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,12 @@ public function saveCategoryLinks(ProductInterface $product, array $categoryLink
8383
$insertUpdate = $this->processCategoryLinks($categoryLinks, $oldCategoryLinks);
8484
$deleteUpdate = $this->processCategoryLinks($oldCategoryLinks, $categoryLinks);
8585

86-
list($delete, $insert) = $this->analyseUpdatedLinks($deleteUpdate, $insertUpdate);
86+
list($delete, $insert, $update) = $this->analyseUpdatedLinks($deleteUpdate, $insertUpdate);
8787

8888
return array_merge(
89-
$this->updateCategoryLinks($product, $insert),
90-
$this->deleteCategoryLinks($product, $delete)
89+
$this->deleteCategoryLinks($product, $delete),
90+
$this->updateCategoryLinks($product, $insert, true),
91+
$this->updateCategoryLinks($product, $update)
9192
);
9293
}
9394

@@ -133,16 +134,15 @@ private function processCategoryLinks($newCategoryPositions, &$oldCategoryPositi
133134
/**
134135
* @param ProductInterface $product
135136
* @param array $insertLinks
137+
* @param bool $insert
136138
* @return array
137139
*/
138-
private function updateCategoryLinks(ProductInterface $product, array $insertLinks)
140+
private function updateCategoryLinks(ProductInterface $product, array $insertLinks, $insert = false)
139141
{
140142
if (empty($insertLinks)) {
141143
return [];
142144
}
143145

144-
$connection = $this->resourceConnection->getConnection();
145-
146146
$data = [];
147147
foreach ($insertLinks as $categoryLink) {
148148
$data[] = [
@@ -153,11 +153,22 @@ private function updateCategoryLinks(ProductInterface $product, array $insertLin
153153
}
154154

155155
if ($data) {
156-
$connection->insertOnDuplicate(
157-
$this->getCategoryLinkMetadata()->getEntityTable(),
158-
$data,
159-
['position']
160-
);
156+
$connection = $this->resourceConnection->getConnection();
157+
if ($insert) {
158+
$connection->insertArray(
159+
$this->getCategoryLinkMetadata()->getEntityTable(),
160+
array_keys($data[0]),
161+
$data,
162+
\Magento\Framework\DB\Adapter\AdapterInterface::INSERT_IGNORE
163+
);
164+
} else {
165+
// for mass update category links with constraint by unique key use insert on duplicate statement
166+
$connection->insertOnDuplicate(
167+
$this->getCategoryLinkMetadata()->getEntityTable(),
168+
$data,
169+
['position']
170+
);
171+
}
161172
}
162173

163174
return array_column($insertLinks, 'category_id');
@@ -215,7 +226,7 @@ private function verifyCategoryLinks(array $links)
215226
}
216227

217228
/**
218-
* Analyse category links for update or/and delete
229+
* Analyse category links for update or/and delete. Return array of links for delete, insert and update
219230
*
220231
* @param array $deleteUpdate
221232
* @param array $insertUpdate
@@ -226,8 +237,7 @@ private function analyseUpdatedLinks($deleteUpdate, $insertUpdate)
226237
$delete = $deleteUpdate['changed'] ?: [];
227238
$insert = $insertUpdate['changed'] ?: [];
228239
$insert = array_merge_recursive($insert, $deleteUpdate['updated']);
229-
$insert = array_merge_recursive($insert, $insertUpdate['updated']);
230240

231-
return [$delete, $insert];
241+
return [$delete, $insert, $insertUpdate['updated']];
232242
}
233243
}

app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php

Lines changed: 98 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
namespace Magento\Catalog\Model\ResourceModel\Product;
77

8+
use Magento\Framework\DB\Adapter\AdapterInterface;
9+
810
/**
911
* Catalog product link resource model
1012
*
@@ -136,7 +138,6 @@ public function saveProductLinks($parentId, $data, $typeId)
136138
$data = [];
137139
}
138140

139-
$attributes = $this->getAttributesByType($typeId);
140141
$connection = $this->getConnection();
141142

142143
$bind = [':product_id' => (int)$parentId, ':link_type_id' => (int)$typeId];
@@ -151,42 +152,38 @@ public function saveProductLinks($parentId, $data, $typeId)
151152

152153
$links = $connection->fetchPairs($select, $bind);
153154

154-
foreach ($data as $linkedProductId => $linkInfo) {
155-
$linkId = null;
156-
if (isset($links[$linkedProductId])) {
157-
$linkId = $links[$linkedProductId];
158-
unset($links[$linkedProductId]);
159-
} else {
160-
$bind = [
161-
'product_id' => $parentId,
162-
'linked_product_id' => $linkedProductId,
163-
'link_type_id' => $typeId,
164-
];
165-
$connection->insert($this->getMainTable(), $bind);
166-
$linkId = $connection->lastInsertId($this->getMainTable());
167-
}
155+
list($insertData, $updateData, $deleteConditions) = $this->prepareProductLinksData(
156+
$parentId,
157+
$data,
158+
$typeId,
159+
$links
160+
);
168161

169-
foreach ($attributes as $attributeInfo) {
170-
$attributeTable = $this->getAttributeTypeTable($attributeInfo['type']);
171-
if ($attributeTable) {
172-
if (isset($linkInfo[$attributeInfo['code']])) {
173-
$value = $this->_prepareAttributeValue(
174-
$attributeInfo['type'],
175-
$linkInfo[$attributeInfo['code']]
176-
);
177-
$bind = [
178-
'product_link_attribute_id' => $attributeInfo['id'],
179-
'link_id' => $linkId,
180-
'value' => $value,
181-
];
182-
$connection->insertOnDuplicate($attributeTable, $bind, ['value']);
183-
} else {
184-
$connection->delete(
185-
$attributeTable,
186-
['link_id = ?' => $linkId, 'product_link_attribute_id = ?' => $attributeInfo['id']]
187-
);
188-
}
189-
}
162+
if ($insertData) {
163+
$insertColumns = [
164+
'product_link_attribute_id',
165+
'link_id',
166+
'value',
167+
];
168+
foreach ($insertData as $table => $values) {
169+
$connection->insertArray($table, $insertColumns, $values, AdapterInterface::INSERT_IGNORE);
170+
}
171+
}
172+
if ($updateData) {
173+
// for mass update product links with constraint by unique key use insert on duplicate statement
174+
foreach ($updateData as $table => $values) {
175+
$connection->insertOnDuplicate($table, $values, ['value']);
176+
}
177+
}
178+
if ($deleteConditions) {
179+
foreach ($deleteConditions as $table => $deleteCondition) {
180+
$connection->delete(
181+
$table,
182+
[
183+
'link_id = ?' => $deleteCondition['link_id'],
184+
'product_link_attribute_id = ?' => $deleteCondition['product_link_attribute_id']
185+
]
186+
);
190187
}
191188
}
192189

@@ -302,4 +299,69 @@ public function getParentIdsByChild($childId, $typeId)
302299

303300
return $parentIds;
304301
}
302+
303+
/**
304+
* Prepare data for insert, update or delete product link attributes
305+
*
306+
* @param int $parentId
307+
* @param array $data
308+
* @param int $typeId
309+
* @param array $links
310+
* @return array
311+
*/
312+
private function prepareProductLinksData($parentId, $data, $typeId, $links)
313+
{
314+
$connection = $this->getConnection();
315+
$attributes = $this->getAttributesByType($typeId);
316+
317+
$insertData = [];
318+
$updateData = [];
319+
$deleteConditions = [];
320+
321+
foreach ($data as $linkedProductId => $linkInfo) {
322+
$linkId = null;
323+
if (isset($links[$linkedProductId])) {
324+
$linkId = $links[$linkedProductId];
325+
} else {
326+
$bind = [
327+
'product_id' => $parentId,
328+
'linked_product_id' => $linkedProductId,
329+
'link_type_id' => $typeId,
330+
];
331+
$connection->insert($this->getMainTable(), $bind);
332+
$linkId = $connection->lastInsertId($this->getMainTable());
333+
}
334+
335+
foreach ($attributes as $attributeInfo) {
336+
$attributeTable = $this->getAttributeTypeTable($attributeInfo['type']);
337+
if (!$attributeTable) {
338+
continue;
339+
}
340+
if (isset($linkInfo[$attributeInfo['code']])) {
341+
$value = $this->_prepareAttributeValue(
342+
$attributeInfo['type'],
343+
$linkInfo[$attributeInfo['code']]
344+
);
345+
if (isset($links[$linkedProductId])) {
346+
$updateData[$attributeTable][] = [
347+
'product_link_attribute_id' => $attributeInfo['id'],
348+
'link_id' => $linkId,
349+
'value' => $value,
350+
];
351+
} else {
352+
$insertData[$attributeTable][] = [
353+
'product_link_attribute_id' => $attributeInfo['id'],
354+
'link_id' => $linkId,
355+
'value' => $value,
356+
];
357+
}
358+
} else {
359+
$deleteConditions[$attributeTable]['link_id'][] = $linkId;
360+
$deleteConditions[$attributeTable]['product_link_attribute_id'][] = $attributeInfo['id'];
361+
}
362+
}
363+
}
364+
365+
return [$insertData, $updateData, $deleteConditions];
366+
}
305367
}

app/code/Magento/Catalog/Observer/UnsetSpecialPrice.php

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)