feature #27549 [Cache] Unconditionally use PhpFilesAdapter for system pools (nicolas-grekas)
This PR was merged into the 4.2-dev branch.
Discussion
----------
[Cache] Unconditionally use PhpFilesAdapter for system pools
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | no
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
Now that we're about to leverage OPCache shared memory even for objects (see #27543), there's no reason anymore to use APCu for system caches. Let's remove some complexity here.
As a bonus, this makes system caches pruneable using the `cache:pool:prune` command.
Commits
-------
51381e530a
[Cache] Unconditionally use PhpFilesAdapter for system pools
This commit is contained in:
commit
d4f5d46b13
@ -1538,7 +1538,6 @@ class FrameworkExtension extends Extension
|
|||||||
{
|
{
|
||||||
$version = new Parameter('container.build_id');
|
$version = new Parameter('container.build_id');
|
||||||
$container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version);
|
$container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version);
|
||||||
$container->getDefinition('cache.adapter.system')->replaceArgument(2, $version);
|
|
||||||
$container->getDefinition('cache.adapter.filesystem')->replaceArgument(2, $config['directory']);
|
$container->getDefinition('cache.adapter.filesystem')->replaceArgument(2, $config['directory']);
|
||||||
|
|
||||||
if (isset($config['prefix_seed'])) {
|
if (isset($config['prefix_seed'])) {
|
||||||
|
@ -35,15 +35,15 @@
|
|||||||
<tag name="cache.pool" />
|
<tag name="cache.pool" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="cache.adapter.system" class="Symfony\Component\Cache\Adapter\AdapterInterface" abstract="true">
|
<service id="cache.adapter.system" class="Symfony\Component\Cache\Adapter\PhpFilesAdapter" abstract="true">
|
||||||
<factory class="Symfony\Component\Cache\Adapter\AbstractAdapter" method="createSystemCache" />
|
|
||||||
<tag name="cache.pool" clearer="cache.system_clearer" />
|
<tag name="cache.pool" clearer="cache.system_clearer" />
|
||||||
<tag name="monolog.logger" channel="cache" />
|
<tag name="monolog.logger" channel="cache" />
|
||||||
<argument /> <!-- namespace -->
|
<argument /> <!-- namespace -->
|
||||||
<argument>0</argument> <!-- default lifetime -->
|
<argument>0</argument> <!-- default lifetime -->
|
||||||
<argument /> <!-- version -->
|
|
||||||
<argument>%kernel.cache_dir%/pools</argument>
|
<argument>%kernel.cache_dir%/pools</argument>
|
||||||
<argument type="service" id="logger" on-invalid="ignore" />
|
<call method="setLogger">
|
||||||
|
<argument type="service" id="logger" on-invalid="ignore" />
|
||||||
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="cache.adapter.apcu" class="Symfony\Component\Cache\Adapter\ApcuAdapter" abstract="true">
|
<service id="cache.adapter.apcu" class="Symfony\Component\Cache\Adapter\ApcuAdapter" abstract="true">
|
||||||
|
@ -102,9 +102,13 @@ abstract class AbstractAdapter implements AdapterInterface, CacheInterface, Logg
|
|||||||
* @param LoggerInterface|null $logger
|
* @param LoggerInterface|null $logger
|
||||||
*
|
*
|
||||||
* @return AdapterInterface
|
* @return AdapterInterface
|
||||||
|
*
|
||||||
|
* @deprecated since Symfony 4.2
|
||||||
*/
|
*/
|
||||||
public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null)
|
public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null)
|
||||||
{
|
{
|
||||||
|
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2.', __CLASS__), E_USER_DEPRECATED);
|
||||||
|
|
||||||
if (null === self::$apcuSupported) {
|
if (null === self::$apcuSupported) {
|
||||||
self::$apcuSupported = ApcuAdapter::isSupported();
|
self::$apcuSupported = ApcuAdapter::isSupported();
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,6 @@ class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInte
|
|||||||
{
|
{
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
$this->pool = $fallbackPool;
|
$this->pool = $fallbackPool;
|
||||||
$this->zendDetectUnicode = ini_get('zend.detect_unicode');
|
|
||||||
$this->createCacheItem = \Closure::bind(
|
$this->createCacheItem = \Closure::bind(
|
||||||
function ($key, $value, $isHit) {
|
function ($key, $value, $isHit) {
|
||||||
$item = new CacheItem();
|
$item = new CacheItem();
|
||||||
|
@ -24,14 +24,11 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
|||||||
*/
|
*/
|
||||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null)
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null)
|
||||||
{
|
{
|
||||||
if (!static::isSupported()) {
|
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||||
throw new CacheException('OPcache is not enabled');
|
|
||||||
}
|
|
||||||
parent::__construct('', $defaultLifetime);
|
parent::__construct('', $defaultLifetime);
|
||||||
$this->init($namespace, $directory);
|
$this->init($namespace, $directory);
|
||||||
|
|
||||||
$e = new \Exception();
|
$e = new \Exception();
|
||||||
$this->includeHandler = function () use ($e) { throw $e; };
|
$this->includeHandler = function () use ($e) { throw $e; };
|
||||||
$this->zendDetectUnicode = ini_get('zend.detect_unicode');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ CHANGELOG
|
|||||||
* added `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
|
* added `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
|
||||||
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
|
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
|
||||||
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
|
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
|
||||||
|
* deprecated the `AbstractAdapter::createSystemCache()` method
|
||||||
|
|
||||||
3.4.0
|
3.4.0
|
||||||
-----
|
-----
|
||||||
|
@ -36,7 +36,6 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
|||||||
{
|
{
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
$this->pool = $fallbackPool;
|
$this->pool = $fallbackPool;
|
||||||
$this->zendDetectUnicode = ini_get('zend.detect_unicode');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,14 +24,11 @@ class PhpFilesCache extends AbstractCache implements PruneableInterface
|
|||||||
*/
|
*/
|
||||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null)
|
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null)
|
||||||
{
|
{
|
||||||
if (!static::isSupported()) {
|
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||||
throw new CacheException('OPcache is not enabled');
|
|
||||||
}
|
|
||||||
parent::__construct('', $defaultLifetime);
|
parent::__construct('', $defaultLifetime);
|
||||||
$this->init($namespace, $directory);
|
$this->init($namespace, $directory);
|
||||||
|
|
||||||
$e = new \Exception();
|
$e = new \Exception();
|
||||||
$this->includeHandler = function () use ($e) { throw $e; };
|
$this->includeHandler = function () use ($e) { throw $e; };
|
||||||
$this->zendDetectUnicode = ini_get('zend.detect_unicode');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class MaxIdLengthAdapterTest extends TestCase
|
|||||||
$cache->expects($this->exactly(2))
|
$cache->expects($this->exactly(2))
|
||||||
->method('doHave')
|
->method('doHave')
|
||||||
->withConsecutive(
|
->withConsecutive(
|
||||||
array($this->equalTo('----------:0GTYWa9n4ed8vqNlOT2iEr:')),
|
array($this->equalTo('----------:nWfzGiCgLczv3SSUzXL3kg:')),
|
||||||
array($this->equalTo('----------:---------------------------------------'))
|
array($this->equalTo('----------:---------------------------------------'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -25,10 +25,6 @@ class PhpFilesAdapterTest extends AdapterTestCase
|
|||||||
|
|
||||||
public function createCachePool()
|
public function createCachePool()
|
||||||
{
|
{
|
||||||
if (!PhpFilesAdapter::isSupported()) {
|
|
||||||
$this->markTestSkipped('OPcache extension is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PhpFilesAdapter('sf-cache');
|
return new PhpFilesAdapter('sf-cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,10 +25,6 @@ class PhpFilesCacheTest extends CacheTestCase
|
|||||||
|
|
||||||
public function createSimpleCache()
|
public function createSimpleCache()
|
||||||
{
|
{
|
||||||
if (!PhpFilesCache::isSupported()) {
|
|
||||||
$this->markTestSkipped('OPcache extension is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PhpFilesCache('sf-cache');
|
return new PhpFilesCache('sf-cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ trait AbstractTrait
|
|||||||
private $namespaceVersion = '';
|
private $namespaceVersion = '';
|
||||||
private $versioningIsEnabled = false;
|
private $versioningIsEnabled = false;
|
||||||
private $deferred = array();
|
private $deferred = array();
|
||||||
|
private $ids = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int|null The maximum length to enforce for identifiers or null when no limit applies
|
* @var int|null The maximum length to enforce for identifiers or null when no limit applies
|
||||||
@ -198,6 +199,7 @@ trait AbstractTrait
|
|||||||
$this->commit();
|
$this->commit();
|
||||||
}
|
}
|
||||||
$this->namespaceVersion = '';
|
$this->namespaceVersion = '';
|
||||||
|
$this->ids = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,8 +231,6 @@ trait AbstractTrait
|
|||||||
|
|
||||||
private function getId($key)
|
private function getId($key)
|
||||||
{
|
{
|
||||||
CacheItem::validateKey($key);
|
|
||||||
|
|
||||||
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
|
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
|
||||||
$this->namespaceVersion = '1:';
|
$this->namespaceVersion = '1:';
|
||||||
foreach ($this->doFetch(array('@'.$this->namespace)) as $v) {
|
foreach ($this->doFetch(array('@'.$this->namespace)) as $v) {
|
||||||
@ -238,11 +238,19 @@ trait AbstractTrait
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (\is_string($key) && isset($this->ids[$key])) {
|
||||||
|
return $this->namespace.$this->namespaceVersion.$this->ids[$key];
|
||||||
|
}
|
||||||
|
CacheItem::validateKey($key);
|
||||||
|
$this->ids[$key] = $key;
|
||||||
|
|
||||||
if (null === $this->maxIdLength) {
|
if (null === $this->maxIdLength) {
|
||||||
return $this->namespace.$this->namespaceVersion.$key;
|
return $this->namespace.$this->namespaceVersion.$key;
|
||||||
}
|
}
|
||||||
if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
|
if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
|
||||||
$id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), ':', -22);
|
// Use MD5 to favor speed over security, which is not an issue here
|
||||||
|
$this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, true)), ':', -2);
|
||||||
|
$id = $this->namespace.$this->namespaceVersion.$id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $id;
|
return $id;
|
||||||
|
@ -56,7 +56,7 @@ trait FilesystemCommonTrait
|
|||||||
$ok = true;
|
$ok = true;
|
||||||
|
|
||||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
|
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
|
||||||
$ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok;
|
$ok = ($file->isDir() || $this->doUnlink($file) || !file_exists($file)) && $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ok;
|
return $ok;
|
||||||
@ -71,12 +71,17 @@ trait FilesystemCommonTrait
|
|||||||
|
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
$file = $this->getFile($id);
|
$file = $this->getFile($id);
|
||||||
$ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok;
|
$ok = (!file_exists($file) || $this->doUnlink($file) || !file_exists($file)) && $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function doUnlink($file)
|
||||||
|
{
|
||||||
|
return @unlink($file);
|
||||||
|
}
|
||||||
|
|
||||||
private function write($file, $data, $expiresAt = null)
|
private function write($file, $data, $expiresAt = null)
|
||||||
{
|
{
|
||||||
set_error_handler(__CLASS__.'::throwError');
|
set_error_handler(__CLASS__.'::throwError');
|
||||||
@ -98,7 +103,8 @@ trait FilesystemCommonTrait
|
|||||||
|
|
||||||
private function getFile($id, $mkdir = false)
|
private function getFile($id, $mkdir = false)
|
||||||
{
|
{
|
||||||
$hash = str_replace('/', '-', base64_encode(hash('sha256', static::class.$id, true)));
|
// Use MD5 to favor speed over security, which is not an issue here
|
||||||
|
$hash = str_replace('/', '-', base64_encode(hash('md5', static::class.$id, true)));
|
||||||
$dir = $this->directory.strtoupper($hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR);
|
$dir = $this->directory.strtoupper($hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
if ($mkdir && !file_exists($dir)) {
|
if ($mkdir && !file_exists($dir)) {
|
||||||
|
@ -26,7 +26,6 @@ trait PhpArrayTrait
|
|||||||
|
|
||||||
private $file;
|
private $file;
|
||||||
private $values;
|
private $values;
|
||||||
private $zendDetectUnicode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store an array of cached values.
|
* Store an array of cached values.
|
||||||
@ -98,7 +97,6 @@ EOF;
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dump .= "\n);\n";
|
$dump .= "\n);\n";
|
||||||
$dump = str_replace("' . \"\\0\" . '", "\0", $dump);
|
|
||||||
|
|
||||||
$tmpFile = uniqid($this->file, true);
|
$tmpFile = uniqid($this->file, true);
|
||||||
|
|
||||||
@ -128,15 +126,6 @@ EOF;
|
|||||||
*/
|
*/
|
||||||
private function initialize()
|
private function initialize()
|
||||||
{
|
{
|
||||||
if ($this->zendDetectUnicode) {
|
$this->values = file_exists($this->file) ? (include $this->file ?: array()) : array();
|
||||||
$zmb = ini_set('zend.detect_unicode', 0);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$this->values = file_exists($this->file) ? (include $this->file ?: array()) : array();
|
|
||||||
} finally {
|
|
||||||
if ($this->zendDetectUnicode) {
|
|
||||||
ini_set('zend.detect_unicode', $zmb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,14 @@ trait PhpFilesTrait
|
|||||||
use FilesystemCommonTrait;
|
use FilesystemCommonTrait;
|
||||||
|
|
||||||
private $includeHandler;
|
private $includeHandler;
|
||||||
private $zendDetectUnicode;
|
|
||||||
|
private static $startTime;
|
||||||
|
|
||||||
public static function isSupported()
|
public static function isSupported()
|
||||||
{
|
{
|
||||||
return function_exists('opcache_invalidate') && ini_get('opcache.enable');
|
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
|
||||||
|
|
||||||
|
return \function_exists('opcache_invalidate') && ini_get('opcache.enable') && ('cli' !== \PHP_SAPI || ini_get('opcache.enable_cli'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +43,6 @@ trait PhpFilesTrait
|
|||||||
{
|
{
|
||||||
$time = time();
|
$time = time();
|
||||||
$pruned = true;
|
$pruned = true;
|
||||||
$allowCompile = 'cli' !== PHP_SAPI || ini_get('opcache.enable_cli');
|
|
||||||
|
|
||||||
set_error_handler($this->includeHandler);
|
set_error_handler($this->includeHandler);
|
||||||
try {
|
try {
|
||||||
@ -48,11 +50,7 @@ trait PhpFilesTrait
|
|||||||
list($expiresAt) = include $file;
|
list($expiresAt) = include $file;
|
||||||
|
|
||||||
if ($time >= $expiresAt) {
|
if ($time >= $expiresAt) {
|
||||||
$pruned = @unlink($file) && !file_exists($file) && $pruned;
|
$pruned = $this->doUnlink($file) && !file_exists($file) && $pruned;
|
||||||
|
|
||||||
if ($allowCompile) {
|
|
||||||
@opcache_invalidate($file, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -70,9 +68,6 @@ trait PhpFilesTrait
|
|||||||
$values = array();
|
$values = array();
|
||||||
$now = time();
|
$now = time();
|
||||||
|
|
||||||
if ($this->zendDetectUnicode) {
|
|
||||||
$zmb = ini_set('zend.detect_unicode', 0);
|
|
||||||
}
|
|
||||||
set_error_handler($this->includeHandler);
|
set_error_handler($this->includeHandler);
|
||||||
try {
|
try {
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
@ -88,9 +83,6 @@ trait PhpFilesTrait
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
if ($this->zendDetectUnicode) {
|
|
||||||
ini_set('zend.detect_unicode', $zmb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($values as $id => $value) {
|
foreach ($values as $id => $value) {
|
||||||
@ -119,7 +111,7 @@ trait PhpFilesTrait
|
|||||||
{
|
{
|
||||||
$ok = true;
|
$ok = true;
|
||||||
$data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, '');
|
$data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, '');
|
||||||
$allowCompile = 'cli' !== PHP_SAPI || ini_get('opcache.enable_cli');
|
$allowCompile = self::isSupported();
|
||||||
|
|
||||||
foreach ($values as $key => $value) {
|
foreach ($values as $key => $value) {
|
||||||
if (null === $value || \is_object($value)) {
|
if (null === $value || \is_object($value)) {
|
||||||
@ -142,7 +134,8 @@ trait PhpFilesTrait
|
|||||||
|
|
||||||
$data[1] = $value;
|
$data[1] = $value;
|
||||||
$file = $this->getFile($key, true);
|
$file = $this->getFile($key, true);
|
||||||
$ok = $this->write($file, '<?php return '.var_export($data, true).';') && $ok;
|
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
|
||||||
|
$ok = $this->write($file, '<?php return '.var_export($data, true).';', self::$startTime - 10) && $ok;
|
||||||
|
|
||||||
if ($allowCompile) {
|
if ($allowCompile) {
|
||||||
@opcache_invalidate($file, true);
|
@opcache_invalidate($file, true);
|
||||||
@ -155,4 +148,13 @@ trait PhpFilesTrait
|
|||||||
|
|
||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function doUnlink($file)
|
||||||
|
{
|
||||||
|
if (self::isSupported()) {
|
||||||
|
@opcache_invalidate($file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return @unlink($file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user