[Cache] improve perf of pruning for fs-based adapters

This commit is contained in:
Nicolas Grekas 2019-10-09 10:18:57 +02:00
parent 1010910bab
commit 0302b1d8b0
4 changed files with 84 additions and 7 deletions

View File

@ -25,6 +25,7 @@ use Symfony\Component\Cache\Traits\FilesystemTrait;
class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface
{
use FilesystemTrait {
doClear as private doClearCache;
doSave as private doSaveCache;
doDelete as private doDeleteCache;
}
@ -41,6 +42,55 @@ class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements Prune
$this->init($namespace, $directory);
}
/**
* {@inheritdoc}
*/
protected function doClear($namespace)
{
$ok = $this->doClearCache($namespace);
if ('' !== $namespace) {
return $ok;
}
set_error_handler(static function () {});
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
try {
foreach ($this->scanHashDir($this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR) as $dir) {
if (rename($dir, $renamed = substr_replace($dir, bin2hex(random_bytes(4)), -8))) {
$dir = $renamed.\DIRECTORY_SEPARATOR;
} else {
$dir .= \DIRECTORY_SEPARATOR;
$renamed = null;
}
for ($i = 0; $i < 38; ++$i) {
if (!file_exists($dir.$chars[$i])) {
continue;
}
for ($j = 0; $j < 38; ++$j) {
if (!file_exists($d = $dir.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) {
continue;
}
foreach (scandir($d, SCANDIR_SORT_NONE) ?: [] as $link) {
if ('.' !== $link && '..' !== $link && (null !== $renamed || !realpath($d.\DIRECTORY_SEPARATOR.$link))) {
unlink($d.\DIRECTORY_SEPARATOR.$link);
}
}
null === $renamed ?: rmdir($d);
}
null === $renamed ?: rmdir($dir.$chars[$i]);
}
null === $renamed ?: rmdir($renamed);
}
} finally {
restore_error_handler();
}
return $ok;
}
/**
* {@inheritdoc}
*/
@ -111,13 +161,13 @@ class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements Prune
set_error_handler(static function () {});
try {
if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -1))) {
if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -9))) {
$tagFolder = $renamed.\DIRECTORY_SEPARATOR;
} else {
$renamed = null;
}
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($tagFolder, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME)) as $itemLink) {
foreach ($this->scanHashDir($tagFolder) as $itemLink) {
unlink(realpath($itemLink) ?: $itemLink);
unlink($itemLink);
}

View File

@ -26,7 +26,7 @@ trait FilesystemCommonTrait
private function init(string $namespace, ?string $directory)
{
if (!isset($directory[0])) {
$directory = sys_get_temp_dir().'/symfony-cache';
$directory = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'symfony-cache';
} else {
$directory = realpath($directory) ?: $directory;
}
@ -55,12 +55,12 @@ trait FilesystemCommonTrait
{
$ok = true;
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
foreach ($this->scanHashDir($this->directory) as $file) {
if ('' !== $namespace && 0 !== strpos($this->getFileKey($file), $namespace)) {
continue;
}
$ok = ($file->isDir() || $this->doUnlink($file) || !file_exists($file)) && $ok;
$ok = ($this->doUnlink($file) || !file_exists($file)) && $ok;
}
return $ok;
@ -123,6 +123,33 @@ trait FilesystemCommonTrait
return '';
}
private function scanHashDir(string $directory): \Generator
{
if (!file_exists($directory)) {
return;
}
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
for ($i = 0; $i < 38; ++$i) {
if (!file_exists($directory.$chars[$i])) {
continue;
}
for ($j = 0; $j < 38; ++$j) {
if (!file_exists($dir = $directory.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) {
continue;
}
foreach (@scandir($dir, SCANDIR_SORT_NONE) ?: [] as $file) {
if ('.' !== $file && '..' !== $file) {
yield $dir.\DIRECTORY_SEPARATOR.$file;
}
}
}
}
}
/**
* @internal
*/

View File

@ -33,7 +33,7 @@ trait FilesystemTrait
$time = time();
$pruned = true;
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
foreach ($this->scanHashDir($this->directory) as $file) {
if (!$h = @fopen($file, 'rb')) {
continue;
}

View File

@ -54,7 +54,7 @@ trait PhpFilesTrait
set_error_handler($this->includeHandler);
try {
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
foreach ($this->scanHashDir($this->directory) as $file) {
try {
if (\is_array($expiresAt = include $file)) {
$expiresAt = $expiresAt[0];