Skip to content

Commit

Permalink
Introduce new cronjob to regularly cleanup outdated lock files if fil…
Browse files Browse the repository at this point in the history
…e based lock provider is being used.
  • Loading branch information
hostep committed Nov 15, 2024
1 parent 0ba9eef commit 1f340c1
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 6 deletions.
37 changes: 37 additions & 0 deletions app/code/Magento/Backend/Cron/CleanLocks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Copyright 2024 Adobe
* All Rights Reserved.
*/
namespace Magento\Backend\Cron;

use Magento\Framework\Lock\Backend\FileLock;
use Magento\Framework\Lock\LockBackendFactory;
use Psr\Log\LoggerInterface;

class CleanLocks
{
/**
* @param LockBackendFactory $lockFactory
* @param LoggerInterface $logger
*/
public function __construct(
private readonly LockBackendFactory $lockFactory,
private readonly LoggerInterface $logger,
) {
}

/**
* Cron job to cleanup old locks
*/
public function execute(): void
{
$locker = $this->lockFactory->create();

if ($locker instanceof FileLock) {
$numberOfLockFilesDeleted = $locker->cleanupOldLocks();

$this->logger->info(sprintf('Deleted %d old lock files', $numberOfLockFilesDeleted));
}
}
}
3 changes: 3 additions & 0 deletions app/code/Magento/Backend/etc/crontab.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
<job name="backend_clean_cache" instance="Magento\Backend\Cron\CleanCache" method="execute">
<schedule>30 2 * * *</schedule>
</job>
<job name="backend_clean_locks" instance="Magento\Backend\Cron\CleanLocks" method="execute">
<schedule>20 2 * * *</schedule>
</job>
</group>
</config>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
* Copyright 2019 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);

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

/** @var string */
private string $lockPath;

protected function setUp(): void
{
$this->lockPath = '/tmp/magento-test-locks';
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$this->model = $this->objectManager->create(
\Magento\Framework\Lock\Backend\FileLock::class,
['path' => '/tmp']
['path' => $this->lockPath]
);
}

Expand All @@ -52,4 +56,28 @@ public function testUnlockWithoutExistingLock()
$this->assertFalse($this->model->isLocked($name));
$this->assertFalse($this->model->unlock($name));
}

public function testCleanupOldFile()
{
$name = 'test_lock';

$this->assertTrue($this->model->lock($name));
$this->assertTrue($this->model->unlock($name));

touch(sprintf('%s/%s', $this->lockPath, $name), strtotime('30 hours ago'));

$this->assertEquals(1, $this->model->cleanupOldLocks());
}

public function testDontCleanupNewFile()
{
$name = 'test_lock';

$this->assertTrue($this->model->lock($name));
$this->assertTrue($this->model->unlock($name));

touch(sprintf('%s/%s', $this->lockPath, $name), strtotime('1 hour ago'));

$this->assertEquals(0, $this->model->cleanupOldLocks());
}
}
44 changes: 41 additions & 3 deletions lib/internal/Magento/Framework/Lock/Backend/FileLock.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
* Copyright 2019 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);

Expand Down Expand Up @@ -42,7 +42,7 @@ class FileLock implements LockManagerInterface
/**
* The mapping list of the path lock with the file resource
*
* @var array
* @var array<string, resource>
*/
private $locks = [];

Expand Down Expand Up @@ -105,6 +105,44 @@ public function lock(string $name, int $timeout = -1): bool
return true;
}

/**
* Find lock files that haven't been touched in the last 24 hours and are unlocked, and delete those
*/
public function cleanupOldLocks(): int
{
if (!$this->fileDriver->isExists($this->path)) {
return 0;
}

$numberOfLocksDeleted = 0;
$timestamp24HoursAgo = strtotime('24 hours ago');

$lockFiles = $this->fileDriver->readDirectory($this->path);
foreach ($lockFiles as $lockFile) {
if (!$this->fileDriver->isFile($lockFile)) {
continue;
}

$modifiedTimestamp = filemtime($lockFile);
if ($timestamp24HoursAgo < $modifiedTimestamp) {
continue;
}

if ($this->isLocked(basename($lockFile))) {
continue;
}

try {
$this->fileDriver->deleteFile($lockFile);
++$numberOfLocksDeleted;
} catch (FileSystemException $exception) { // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
// do nothing
}
}

return $numberOfLocksDeleted;
}

/**
* Checks if a lock exists by name
*
Expand Down

0 comments on commit 1f340c1

Please sign in to comment.