Skip to content

Commit d64ecf3

Browse files
committed
Merge remote-tracking branch 'origin/ACP2E-4360' into PR_2025_12_11_muntianu
2 parents 9071164 + ed79fe6 commit d64ecf3

File tree

5 files changed

+122
-66
lines changed

5 files changed

+122
-66
lines changed

app/code/Magento/Sales/Model/GridAsyncInsert.php

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
*/
66
namespace Magento\Sales\Model;
77

8+
use Magento\Framework\Lock\LockManagerInterface;
9+
use Psr\Log\LoggerInterface;
10+
811
/**
912
* Sales entity grids indexing observer.
1013
*
@@ -27,23 +30,46 @@ class GridAsyncInsert
2730
*/
2831
protected $globalConfig;
2932

33+
/**
34+
* @var LockManagerInterface|null
35+
*/
36+
private $lockManager;
37+
38+
/**
39+
* @var LoggerInterface|null
40+
*/
41+
private $logger;
42+
43+
/**
44+
* @var string
45+
*/
46+
private $lockName;
47+
3048
/**
3149
* @param \Magento\Sales\Model\ResourceModel\GridInterface $entityGrid
3250
* @param \Magento\Framework\App\Config\ScopeConfigInterface $globalConfig
51+
* @param LockManagerInterface|null $lockManager
52+
* @param LoggerInterface|null $logger
53+
* @param string $lockName
3354
*/
3455
public function __construct(
3556
\Magento\Sales\Model\ResourceModel\GridInterface $entityGrid,
36-
\Magento\Framework\App\Config\ScopeConfigInterface $globalConfig
57+
\Magento\Framework\App\Config\ScopeConfigInterface $globalConfig,
58+
?LockManagerInterface $lockManager = null,
59+
?LoggerInterface $logger = null,
60+
string $lockName = ''
3761
) {
3862
$this->entityGrid = $entityGrid;
3963
$this->globalConfig = $globalConfig;
64+
$this->lockManager = $lockManager;
65+
$this->logger = $logger;
66+
$this->lockName = $lockName;
4067
}
4168

4269
/**
43-
* Handles asynchronous insertion of the new entity into
44-
* corresponding grid during cron job.
70+
* Handles asynchronous insertion of the new entity into corresponding grid during cron job.
4571
*
46-
* Also method is used in the next events:
72+
* Also, method is used in the next events:
4773
*
4874
* - config_data_dev_grid_async_indexing_disabled
4975
*
@@ -55,7 +81,23 @@ public function __construct(
5581
public function asyncInsert()
5682
{
5783
if ($this->globalConfig->getValue('dev/grid/async_indexing')) {
58-
$this->entityGrid->refreshBySchedule();
84+
if ($this->lockManager && $this->lockName !== '') {
85+
if (!$this->lockManager->lock($this->lockName, 0)) {
86+
if ($this->logger) {
87+
$this->logger->warning(
88+
sprintf('Grid async insert is locked: %s, skipping run', $this->lockName)
89+
);
90+
}
91+
return;
92+
}
93+
try {
94+
$this->entityGrid->refreshBySchedule();
95+
} finally {
96+
$this->lockManager->unlock($this->lockName);
97+
}
98+
} else {
99+
$this->entityGrid->refreshBySchedule();
100+
}
59101
}
60102
}
61103
}

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

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class Grid extends AbstractGrid
5454
/**
5555
* Order grid rows batch size
5656
*/
57-
const BATCH_SIZE = 100;
57+
public const BATCH_SIZE = 100;
5858

5959
/**
6060
* @param Context $context
@@ -130,24 +130,26 @@ public function refresh($value, $field = null)
130130
public function refreshBySchedule()
131131
{
132132
$lastUpdatedAt = null;
133-
$notSyncedIds = $this->notSyncedDataProvider->getIds($this->mainTableName, $this->gridTableName);
134-
foreach (array_chunk($notSyncedIds, self::BATCH_SIZE) as $bunch) {
135-
$select = $this->getGridOriginSelect()->where($this->mainTableName . '.entity_id IN (?)', $bunch);
136-
$fetchResult = $this->getConnection()->fetchAll($select);
137-
$this->getConnection()->insertOnDuplicate(
138-
$this->getTable($this->gridTableName),
139-
$fetchResult,
140-
array_keys($this->columns)
141-
);
142-
143-
$timestamps = array_column($fetchResult, 'updated_at');
144-
if ($timestamps) {
145-
$lastUpdatedAt = max(max($timestamps), $lastUpdatedAt);
133+
while (true) {
134+
$notSyncedIds = $this->notSyncedDataProvider->getIds($this->mainTableName, $this->gridTableName);
135+
if (empty($notSyncedIds)) {
136+
break;
137+
}
138+
foreach (array_chunk($notSyncedIds, self::BATCH_SIZE) as $bunch) {
139+
$select = $this->getGridOriginSelect()->where($this->mainTableName . '.entity_id IN (?)', $bunch);
140+
$fetchResult = $this->getConnection()->fetchAll($select);
141+
$this->getConnection()->insertOnDuplicate(
142+
$this->getTable($this->gridTableName),
143+
$fetchResult,
144+
array_keys($this->columns)
145+
);
146+
147+
$timestamps = array_column($fetchResult, 'updated_at');
148+
if ($timestamps) {
149+
$lastUpdatedAt = max(max($timestamps), $lastUpdatedAt);
150+
$this->lastUpdateTimeCache->save($this->gridTableName, $lastUpdatedAt);
151+
}
146152
}
147-
}
148-
149-
if ($lastUpdatedAt) {
150-
$this->lastUpdateTimeCache->save($this->gridTableName, $lastUpdatedAt);
151153
}
152154
}
153155

app/code/Magento/Sales/Test/Unit/Model/GridAsyncInsertTest.php

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,74 +8,78 @@
88
namespace Magento\Sales\Test\Unit\Model;
99

1010
use Magento\Framework\App\Config\ScopeConfigInterface;
11-
use Magento\Sales\Model\AbstractModel;
11+
use Magento\Framework\Lock\LockManagerInterface;
1212
use Magento\Sales\Model\GridAsyncInsert;
1313
use Magento\Sales\Model\ResourceModel\GridInterface;
1414
use PHPUnit\Framework\MockObject\MockObject;
1515
use PHPUnit\Framework\TestCase;
16+
use Psr\Log\LoggerInterface;
1617

1718
class GridAsyncInsertTest extends TestCase
1819
{
1920
/**
20-
* @var GridAsyncInsert
21+
* @var GridInterface|MockObject
2122
*/
22-
protected $unit;
23+
private $grid;
2324

2425
/**
25-
* @var GridInterface|MockObject
26+
* @var ScopeConfigInterface|MockObject
2627
*/
27-
protected $gridAggregatorMock;
28+
private $scopeConfig;
2829

2930
/**
30-
* @var AbstractModel|MockObject
31+
* @var LockManagerInterface|MockObject
3132
*/
32-
protected $salesModelMock;
33+
private $lockManager;
3334

3435
/**
35-
* @var ScopeConfigInterface|MockObject
36+
* @var LoggerInterface|MockObject
3637
*/
37-
protected $scopeConfigurationMock;
38+
private $logger;
3839

3940
protected function setUp(): void
4041
{
41-
$this->gridAggregatorMock = $this->getMockBuilder(GridInterface::class)
42-
->getMockForAbstractClass();
43-
$this->salesModelMock = $this->getMockBuilder(AbstractModel::class)
44-
->disableOriginalConstructor()
45-
->onlyMethods(
46-
[
47-
'getId'
48-
]
49-
)
50-
->getMockForAbstractClass();
51-
$this->scopeConfigurationMock = $this->getMockBuilder(ScopeConfigInterface::class)
52-
->getMockForAbstractClass();
53-
54-
$this->unit = new GridAsyncInsert(
55-
$this->gridAggregatorMock,
56-
$this->scopeConfigurationMock
57-
);
42+
$this->grid = $this->createMock(GridInterface::class);
43+
$this->scopeConfig = $this->createMock(ScopeConfigInterface::class);
44+
$this->lockManager = $this->createMock(LockManagerInterface::class);
45+
$this->logger = $this->createMock(LoggerInterface::class);
46+
$this->scopeConfig
47+
->method('getValue')
48+
->with('dev/grid/async_indexing')
49+
->willReturn('1');
5850
}
5951

60-
public function testAsyncInsert()
52+
public function testAsyncInsertSkipsWhenLocked(): void
6153
{
62-
$this->scopeConfigurationMock->expects($this->once())
63-
->method('getValue')
64-
->with('dev/grid/async_indexing', 'default', null)
65-
->willReturn(true);
66-
$this->gridAggregatorMock->expects($this->once())
67-
->method('refreshBySchedule');
68-
$this->unit->asyncInsert();
54+
$this->lockManager->expects($this->once())->method('lock')->with('lock_name_test', 0)->willReturn(false);
55+
$this->grid->expects($this->never())->method('refreshBySchedule');
56+
$this->lockManager->expects($this->never())->method('unlock');
57+
$this->logger->expects($this->once())->method('warning');
58+
59+
$model = new GridAsyncInsert(
60+
$this->grid,
61+
$this->scopeConfig,
62+
$this->lockManager,
63+
$this->logger,
64+
'lock_name_test'
65+
);
66+
$model->asyncInsert();
6967
}
7068

71-
public function testAsyncInsertDisabled()
69+
public function testAsyncInsertExecutesWhenLockAcquired(): void
7270
{
73-
$this->scopeConfigurationMock->expects($this->once())
74-
->method('getValue')
75-
->with('dev/grid/async_indexing', 'default', null)
76-
->willReturn(false);
77-
$this->gridAggregatorMock->expects($this->never())
78-
->method('refreshBySchedule');
79-
$this->unit->asyncInsert();
71+
$this->lockManager->expects($this->once())->method('lock')->with('lock_name_test', 0)->willReturn(true);
72+
$this->grid->expects($this->once())->method('refreshBySchedule');
73+
$this->lockManager->expects($this->once())->method('unlock')->with('lock_name_test');
74+
$this->logger->expects($this->never())->method('warning');
75+
76+
$model = new GridAsyncInsert(
77+
$this->grid,
78+
$this->scopeConfig,
79+
$this->lockManager,
80+
$this->logger,
81+
'lock_name_test'
82+
);
83+
$model->asyncInsert();
8084
}
8185
}

app/code/Magento/Sales/Test/Unit/Model/ResourceModel/GridTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public function testRefreshBySchedule()
103103

104104
$this->notSyncedDataProvider->expects($this->atLeastOnce())
105105
->method('getIds')
106-
->willReturn($notSyncedIds);
106+
->willReturnOnConsecutiveCalls($notSyncedIds, []);
107107
$select = $this->createMock(Select::class);
108108
$select->expects($this->atLeastOnce())
109109
->method('from')

app/code/Magento/Sales/etc/di.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,21 +267,29 @@
267267
<virtualType name="SalesOrderIndexGridAsyncInsert" type="Magento\Sales\Model\GridAsyncInsert">
268268
<arguments>
269269
<argument name="entityGrid" xsi:type="object">Magento\Sales\Model\ResourceModel\Order\Grid</argument>
270+
<argument name="lockManager" xsi:type="object">Magento\Framework\Lock\LockManagerInterface</argument>
271+
<argument name="lockName" xsi:type="string">grid_async_insert_sales_order</argument>
270272
</arguments>
271273
</virtualType>
272274
<virtualType name="SalesInvoiceIndexGridAsyncInsert" type="Magento\Sales\Model\GridAsyncInsert">
273275
<arguments>
274276
<argument name="entityGrid" xsi:type="object">Magento\Sales\Model\ResourceModel\Order\Invoice\Grid</argument>
277+
<argument name="lockManager" xsi:type="object">Magento\Framework\Lock\LockManagerInterface</argument>
278+
<argument name="lockName" xsi:type="string">grid_async_insert_sales_invoice</argument>
275279
</arguments>
276280
</virtualType>
277281
<virtualType name="SalesShipmentIndexGridAsyncInsert" type="Magento\Sales\Model\GridAsyncInsert">
278282
<arguments>
279283
<argument name="entityGrid" xsi:type="object">ShipmentGridAggregator</argument>
284+
<argument name="lockManager" xsi:type="object">Magento\Framework\Lock\LockManagerInterface</argument>
285+
<argument name="lockName" xsi:type="string">grid_async_insert_sales_shipment</argument>
280286
</arguments>
281287
</virtualType>
282288
<virtualType name="SalesCreditmemoIndexGridAsyncInsert" type="Magento\Sales\Model\GridAsyncInsert">
283289
<arguments>
284290
<argument name="entityGrid" xsi:type="object">CreditmemoGridAggregator</argument>
291+
<argument name="lockManager" xsi:type="object">Magento\Framework\Lock\LockManagerInterface</argument>
292+
<argument name="lockName" xsi:type="string">grid_async_insert_sales_creditmemo</argument>
285293
</arguments>
286294
</virtualType>
287295

0 commit comments

Comments
 (0)