Skip to content

Commit 292769a

Browse files
Merge remote-tracking branch '39372/fix-for-issue-39369' into febcomprs
2 parents 6614e0a + b635942 commit 292769a

File tree

4 files changed

+121
-8
lines changed

4 files changed

+121
-8
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Backend\Cron;
9+
10+
use Magento\Framework\Lock\Backend\FileLock;
11+
use Magento\Framework\Lock\LockBackendFactory;
12+
use Psr\Log\LoggerInterface;
13+
14+
class CleanLocks
15+
{
16+
/**
17+
* @param LockBackendFactory $lockFactory
18+
* @param LoggerInterface $logger
19+
*/
20+
public function __construct(
21+
private readonly LockBackendFactory $lockFactory,
22+
private readonly LoggerInterface $logger,
23+
) {
24+
}
25+
26+
/**
27+
* Cron job to cleanup old locks
28+
*/
29+
public function execute(): void
30+
{
31+
$locker = $this->lockFactory->create();
32+
33+
if ($locker instanceof FileLock) {
34+
$numberOfLockFilesDeleted = $locker->cleanupOldLocks();
35+
36+
$this->logger->info(sprintf('Deleted %d old lock files', $numberOfLockFilesDeleted));
37+
}
38+
}
39+
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
<?xml version="1.0"?>
22
<!--
33
/**
4-
* Copyright © Magento, Inc. All rights reserved.
5-
* See COPYING.txt for license details.
4+
* Copyright 2015 Adobe
5+
* All Rights Reserved.
66
*/
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
99
<group id="default">
1010
<job name="backend_clean_cache" instance="Magento\Backend\Cron\CleanCache" method="execute">
1111
<schedule>30 2 * * *</schedule>
1212
</job>
13+
<job name="backend_clean_locks" instance="Magento\Backend\Cron\CleanLocks" method="execute">
14+
<schedule>20 2 * * *</schedule>
15+
</job>
1316
</group>
1417
</config>

dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2019 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -22,12 +22,16 @@ class FileLockTest extends \PHPUnit\Framework\TestCase
2222
*/
2323
private $objectManager;
2424

25+
/** @var string */
26+
private string $lockPath;
27+
2528
protected function setUp(): void
2629
{
30+
$this->lockPath = '/tmp/magento-test-locks';
2731
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
2832
$this->model = $this->objectManager->create(
2933
\Magento\Framework\Lock\Backend\FileLock::class,
30-
['path' => '/tmp']
34+
['path' => $this->lockPath]
3135
);
3236
}
3337

@@ -52,4 +56,28 @@ public function testUnlockWithoutExistingLock()
5256
$this->assertFalse($this->model->isLocked($name));
5357
$this->assertFalse($this->model->unlock($name));
5458
}
59+
60+
public function testCleanupOldFile()
61+
{
62+
$name = 'test_lock';
63+
64+
$this->assertTrue($this->model->lock($name));
65+
$this->assertTrue($this->model->unlock($name));
66+
67+
touch(sprintf('%s/%s', $this->lockPath, $name), strtotime('30 hours ago'));
68+
69+
$this->assertEquals(1, $this->model->cleanupOldLocks());
70+
}
71+
72+
public function testDontCleanupNewFile()
73+
{
74+
$name = 'test_lock';
75+
76+
$this->assertTrue($this->model->lock($name));
77+
$this->assertTrue($this->model->unlock($name));
78+
79+
touch(sprintf('%s/%s', $this->lockPath, $name), strtotime('1 hour ago'));
80+
81+
$this->assertEquals(0, $this->model->cleanupOldLocks());
82+
}
5583
}

lib/internal/Magento/Framework/Lock/Backend/FileLock.php

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2019 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

@@ -42,7 +42,7 @@ class FileLock implements LockManagerInterface
4242
/**
4343
* The mapping list of the path lock with the file resource
4444
*
45-
* @var array
45+
* @var array<string, resource>
4646
*/
4747
private $locks = [];
4848

@@ -105,6 +105,49 @@ public function lock(string $name, int $timeout = -1): bool
105105
return true;
106106
}
107107

108+
/**
109+
* Find lock files that haven't been touched in the last 24 hours, are 0 bytes and are unlocked, then delete those
110+
*/
111+
public function cleanupOldLocks(): int
112+
{
113+
if (!$this->fileDriver->isExists($this->path)) {
114+
return 0;
115+
}
116+
117+
$numberOfLocksDeleted = 0;
118+
$timestamp24HoursAgo = strtotime('24 hours ago');
119+
120+
$lockFiles = $this->fileDriver->readDirectory($this->path);
121+
foreach ($lockFiles as $lockFile) {
122+
if (!$this->fileDriver->isFile($lockFile)) {
123+
continue;
124+
}
125+
126+
$modifiedTimestamp = filemtime($lockFile);
127+
if ($timestamp24HoursAgo < $modifiedTimestamp) {
128+
continue;
129+
}
130+
131+
$filesize = filesize($lockFile);
132+
if ($filesize !== 0) {
133+
continue;
134+
}
135+
136+
if ($this->isLocked(basename($lockFile))) {
137+
continue;
138+
}
139+
140+
try {
141+
$this->fileDriver->deleteFile($lockFile);
142+
++$numberOfLocksDeleted;
143+
} catch (FileSystemException $exception) { // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
144+
// do nothing
145+
}
146+
}
147+
148+
return $numberOfLocksDeleted;
149+
}
150+
108151
/**
109152
* Checks if a lock exists by name
110153
*

0 commit comments

Comments
 (0)