From 3ca8fa0e00038f77c1666131f53eda56ef302ef7 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sat, 20 Apr 2019 17:37:57 +0200 Subject: [PATCH] Support for APCu caching --- .travis.yml | 1 + src/Core/Cache/APCuCache.php | 154 ++++++++++++++++++ src/Core/Cache/AbstractCacheDriver.php | 15 +- src/Factory/CacheDriverFactory.php | 5 + tests/src/Core/Cache/APCuCacheDriverTest.php | 29 ++++ .../src/Core/Lock/APCuCacheLockDriverTest.php | 15 ++ 6 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 src/Core/Cache/APCuCache.php create mode 100644 tests/src/Core/Cache/APCuCacheDriverTest.php create mode 100644 tests/src/Core/Lock/APCuCacheLockDriverTest.php diff --git a/.travis.yml b/.travis.yml index c66457325f..a241dff0fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,4 +21,5 @@ before_script: - mysql -utravis test < database.sql - echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - echo "apc.enable_cli = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini after_success: bash <(curl -s https://codecov.io/bash) diff --git a/src/Core/Cache/APCuCache.php b/src/Core/Cache/APCuCache.php new file mode 100644 index 0000000000..f658424cdc --- /dev/null +++ b/src/Core/Cache/APCuCache.php @@ -0,0 +1,154 @@ + + */ +class APCuCache extends AbstractCacheDriver implements IMemoryCacheDriver +{ + use TraitCompareSet; + use TraitCompareDelete; + + /** + * @throws Exception + */ + public function __construct() + { + if (!self::isAvailable()) { + throw new Exception('APCu is not available.'); + } + } + + /** + * (@inheritdoc) + */ + public function getAllKeys($prefix = null) + { + $ns = $this->getCacheKey($prefix); + $ns = preg_quote($ns, '/'); + + if (class_exists('\APCIterator')) { + $iterator = new \APCIterator('user', '/^' . $ns. '/', APC_ITER_KEY); + } else { + $iterator = new \APCUIterator('/^' . $ns . '/', APC_ITER_KEY); + } + + $keys = []; + foreach ($iterator as $item) { + array_push($keys, $item['key']); + } + + return $this->getOriginalKeys($keys); + } + + /** + * (@inheritdoc) + */ + public function get($key) + { + $return = null; + $cachekey = $this->getCacheKey($key); + + $cached = apcu_fetch($cachekey, $success); + if (!$success) { + return null; + } + + $value = unserialize($cached); + + // Only return a value if the serialized value is valid. + // We also check if the db entry is a serialized + // boolean 'false' value (which we want to return). + if ($cached === serialize(false) || $value !== false) { + $return = $value; + } + + return $return; + } + + /** + * (@inheritdoc) + */ + public function set($key, $value, $ttl = Cache::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + + $cached = serialize($value); + + if ($ttl > 0) { + return apcu_store( + $cachekey, + $cached, + $ttl + ); + } else { + return apcu_store( + $cachekey, + $cached + ); + } + } + + /** + * (@inheritdoc) + */ + public function delete($key) + { + $cachekey = $this->getCacheKey($key); + return apcu_delete($cachekey); + } + + /** + * (@inheritdoc) + */ + public function clear($outdated = true) + { + if ($outdated) { + return true; + } else { + $prefix = $this->getPrefix(); + $prefix = preg_quote($prefix, '/'); + + if (class_exists('\APCIterator')) { + $iterator = new \APCIterator('user', '/^' . $prefix . '/', APC_ITER_KEY); + } else { + $iterator = new \APCUIterator('/^' . $prefix . '/', APC_ITER_KEY); + } + + return apcu_delete($iterator); + } + } + + /** + * (@inheritdoc) + */ + public function add($key, $value, $ttl = Cache::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + $cached = serialize($value); + + return apcu_add($cachekey, $cached); + } + + public static function isAvailable() + { + if (!extension_loaded('apcu')) { + return false; + } elseif (!ini_get('apc.enabled') && !ini_get('apc.enable_cli')) { + return false; + } elseif ( + version_compare(phpversion('apc') ?: '0.0.0', '4.0.6') === -1 && + version_compare(phpversion('apcu') ?: '0.0.0', '5.1.0') === -1 + ) { + return false; + } + + return true; + } +} diff --git a/src/Core/Cache/AbstractCacheDriver.php b/src/Core/Cache/AbstractCacheDriver.php index 13993385ad..ee67eb7bce 100644 --- a/src/Core/Cache/AbstractCacheDriver.php +++ b/src/Core/Cache/AbstractCacheDriver.php @@ -13,6 +13,18 @@ use Friendica\BaseObject; */ abstract class AbstractCacheDriver extends BaseObject { + /** + * Returns the prefix (to avoid namespace conflicts) + * + * @return string + * @throws \Exception + */ + protected function getPrefix() + { + // We fetch with the hostname as key to avoid problems with other applications + return self::getApp()->getHostName(); + } + /** * @param string $key The original key * @return string The cache key used for the cache @@ -20,8 +32,7 @@ abstract class AbstractCacheDriver extends BaseObject */ protected function getCacheKey($key) { - // We fetch with the hostname as key to avoid problems with other applications - return self::getApp()->getHostName() . ":" . $key; + return $this->getPrefix() . ":" . $key; } /** diff --git a/src/Factory/CacheDriverFactory.php b/src/Factory/CacheDriverFactory.php index f802d73fa6..390534a70a 100644 --- a/src/Factory/CacheDriverFactory.php +++ b/src/Factory/CacheDriverFactory.php @@ -45,6 +45,11 @@ class CacheDriverFactory return new Cache\RedisCacheDriver($redis_host, $redis_port, $redis_db, $redis_pw); break; + + case 'apcu': + return new Cache\APCuCache(); + break; + default: return new Cache\DatabaseCacheDriver(); } diff --git a/tests/src/Core/Cache/APCuCacheDriverTest.php b/tests/src/Core/Cache/APCuCacheDriverTest.php new file mode 100644 index 0000000000..eac00f559a --- /dev/null +++ b/tests/src/Core/Cache/APCuCacheDriverTest.php @@ -0,0 +1,29 @@ +markTestSkipped('APCu is not available'); + } + + parent::setUp(); + } + + protected function getInstance() + { + $this->cache = new APCuCache(); + return $this->cache; + } + + public function tearDown() + { + $this->cache->clear(false); + parent::tearDown(); + } +} diff --git a/tests/src/Core/Lock/APCuCacheLockDriverTest.php b/tests/src/Core/Lock/APCuCacheLockDriverTest.php new file mode 100644 index 0000000000..7b0137e368 --- /dev/null +++ b/tests/src/Core/Lock/APCuCacheLockDriverTest.php @@ -0,0 +1,15 @@ +