Skip to content

Commit 76c6935

Browse files
committed
MAGETWO-75530: Deadlock when 'Asynchronous indexing' is enabled (insert from select on sales_order_grid).
1 parent 5565a24 commit 76c6935

File tree

3 files changed

+123
-16
lines changed

3 files changed

+123
-16
lines changed

app/code/Magento/Sales/Model/ResourceModel/Grid.php

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class Grid extends AbstractGrid
4545
*/
4646
private $notSyncedDataProvider;
4747

48+
/**
49+
* Order grid rows batch size
50+
*/
51+
const BATCH_SIZE = 100;
52+
4853
/**
4954
* @param Context $context
5055
* @param string $mainTableName
@@ -104,25 +109,20 @@ public function refresh($value, $field = null)
104109
*
105110
* Only orders created/updated since the last method call will be added.
106111
*
107-
* @return \Zend_Db_Statement_Interface
112+
* @return void
108113
*/
109114
public function refreshBySchedule()
110115
{
111-
$select = $this->getGridOriginSelect()
112-
->where(
113-
$this->mainTableName . '.entity_id IN (?)',
114-
$this->notSyncedDataProvider->getIds($this->mainTableName, $this->gridTableName)
116+
$notSyncedIds = $this->notSyncedDataProvider->getIds($this->mainTableName, $this->gridTableName);
117+
foreach (array_chunk($notSyncedIds, self::BATCH_SIZE) as $bunch) {
118+
$select = $this->getGridOriginSelect()->where($this->mainTableName . '.entity_id IN (?)', $bunch);
119+
$fetchResult = $this->getConnection()->fetchAll($select);
120+
$this->getConnection()->insertOnDuplicate(
121+
$this->getTable($this->gridTableName),
122+
$fetchResult,
123+
array_keys($this->columns)
115124
);
116-
117-
return $this->getConnection()->query(
118-
$this->getConnection()
119-
->insertFromSelect(
120-
$select,
121-
$this->getTable($this->gridTableName),
122-
array_keys($this->columns),
123-
AdapterInterface::INSERT_ON_DUPLICATE
124-
)
125-
);
125+
}
126126
}
127127

128128
/**

app/code/Magento/Sales/Model/ResourceModel/GridInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function refresh($value, $field = null);
2929
*
3030
* Only rows created/updated since the last method call should be added.
3131
*
32-
* @return \Zend_Db_Statement_Interface
32+
* @return void
3333
*/
3434
public function refreshBySchedule();
3535

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Sales\Test\Unit\Model\ResourceModel;
7+
8+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
9+
use Magento\Sales\Model\ResourceModel\Provider\NotSyncedDataProviderInterface;
10+
use Magento\Framework\DB\Adapter\AdapterInterface as ConnectionAdapterInterface;
11+
use Magento\Sales\Model\ResourceModel\Grid;
12+
13+
/**
14+
* Unit tests for \Magento\Sales\Model\ResourceModel\Grid class
15+
*/
16+
class GridTest extends \PHPUnit\Framework\TestCase
17+
{
18+
/**
19+
* @var Grid
20+
*/
21+
private $grid;
22+
23+
/**
24+
* @var NotSyncedDataProviderInterface|\PHPUnit_Framework_MockObject_MockObject
25+
*/
26+
private $notSyncedDataProvider;
27+
28+
/**
29+
* @var ConnectionAdapterInterface|\PHPUnit_Framework_MockObject_MockObject
30+
*/
31+
private $connection;
32+
33+
/**
34+
* @var string
35+
*/
36+
private $mainTable = 'sales_order';
37+
38+
/**
39+
* @var string
40+
*/
41+
private $gridTable = 'sales_order_grid';
42+
43+
/**
44+
* @var array
45+
*/
46+
private $columns = [
47+
'column_1_key' => 'column_1_value',
48+
'column_2_key' => 'column_2_value'
49+
];
50+
51+
/**
52+
* @inheritdoc
53+
*/
54+
protected function setUp()
55+
{
56+
$objectManager = new ObjectManager($this);
57+
$this->notSyncedDataProvider = $this->getMockBuilder(NotSyncedDataProviderInterface::class)
58+
->disableOriginalConstructor()
59+
->setMethods(['getIds'])
60+
->getMockForAbstractClass();
61+
$this->connection = $this->getMockBuilder(ConnectionAdapterInterface::class)
62+
->disableOriginalConstructor()
63+
->setMethods(['select', 'fetchAll', 'insertOnDuplicate'])
64+
->getMockForAbstractClass();
65+
66+
$this->grid = $objectManager->getObject(
67+
\Magento\Sales\Model\ResourceModel\Grid::class,
68+
[
69+
'notSyncedDataProvider' => $this->notSyncedDataProvider,
70+
'mainTableName' => $this->mainTable,
71+
'gridTableName' => $this->gridTable,
72+
'connection' => $this->connection,
73+
'_tables' => ['sales_order' => $this->mainTable, 'sales_order_grid' => $this->gridTable],
74+
'columns' => $this->columns
75+
]
76+
);
77+
}
78+
79+
/**
80+
* Test for refreshBySchedule() method
81+
*/
82+
public function testRefreshBySchedule()
83+
{
84+
$notSyncedIds = ['1', '2', '3'];
85+
$fetchResult = ['column_1' => '1', 'column_2' => '2'];
86+
87+
$this->notSyncedDataProvider->expects($this->atLeastOnce())->method('getIds')->willReturn($notSyncedIds);
88+
$select = $this->getMockBuilder(\Magento\Framework\DB\Select::class)
89+
->disableOriginalConstructor()
90+
->setMethods(['from', 'columns', 'where'])
91+
->getMock();
92+
$select->expects($this->atLeastOnce())->method('from')->with(['sales_order' => $this->mainTable], [])
93+
->willReturnSelf();
94+
$select->expects($this->atLeastOnce())->method('columns')->willReturnSelf();
95+
$select->expects($this->atLeastOnce())->method('where')
96+
->with($this->mainTable . '.entity_id IN (?)', $notSyncedIds)
97+
->willReturnSelf();
98+
99+
$this->connection->expects($this->atLeastOnce())->method('select')->willReturn($select);
100+
$this->connection->expects($this->atLeastOnce())->method('fetchAll')->with($select)->willReturn($fetchResult);
101+
$this->connection->expects($this->atLeastOnce())->method('insertOnDuplicate')
102+
->with($this->gridTable, $fetchResult, array_keys($this->columns))
103+
->willReturn(array_count_values($notSyncedIds));
104+
105+
$this->grid->refreshBySchedule();
106+
}
107+
}

0 commit comments

Comments
 (0)