/
githubmirror
/
magento2
Обзор
Документация
Войти
/
githubmirror
/
magento2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
2.4-develop
lib/internal/Magento/Framework/Lock/Backend/FileLock.php
245 строк
7 KB
Oleksandr Iegorov
ACP2E-5027: Indexers stuck in self-lock loop with 'use_application_lock' and 'file' provider
11 июн 2026, 23:41
11 июн 2026, 23:41
9947acb
Код
Авторство
О чём код?
<?php /** * Copyright 2019 Adobe * All Rights Reserved. */ declare(strict_types=1); namespace Magento\Framework\Lock\Backend; use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Exception\RuntimeException; use Magento\Framework\Filesystem\Driver\File as FileDriver; use Magento\Framework\Lock\LockManagerInterface; use Magento\Framework\Phrase; /** * LockManager using the file system for locks */ class FileLock implements LockManagerInterface { /** * The file driver instance * * @var FileDriver */ private $fileDriver; /** * The path to the locks storage folder * * @var string */ private $path; /** * How many microseconds to wait before re-try to acquire a lock * * @var int */ private $sleepCycle = 100000; /** * The mapping list of the path lock with the file resource * * @var array<string, resource> */ private $locks = []; /** * @param FileDriver $fileDriver The file driver * @param string $path The path to the locks storage folder * @throws RuntimeException Throws RuntimeException if $path is empty * or cannot create the directory for locks */ public function __construct(FileDriver $fileDriver, string $path) { if (!$path) { throw new RuntimeException(new Phrase('The path needs to be a non-empty string.')); } $this->fileDriver = $fileDriver; $this->path = rtrim($path, '/') . '/'; try { if (!$this->fileDriver->isExists($this->path)) { $this->fileDriver->createDirectory($this->path); } } catch (FileSystemException $exception) { throw new RuntimeException( new Phrase('Cannot create the directory for locks: %1', [$this->path]), $exception ); } } /** * Acquires a lock by name * * @param string $name The lock name * @param int $timeout Timeout in seconds. A negative timeout value means infinite timeout * @return bool Returns true if the lock is acquired, otherwise returns false * @throws RuntimeException Throws RuntimeException if cannot acquires the lock because FS problems */ public function lock(string $name, int $timeout = -1): bool { $lockFile = $this->getLockPath($name); // If already held by this process, don't open a second fd on the same file. if (isset($this->locks[$lockFile])) { return true; } try { $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+'); $skipDeadline = $timeout < 0; $deadline = microtime(true) + $timeout; while (!$this->tryToLock($fileResource)) { if (!$skipDeadline && $deadline <= microtime(true)) { $this->tryToUnlock($fileResource); $this->fileDriver->fileClose($fileResource); return false; } usleep($this->sleepCycle); } } catch (FileSystemException $exception) { throw new RuntimeException(new Phrase('Cannot acquire a lock.'), $exception); } $this->locks[$lockFile] = $fileResource; return true; } /** * Find lock files that haven't been touched in the last 24 hours, are 0 bytes and are unlocked, then 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; } $filesize = filesize($lockFile); if ($filesize !== 0) { 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 * * @param string $name The lock name * @return bool Returns true if the lock exists, otherwise returns false * @throws RuntimeException Throws RuntimeException if cannot check that the lock exists */ public function isLocked(string $name): bool { $lockFile = $this->getLockPath($name); $result = false; try { if ($this->fileDriver->isExists($lockFile)) { $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+'); if ($this->tryToLock($fileResource)) { $result = false; } else { $result = true; } $this->tryToUnlock($fileResource); $this->fileDriver->fileClose($fileResource); } } catch (FileSystemException $exception) { throw new RuntimeException(new Phrase('Cannot verify that the lock exists.'), $exception); } return $result; } /** * Remove the lock by name * * @param string $name The lock name * @return bool If the lock is removed returns true, otherwise returns false */ public function unlock(string $name): bool { $lockFile = $this->getLockPath($name); if (isset($this->locks[$lockFile]) && $this->tryToUnlock($this->locks[$lockFile])) { unset($this->locks[$lockFile]); return true; } return false; } /** * Returns the full path to the lock file by name * * @param string $name The lock name * @return string The path to the lock file */ private function getLockPath(string $name): string { return $this->path . $name; } /** * Tries to lock a file resource * * @param resource $resource The file resource * @return bool If the lock is acquired returns true, otherwise returns false */ private function tryToLock($resource): bool { try { return $this->fileDriver->fileLock($resource, LOCK_EX | LOCK_NB); } catch (FileSystemException $exception) { return false; } } /** * Tries to unlock a file resource * * @param resource $resource The file resource * @return bool If the lock is removed returns true, otherwise returns false */ private function tryToUnlock($resource): bool { try { return $this->fileDriver->fileLock($resource, LOCK_UN | LOCK_NB); } catch (FileSystemException $exception) { return false; } } }