Adding possibility to use a different cache-backend for locking and caching

- Renaming *LockDriver to *Lock since it isn't a "driver" anymore
This commit is contained in:
Philipp Holzer 2019-08-04 15:42:39 +02:00
parent 86bf2ee45a
commit 34e4968c06
No known key found for this signature in database
GPG key ID: D8365C3D36B77D90
19 changed files with 149 additions and 64 deletions

View file

@ -5,8 +5,6 @@ namespace Friendica\Console;
use Asika\SimpleConsole\CommandArgsException;
use Friendica\App;
use Friendica\Core\Cache\ICache;
use Friendica\Core\Config\Configuration;
use Friendica\Factory\CacheDriverFactory;
use RuntimeException;
/**
@ -71,13 +69,12 @@ HELP;
return $help;
}
public function __construct(App\Mode $appMode, Configuration $config, ICache $cache, array $argv = null)
public function __construct(App\Mode $appMode, ICache $cache, array $argv = null)
{
parent::__construct($argv);
$this->appMode = $appMode;
$this->cache = $cache;
$this->cacheDriverName = $config->get('system', 'cache_driver', CacheDriverFactory::DEFAULT_DRIVER);
$this->cache = $cache;
}
protected function doExecute()
@ -94,7 +91,7 @@ HELP;
}
if ($this->getOption('v')) {
$this->out('Cache Driver Name: ' . $this->cacheDriverName);
$this->out('Cache Driver Name: ' . (string)$this->cache);
$this->out('Cache Driver Class: ' . get_class($this->cache));
}
@ -127,7 +124,7 @@ HELP;
private function executeList()
{
$prefix = $this->getArgument(1);
$keys = $this->cache->getAllKeys($prefix);
$keys = $this->cache->getAllKeys($prefix);
if (empty($prefix)) {
$this->out('Listing all cache keys:');
@ -147,7 +144,7 @@ HELP;
private function executeGet()
{
if (count($this->args) >= 2) {
$key = $this->getArgument(1);
$key = $this->getArgument(1);
$value = $this->cache->get($key);
$this->out("{$key} => " . var_export($value, true));
@ -159,8 +156,8 @@ HELP;
private function executeSet()
{
if (count($this->args) >= 3) {
$key = $this->getArgument(1);
$value = $this->getArgument(2);
$key = $this->getArgument(1);
$value = $this->getArgument(2);
$duration = intval($this->getArgument(3, ICache::FIVE_MINUTES));
if (is_array($this->cache->get($key))) {

View file

@ -153,4 +153,9 @@ class APCuCache extends AbstractCache implements IMemoryCache
return true;
}
public function __toString()
{
return self::TYPE_APCU;
}
}

View file

@ -11,6 +11,20 @@ namespace Friendica\Core\Cache;
*/
abstract class AbstractCache implements ICache
{
const TYPE_APCU = 'apcu';
const TYPE_ARRAY = 'array';
const TYPE_DATABASE = 'database';
const TYPE_MEMCACHE = 'memcache';
const TYPE_MEMCACHED = 'memcached';
const TYPE_REDIS = 'redis';
/**
* Force each Cache implementation to define the ToString method
*
* @return string
*/
abstract function __toString();
/**
* @var string The hostname
*/

View file

@ -92,4 +92,9 @@ class ArrayCache extends AbstractCache implements IMemoryCache
return false;
}
}
public function __toString()
{
return self::TYPE_ARRAY;
}
}

View file

@ -110,4 +110,9 @@ class DatabaseCache extends AbstractCache implements ICache
return $this->dba->delete('cache', ['`k` IS NOT NULL ']);
}
}
public function __toString()
{
return self::TYPE_DATABASE;
}
}

View file

@ -148,4 +148,9 @@ class MemcacheCache extends AbstractCache implements IMemoryCache
$cachekey = $this->getCacheKey($key);
return $this->memcache->add($cachekey, serialize($value), MEMCACHE_COMPRESSED, $ttl);
}
public function __toString()
{
return self::TYPE_MEMCACHE;
}
}

View file

@ -151,4 +151,9 @@ class MemcachedCache extends AbstractCache implements IMemoryCache
$cachekey = $this->getCacheKey($key);
return $this->memcached->add($cachekey, $value, $ttl);
}
public function __toString()
{
return self::TYPE_MEMCACHED;
}
}

View file

@ -152,4 +152,9 @@ class ProfilerCache implements ICache, IMemoryCache
return false;
}
}
public function __toString()
{
return (string)$this->cache . ' (with profiler)';
}
}

View file

@ -192,4 +192,9 @@ class RedisCache extends AbstractCache implements IMemoryCache
$this->redis->unwatch();
return false;
}
public function __toString()
{
return self::TYPE_REDIS;
}
}

View file

@ -5,7 +5,7 @@ namespace Friendica\Core\Lock;
use Friendica\Core\Cache;
use Friendica\Core\Cache\IMemoryCache;
class CacheLockDriver extends AbstractLock
class CacheLock extends AbstractLock
{
/**
* @var \Friendica\Core\Cache\ICache;
@ -13,7 +13,7 @@ class CacheLockDriver extends AbstractLock
private $cache;
/**
* CacheLockDriver constructor.
* CacheLock constructor.
*
* @param IMemoryCache $cache The CacheDriver for this type of lock
*/

View file

@ -11,18 +11,18 @@ use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
* Class CacheDriverFactory
* Class CacheFactory
*
* @package Friendica\Core\Cache
*
* A basic class to generate a CacheDriver
*/
class CacheDriverFactory
class CacheFactory
{
/**
* @var string The default driver for caching
* @var string The default cache if nothing set
*/
const DEFAULT_DRIVER = 'database';
const DEFAULT_TYPE = Cache\AbstractCache::TYPE_DATABASE;
/**
* @var Configuration The configuration to read parameters out of the config
@ -52,33 +52,37 @@ class CacheDriverFactory
public function __construct(BaseURL $baseURL, Configuration $config, Database $dba, Profiler $profiler, LoggerInterface $logger)
{
$this->hostname = $baseURL->getHostname();
$this->config = $config;
$this->dba = $dba;
$this->config = $config;
$this->dba = $dba;
$this->profiler = $profiler;
$this->logger = $logger;
$this->logger = $logger;
}
/**
* This method creates a CacheDriver for the given cache driver name
*
* @param string $type The cache type to create (default is per config)
*
* @return ICache The instance of the CacheDriver
* @throws \Exception The exception if something went wrong during the CacheDriver creation
*/
public function create()
public function create(string $type = null)
{
$driver = $this->config->get('system', 'cache_driver', self::DEFAULT_DRIVER);
if (empty($type)) {
$type = $this->config->get('system', 'cache_driver', self::DEFAULT_TYPE);
}
switch ($driver) {
case 'memcache':
switch ($type) {
case Cache\AbstractCache::TYPE_MEMCACHE:
$cache = new Cache\MemcacheCache($this->hostname, $this->config);
break;
case 'memcached':
case Cache\AbstractCache::TYPE_MEMCACHED:
$cache = new Cache\MemcachedCache($this->hostname, $this->config, $this->logger);
break;
case 'redis':
case Cache\AbstractCache::TYPE_REDIS:
$cache = new Cache\RedisCache($this->hostname, $this->config);
break;
case 'apcu':
case Cache\AbstractCache::TYPE_APCU:
$cache = new Cache\APCuCache($this->hostname);
break;
default:
@ -89,7 +93,7 @@ class CacheDriverFactory
// In case profiling is enabled, wrap the ProfilerCache around the current cache
if (isset($profiling) && $profiling !== false) {
return new Cache\ProfilerCache($cache, $this->profiler);
return new Cache\ProfilerCache($cache, $this->profiler);
} else {
return $cache;
}

View file

@ -2,7 +2,7 @@
namespace Friendica\Factory;
use Friendica\Core\Cache\ICache;
use Friendica\Core\Cache\AbstractCache;
use Friendica\Core\Cache\IMemoryCache;
use Friendica\Core\Config\Configuration;
use Friendica\Core\Lock;
@ -11,13 +11,13 @@ use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
* Class LockDriverFactory
* Class LockFactory
*
* @package Friendica\Core\Cache
*
* A basic class to generate a LockDriver
*/
class LockDriverFactory
class LockFactory
{
/**
* @var string The default driver for caching
@ -35,9 +35,9 @@ class LockDriverFactory
private $dba;
/**
* @var ICache The memory cache driver in case we use it
* @var CacheFactory The memory cache driver in case we use it
*/
private $cacheDriver;
private $cacheFactory;
/**
* @var Profiler The optional profiler if the cached should be profiled
@ -49,25 +49,29 @@ class LockDriverFactory
*/
private $logger;
public function __construct(ICache $cacheDriver, Configuration $config, Database $dba, Profiler $profiler, LoggerInterface $logger)
public function __construct(CacheFactory $cacheFactory, Configuration $config, Database $dba, Profiler $profiler, LoggerInterface $logger)
{
$this->cacheDriver = $cacheDriver;
$this->config = $config;
$this->dba = $dba;
$this->logger = $logger;
$this->cacheFactory = $cacheFactory;
$this->config = $config;
$this->dba = $dba;
$this->logger = $logger;
}
public function create()
{
$lock_driver = $this->config->get('system', 'lock_driver', self::DEFAULT_DRIVER);
$lock_type = $this->config->get('system', 'lock_driver', self::DEFAULT_DRIVER);
try {
switch ($lock_driver) {
case 'memcache':
case 'memcached':
case 'redis':
if ($this->cacheDriver instanceof IMemoryCache) {
return new Lock\CacheLockDriver($this->cacheDriver);
switch ($lock_type) {
case AbstractCache::TYPE_MEMCACHE:
case AbstractCache::TYPE_MEMCACHED:
case AbstractCache::TYPE_REDIS:
case AbstractCache::TYPE_APCU:
$cache = $this->cacheFactory->create($lock_type);
if ($cache instanceof IMemoryCache) {
return new Lock\CacheLock($cache);
} else {
throw new \Exception(sprintf('Incompatible cache driver \'%s\' for lock used', $lock_type));
}
break;
@ -83,7 +87,7 @@ class LockDriverFactory
return self::useAutoDriver();
}
} catch (\Exception $exception) {
$this->logger->alert('Driver \'' . $lock_driver . '\' failed - Fallback to \'useAutoDriver()\'', ['exception' => $exception]);
$this->logger->alert('Driver \'' . $lock_type . '\' failed - Fallback to \'useAutoDriver()\'', ['exception' => $exception]);
return self::useAutoDriver();
}
}
@ -100,7 +104,6 @@ class LockDriverFactory
*/
private function useAutoDriver()
{
// 1. Try to use Semaphores for - local - locking
if (function_exists('sem_get')) {
try {
@ -111,11 +114,12 @@ class LockDriverFactory
}
// 2. Try to use Cache Locking (don't use the DB-Cache Locking because it works different!)
$cache_driver = $this->config->get('system', 'cache_driver', 'database');
if ($cache_driver != 'database') {
$cache_type = $this->config->get('system', 'cache_driver', 'database');
if ($cache_type != AbstractCache::TYPE_DATABASE) {
try {
if ($this->cacheDriver instanceof IMemoryCache) {
return new Lock\CacheLockDriver($this->cacheDriver);
$cache = $this->cacheFactory->create($cache_type);
if ($cache instanceof IMemoryCache) {
return new Lock\CacheLock($cache);
}
} catch (\Exception $exception) {
$this->logger->debug('Using Cache driver for locking failed.', ['exception' => $exception]);

View file

@ -119,16 +119,19 @@ return [
]
],
Cache\ICache::class => [
'instanceOf' => Factory\CacheDriverFactory::class,
'instanceOf' => Factory\CacheFactory::class,
'call' => [
['create', [], Dice::CHAIN_CALL],
],
],
Cache\IMemoryCache::class => [
'instanceOf' => Cache\ICache::class,
'instanceOf' => Factory\CacheFactory::class,
'call' => [
['create', [], Dice::CHAIN_CALL],
],
],
ILock::class => [
'instanceOf' => Factory\LockDriverFactory::class,
'instanceOf' => Factory\LockFactory::class,
'call' => [
['create', [], Dice::CHAIN_CALL],
],

View file

@ -4,8 +4,11 @@ namespace functional;
use Dice\Dice;
use Friendica\App;
use Friendica\Core\Cache\ICache;
use Friendica\Core\Cache\IMemoryCache;
use Friendica\Core\Config\Cache\ConfigCache;
use Friendica\Core\Config\Configuration;
use Friendica\Core\Lock\ILock;
use Friendica\Database\Database;
use Friendica\Test\Util\VFSTrait;
use Friendica\Util\BasePath;
@ -133,6 +136,31 @@ class dependencyCheck extends TestCase
/** @var LoggerInterface $logger */
$logger = $this->dice->create('$devLogger', ['dev']);
self::assertInstanceOf(LoggerInterface::class, $logger);
$this->assertInstanceOf(LoggerInterface::class, $logger);
}
public function testCache()
{
/** @var ICache $cache */
$cache = $this->dice->create(ICache::class);
$this->assertInstanceOf(ICache::class, $cache);
}
public function testMemoryCache()
{
/** @var IMemoryCache $cache */
$cache = $this->dice->create(IMemoryCache::class);
// We need to check "just" ICache, because the default Cache is DB-Cache, which isn't a memorycache
$this->assertInstanceOf(ICache::class, $cache);
}
public function testLock()
{
/** @var ILock $cache */
$lock = $this->dice->create(ILock::class);
$this->assertInstanceOf(ILock::class, $lock);
}
}

View file

@ -3,7 +3,7 @@
namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\APCuCache;
use Friendica\Core\Lock\CacheLockDriver;
use Friendica\Core\Lock\CacheLock;
class APCuCacheLockTest extends LockTest
{
@ -18,6 +18,6 @@ class APCuCacheLockTest extends LockTest
protected function getInstance()
{
return new CacheLockDriver(new APCuCache('localhost'));
return new CacheLock(new APCuCache('localhost'));
}
}

View file

@ -3,13 +3,13 @@
namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\ArrayCache;
use Friendica\Core\Lock\CacheLockDriver;
use Friendica\Core\Lock\CacheLock;
class ArrayCacheLockTest extends LockTest
{
protected function getInstance()
{
return new CacheLockDriver(new ArrayCache('localhost'));
return new CacheLock(new ArrayCache('localhost'));
}
public function testLockTTL()

View file

@ -5,7 +5,7 @@ namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\MemcacheCache;
use Friendica\Core\Config\Configuration;
use Friendica\Core\Lock\CacheLockDriver;
use Friendica\Core\Lock\CacheLock;
/**
* @requires extension Memcache
@ -25,6 +25,6 @@ class MemcacheCacheLockTest extends LockTest
->with('system', 'memcache_port')
->andReturn(11211);
return new CacheLockDriver(new MemcacheCache('localhost', $configMock));
return new CacheLock(new MemcacheCache('localhost', $configMock));
}
}

View file

@ -5,7 +5,7 @@ namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\MemcachedCache;
use Friendica\Core\Config\Configuration;
use Friendica\Core\Lock\CacheLockDriver;
use Friendica\Core\Lock\CacheLock;
use Psr\Log\NullLogger;
/**
@ -24,6 +24,6 @@ class MemcachedCacheLockTest extends LockTest
$logger = new NullLogger();
return new CacheLockDriver(new MemcachedCache('localhost', $configMock, $logger));
return new CacheLock(new MemcachedCache('localhost', $configMock, $logger));
}
}

View file

@ -5,7 +5,7 @@ namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\RedisCache;
use Friendica\Core\Config\Configuration;
use Friendica\Core\Lock\CacheLockDriver;
use Friendica\Core\Lock\CacheLock;
/**
* @requires extension redis
@ -34,6 +34,6 @@ class RedisCacheLockTest extends LockTest
->with('system', 'redis_password')
->andReturn(null);
return new CacheLockDriver(new RedisCache('localhost', $configMock));
return new CacheLock(new RedisCache('localhost', $configMock));
}
}