Skip to content

Commit fe89abf

Browse files
committed
ACP2E-3138: Cron job aggregate_sales_report_bestsellers_data cause deadlock if handles more that 100K rows
1 parent de4dfb8 commit fe89abf

File tree

2 files changed

+124
-2
lines changed

2 files changed

+124
-2
lines changed

app/code/Magento/Sales/Model/ResourceModel/Report/Bestsellers.php

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,67 @@ public function aggregate($from = null, $to = null)
169169
private function clearByDateRange($from = null, $to = null): void
170170
{
171171
$subSelect = $this->getRangeSubSelect($from, $to);
172-
$this->_clearTableByDateRange($this->getMainTable(), $from, $to, $subSelect);
172+
$this->clearTableRanges($this->getMainTable(), $from, $to, $subSelect);
173+
}
174+
175+
/**
176+
* Clear table by date range
177+
*
178+
* @param string $table
179+
* @param ?string $from
180+
* @param ?string $to
181+
* @param null|\Magento\Framework\DB\Select|string $subSelect
182+
* @return void
183+
*/
184+
private function clearTableRanges($table, $from = null, $to = null, $subSelect = null): void
185+
{
186+
if ($from === null && $to === null) {
187+
$this->_truncateTable($table);
188+
return;
189+
}
190+
191+
if ($subSelect !== null) {
192+
$dataRange = $this->getRange($subSelect);
193+
foreach ($dataRange as $date) {
194+
$deleteCondition = $this->getConnection()->prepareSqlCondition('period', ['like' => $date]);
195+
$this->getConnection()->delete($table, $deleteCondition);
196+
}
197+
return;
198+
} else {
199+
$condition = [];
200+
if ($from !== null) {
201+
$condition[] = $this->getConnection()->quoteInto('period >= ?', $from);
202+
}
203+
204+
if ($to !== null) {
205+
$condition[] = $this->getConnection()->quoteInto('period <= ?', $to);
206+
}
207+
$deleteCondition = implode(' AND ', $condition);
208+
}
209+
$this->getConnection()->delete($table, $deleteCondition);
210+
}
211+
212+
/**
213+
* Get dates range to clear the table
214+
*
215+
* @param \Magento\Framework\DB\Select $select
216+
* @return array|false
217+
*/
218+
private function getRange(\Magento\Framework\DB\Select $select)
219+
{
220+
$connection = $this->getConnection();
221+
try {
222+
$range = [];
223+
224+
$query = $connection->query($select);
225+
226+
while (true == ($date = $query->fetchColumn())) {
227+
$range[] = $date;
228+
}
229+
} catch (\Exception $e) {
230+
$range = false;
231+
}
232+
return $range;
173233
}
174234

175235
/**

app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Report/BestsellersTest.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public function testAggregatePerStoreCalculationWithInterval(): void
139139
->willReturn($periodExpr);
140140
$connection->expects($this->any())->method('select')->willReturn($select);
141141
$query = $this->createMock(\Zend_Db_Statement_Interface::class);
142-
$connection->expects($this->exactly(3))->method('query')->willReturn($query);
142+
$connection->expects($this->exactly(4))->method('query')->willReturn($query);
143143
$resource = $this->createMock(ResourceConnection::class);
144144
$resource->expects($this->any())
145145
->method('getConnection')
@@ -180,6 +180,68 @@ public function testAggregatePerStoreCalculationWithInterval(): void
180180
$this->report->aggregate($from, $to);
181181
}
182182

183+
public function testClearByDateRange()
184+
{
185+
$from = new \DateTime('yesterday');
186+
$to = new \DateTime();
187+
$periodExpr = 'DATE(DATE_ADD(`source_table`.`created_at`, INTERVAL -25200 SECOND))';
188+
189+
$connection = $this->createMock(AdapterInterface::class);
190+
$resource = $this->createMock(ResourceConnection::class);
191+
$resource->expects($this->any())
192+
->method('getConnection')
193+
->with($this->connectionName)
194+
->willReturn($connection);
195+
$this->context->expects($this->any())->method('getResources')->willReturn($resource);
196+
197+
$store = $this->createMock(StoreInterface::class);
198+
$store->expects($this->once())->method('getId')->willReturn(1);
199+
$this->storeManager->expects($this->once())->method('getStores')->with(true)->willReturn([$store]);
200+
201+
$select = $this->getMockBuilder(Select::class)
202+
->disableOriginalConstructor()
203+
->getMock();
204+
$select->expects($this->atLeastOnce())->method('from')->willReturnSelf();
205+
$select->expects($this->atLeastOnce())->method('joinInner')->willReturnSelf();
206+
$select->expects($this->atLeastOnce())->method('joinLeft')->willReturnSelf();
207+
$select->expects($this->atLeastOnce())->method('where')->willReturnSelf();
208+
$select->expects($this->atLeastOnce())->method('distinct')->willReturnSelf();
209+
$connection->expects($this->any())->method('select')->willReturn($select);
210+
211+
$date = $this->createMock(\DateTime::class);
212+
$date->expects($this->atLeastOnce())->method('format')->with('e');
213+
$this->time->expects($this->atLeastOnce())->method('scopeDate')->willReturn($date);
214+
215+
$flag = $this->createMock(Flag::class);
216+
$flag->expects($this->atLeastOnce())->method('setReportFlagCode')->willReturnSelf();
217+
$flag->expects($this->atLeastOnce())->method('unsetData')->willReturnSelf();
218+
$flag->expects($this->atLeastOnce())->method('loadSelf');
219+
$this->flagFactory->expects($this->atLeastOnce())->method('create')->willReturn($flag);
220+
221+
$query = $this->createMock(\Zend_Db_Statement_Interface::class);
222+
$query->method('fetchColumn')->willReturnOnConsecutiveCalls('date1', 'date2', false);
223+
$connection->expects($this->atLeastOnce())->method('query')->willReturn($query);
224+
$connection->expects($this->atLeastOnce())->method('getDatePartSql')->willReturn($periodExpr);
225+
226+
$connection->expects($this->exactly(2))->method('delete');
227+
228+
$this->report = new Bestsellers(
229+
$this->context,
230+
$this->logger,
231+
$this->time,
232+
$this->flagFactory,
233+
$this->validator,
234+
$this->date,
235+
$this->product,
236+
$this->helper,
237+
$this->connectionName,
238+
[],
239+
$this->storeManager
240+
);
241+
242+
$this->report->aggregate($from, $to);
243+
}
244+
183245
/**
184246
* @return void
185247
* @throws \Exception

0 commit comments

Comments
 (0)