diff --git a/src/Console/Cache.php b/src/Console/Cache.php index e2e1dba6c0..7596ae6d51 100644 --- a/src/Console/Cache.php +++ b/src/Console/Cache.php @@ -4,7 +4,9 @@ namespace Friendica\Console; use Asika\SimpleConsole\CommandArgsException; use Friendica\App; -use Friendica\Core; +use Friendica\Core\Cache\ICacheDriver; +use Friendica\Core\Config\Configuration; +use Friendica\Factory\CacheDriverFactory; use RuntimeException; /** @@ -25,6 +27,16 @@ class Cache extends \Asika\SimpleConsole\Console */ private $appMode; + /** + * @var string The cache driver name + */ + private $cacheDriverName; + + /** + * @var ICacheDriver + */ + private $cache; + protected function getHelp() { $help = <<appMode = $appMode; + $this->cache = $cache; + $this->cacheDriverName = $config->get('system', 'cache_driver', CacheDriverFactory::DEFAULT_DRIVER); } protected function doExecute() @@ -79,11 +93,9 @@ HELP; $this->out('Database isn\'t ready or populated yet, database cache won\'t be available'); } - Core\Cache::init(); - if ($this->getOption('v')) { - $this->out('Cache Driver Name: ' . Core\Cache::$driver_name); - $this->out('Cache Driver Class: ' . Core\Cache::$driver_class); + $this->out('Cache Driver Name: ' . $this->cacheDriverName); + $this->out('Cache Driver Class: ' . get_class($this->cache)); } switch ($this->getArgument(0)) { @@ -115,7 +127,7 @@ HELP; private function executeList() { $prefix = $this->getArgument(1); - $keys = Core\Cache::getAllKeys($prefix); + $keys = $this->cache->getAllKeys($prefix); if (empty($prefix)) { $this->out('Listing all cache keys:'); @@ -136,7 +148,7 @@ HELP; { if (count($this->args) >= 2) { $key = $this->getArgument(1); - $value = Core\Cache::get($key); + $value = $this->cache->get($key); $this->out("{$key} => " . var_export($value, true)); } else { @@ -149,15 +161,15 @@ HELP; if (count($this->args) >= 3) { $key = $this->getArgument(1); $value = $this->getArgument(2); - $duration = intval($this->getArgument(3, Core\Cache::FIVE_MINUTES)); + $duration = intval($this->getArgument(3, ICacheDriver::FIVE_MINUTES)); - if (is_array(Core\Cache::get($key))) { + if (is_array($this->cache->get($key))) { throw new RuntimeException("$key is an array and can't be set using this command."); } - $result = Core\Cache::set($key, $value, $duration); + $result = $this->cache->set($key, $value, $duration); if ($result) { - $this->out("{$key} <= " . Core\Cache::get($key)); + $this->out("{$key} <= " . $this->cache->get($key)); } else { $this->out("Unable to set {$key}"); } @@ -168,7 +180,7 @@ HELP; private function executeFlush() { - $result = Core\Cache::clear(); + $result = $this->cache->clear(); if ($result) { $this->out('Cache successfully flushed'); } else { @@ -178,7 +190,7 @@ HELP; private function executeClear() { - $result = Core\Cache::clear(false); + $result = $this->cache->clear(false); if ($result) { $this->out('Cache successfully cleared'); } else { diff --git a/src/Core/Cache.php b/src/Core/Cache.php index 7a8f7367ec..0c5597aa5e 100644 --- a/src/Core/Cache.php +++ b/src/Core/Cache.php @@ -4,51 +4,33 @@ */ namespace Friendica\Core; -use Friendica\Factory\CacheDriverFactory; +use Friendica\BaseObject; +use Friendica\Core\Cache\ICacheDriver; /** * @brief Class for storing data for a short time */ -class Cache extends \Friendica\BaseObject +class Cache extends BaseObject { - const MONTH = 2592000; + /** @deprecated Use ICacheDriver::MONTH */ + const MONTH = ICacheDriver::MONTH; + /** @deprecated Use ICacheDriver::WEEK */ const WEEK = 604800; + /** @deprecated Use ICacheDriver::DAY */ const DAY = 86400; + /** @deprecated Use ICacheDriver::HOUR */ const HOUR = 3600; + /** @deprecated Use ICacheDriver::HALF_HOUR */ const HALF_HOUR = 1800; + /** @deprecated Use ICacheDriver::QUARTER_HOUR */ const QUARTER_HOUR = 900; + /** @deprecated Use ICacheDriver::FIVE_MINUTES */ const FIVE_MINUTES = 300; + /** @deprecated Use ICacheDriver::MINUTE */ const MINUTE = 60; + /** @deprecated Use ICacheDriver::INFINITE */ const INFINITE = 0; - /** - * @var Cache\ICacheDriver - */ - private static $driver = null; - public static $driver_class = null; - public static $driver_name = null; - - public static function init() - { - self::$driver_name = Config::get('system', 'cache_driver', 'database'); - self::$driver = CacheDriverFactory::create(self::$driver_name); - self::$driver_class = get_class(self::$driver); - } - - /** - * Returns the current cache driver - * - * @return Cache\ICacheDriver - */ - private static function getDriver() - { - if (self::$driver === null) { - self::init(); - } - - return self::$driver; - } - /** * @brief Returns all the cache keys sorted alphabetically * @@ -59,13 +41,7 @@ class Cache extends \Friendica\BaseObject */ public static function getAllKeys($prefix = null) { - $time = microtime(true); - - $return = self::getDriver()->getAllKeys($prefix); - - self::getApp()->getProfiler()->saveTimestamp($time, 'cache', System::callstack()); - - return $return; + return self::getClass(ICacheDriver::class)->getAllKeys($prefix); } /** @@ -78,13 +54,7 @@ class Cache extends \Friendica\BaseObject */ public static function get($key) { - $time = microtime(true); - - $return = self::getDriver()->get($key); - - self::getApp()->getProfiler()->saveTimestamp($time, 'cache', System::callstack()); - - return $return; + return self::getClass(ICacheDriver::class)->get($key); } /** @@ -99,15 +69,9 @@ class Cache extends \Friendica\BaseObject * @return bool * @throws \Exception */ - public static function set($key, $value, $duration = self::MONTH) + public static function set($key, $value, $duration = ICacheDriver::MONTH) { - $time = microtime(true); - - $return = self::getDriver()->set($key, $value, $duration); - - self::getApp()->getProfiler()->saveTimestamp($time, 'cache_write', System::callstack()); - - return $return; + return self::getClass(ICacheDriver::class)->set($key, $value, $duration); } /** @@ -120,13 +84,7 @@ class Cache extends \Friendica\BaseObject */ public static function delete($key) { - $time = microtime(true); - - $return = self::getDriver()->delete($key); - - self::getApp()->getProfiler()->saveTimestamp($time, 'cache_write', System::callstack()); - - return $return; + return self::getClass(ICacheDriver::class)->delete($key); } /** @@ -135,9 +93,10 @@ class Cache extends \Friendica\BaseObject * @param boolean $outdated just remove outdated values * * @return bool + * @throws \Exception */ public static function clear($outdated = true) { - return self::getDriver()->clear($outdated); + return self::getClass(ICacheDriver::class)->clear($outdated); } } diff --git a/src/Core/Cache/APCuCache.php b/src/Core/Cache/APCuCache.php index f658424cdc..b89d7fe4b3 100644 --- a/src/Core/Cache/APCuCache.php +++ b/src/Core/Cache/APCuCache.php @@ -18,11 +18,13 @@ class APCuCache extends AbstractCacheDriver implements IMemoryCacheDriver /** * @throws Exception */ - public function __construct() + public function __construct(string $hostname) { if (!self::isAvailable()) { throw new Exception('APCu is not available.'); } + + parent::__construct($hostname); } /** diff --git a/src/Core/Cache/AbstractCacheDriver.php b/src/Core/Cache/AbstractCacheDriver.php index f238a7819c..e12f63bfa4 100644 --- a/src/Core/Cache/AbstractCacheDriver.php +++ b/src/Core/Cache/AbstractCacheDriver.php @@ -1,8 +1,6 @@ hostName = $hostName; + } + /** * Returns the prefix (to avoid namespace conflicts) * @@ -22,7 +30,7 @@ abstract class AbstractCacheDriver extends BaseObject protected function getPrefix() { // We fetch with the hostname as key to avoid problems with other applications - return self::getApp()->getHostName(); + return $this->hostName; } /** @@ -46,7 +54,7 @@ abstract class AbstractCacheDriver extends BaseObject } else { // Keys are prefixed with the node hostname, let's remove it array_walk($keys, function (&$value) { - $value = preg_replace('/^' . self::getApp()->getHostName() . ':/', '', $value); + $value = preg_replace('/^' . $this->hostName . ':/', '', $value); }); sort($keys); @@ -79,6 +87,5 @@ abstract class AbstractCacheDriver extends BaseObject return $result; } - } } diff --git a/src/Core/Cache/ArrayCache.php b/src/Core/Cache/ArrayCache.php index a99b05788f..f2f37708de 100644 --- a/src/Core/Cache/ArrayCache.php +++ b/src/Core/Cache/ArrayCache.php @@ -2,7 +2,6 @@ namespace Friendica\Core\Cache; - use Friendica\Core\Cache; /** diff --git a/src/Core/Cache/DatabaseCacheDriver.php b/src/Core/Cache/DatabaseCacheDriver.php index f6f5b6486c..f1d6150701 100644 --- a/src/Core/Cache/DatabaseCacheDriver.php +++ b/src/Core/Cache/DatabaseCacheDriver.php @@ -3,7 +3,7 @@ namespace Friendica\Core\Cache; use Friendica\Core\Cache; -use Friendica\Database\DBA; +use Friendica\Database\Database; use Friendica\Util\DateTimeFormat; /** @@ -13,6 +13,18 @@ use Friendica\Util\DateTimeFormat; */ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver { + /** + * @var Database + */ + private $dba; + + public function __construct(string $hostname, Database $dba) + { + parent::__construct($hostname); + + $this->dba = $dba; + } + /** * (@inheritdoc) */ @@ -24,13 +36,13 @@ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver $where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix]; } - $stmt = DBA::select('cache', ['k'], $where); + $stmt = $this->dba->select('cache', ['k'], $where); $keys = []; - while ($key = DBA::fetch($stmt)) { + while ($key = $this->dba->fetch($stmt)) { array_push($keys, $key['k']); } - DBA::close($stmt); + $this->dba->close($stmt); return $keys; } @@ -40,9 +52,9 @@ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver */ public function get($key) { - $cache = DBA::selectFirst('cache', ['v'], ['`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, DateTimeFormat::utcNow()]); + $cache = $this->dba->selectFirst('cache', ['v'], ['`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, DateTimeFormat::utcNow()]); - if (DBA::isResult($cache)) { + if ($this->dba->isResult($cache)) { $cached = $cache['v']; $value = @unserialize($cached); @@ -76,7 +88,7 @@ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver ]; } - return DBA::update('cache', $fields, ['k' => $key], true); + return $this->dba->update('cache', $fields, ['k' => $key], true); } /** @@ -84,7 +96,7 @@ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver */ public function delete($key) { - return DBA::delete('cache', ['k' => $key]); + return $this->dba->delete('cache', ['k' => $key]); } /** @@ -93,9 +105,9 @@ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver public function clear($outdated = true) { if ($outdated) { - return DBA::delete('cache', ['`expires` < NOW()']); + return $this->dba->delete('cache', ['`expires` < NOW()']); } else { - return DBA::delete('cache', ['`k` IS NOT NULL ']); + return $this->dba->delete('cache', ['`k` IS NOT NULL ']); } } } diff --git a/src/Core/Cache/ICacheDriver.php b/src/Core/Cache/ICacheDriver.php index 1188e51877..34b741968a 100644 --- a/src/Core/Cache/ICacheDriver.php +++ b/src/Core/Cache/ICacheDriver.php @@ -2,8 +2,6 @@ namespace Friendica\Core\Cache; -use Friendica\Core\Cache; - /** * Cache Driver Interface * @@ -11,6 +9,16 @@ use Friendica\Core\Cache; */ interface ICacheDriver { + const MONTH = 2592000; + const WEEK = 604800; + const DAY = 86400; + const HOUR = 3600; + const HALF_HOUR = 1800; + const QUARTER_HOUR = 900; + const FIVE_MINUTES = 300; + const MINUTE = 60; + const INFINITE = 0; + /** * Lists all cache keys * @@ -38,7 +46,7 @@ interface ICacheDriver * * @return bool */ - public function set($key, $value, $ttl = Cache::FIVE_MINUTES); + public function set($key, $value, $ttl = self::FIVE_MINUTES); /** * Delete a key from the cache diff --git a/src/Core/Cache/IMemoryCacheDriver.php b/src/Core/Cache/IMemoryCacheDriver.php index 0c5146f439..e35159a3a6 100644 --- a/src/Core/Cache/IMemoryCacheDriver.php +++ b/src/Core/Cache/IMemoryCacheDriver.php @@ -1,7 +1,6 @@ memcache = new Memcache(); + $memcache_host = $config->get('system', 'memcache_host'); + $memcache_port = $config->get('system', 'memcache_port'); + if (!$this->memcache->connect($memcache_host, $memcache_port)) { throw new Exception('Expected Memcache server at ' . $memcache_host . ':' . $memcache_port . ' isn\'t available'); } diff --git a/src/Core/Cache/MemcachedCacheDriver.php b/src/Core/Cache/MemcachedCacheDriver.php index 687e67416a..7b3a2d5dfa 100644 --- a/src/Core/Cache/MemcachedCacheDriver.php +++ b/src/Core/Cache/MemcachedCacheDriver.php @@ -2,11 +2,11 @@ namespace Friendica\Core\Cache; -use Friendica\Core\Cache; -use Friendica\Core\Logger; - use Exception; +use Friendica\Core\Cache; +use Friendica\Core\Config\Configuration; use Memcached; +use Psr\Log\LoggerInterface; /** * Memcached Cache Driver @@ -23,6 +23,11 @@ class MemcachedCacheDriver extends AbstractCacheDriver implements IMemoryCacheDr */ private $memcached; + /** + * @var LoggerInterface + */ + private $logger; + /** * Due to limitations of the INI format, the expected configuration for Memcached servers is the following: * array { @@ -33,14 +38,20 @@ class MemcachedCacheDriver extends AbstractCacheDriver implements IMemoryCacheDr * @param array $memcached_hosts * @throws \Exception */ - public function __construct(array $memcached_hosts) + public function __construct(string $hostname, Configuration $config, LoggerInterface $logger) { if (!class_exists('Memcached', false)) { throw new Exception('Memcached class isn\'t available'); } + parent::__construct($hostname); + + $this->logger = $logger; + $this->memcached = new Memcached(); + $memcached_hosts = $config->get('system', 'memcached_hosts'); + array_walk($memcached_hosts, function (&$value) { if (is_string($value)) { $value = array_map('trim', explode(',', $value)); @@ -64,7 +75,7 @@ class MemcachedCacheDriver extends AbstractCacheDriver implements IMemoryCacheDr if ($this->memcached->getResultCode() == Memcached::RES_SUCCESS) { return $this->filterArrayKeysByPrefix($keys, $prefix); } else { - Logger::log('Memcached \'getAllKeys\' failed with ' . $this->memcached->getResultMessage(), Logger::ALL); + $this->logger->debug('Memcached \'getAllKeys\' failed', ['result' => $this->memcached->getResultMessage()]); return []; } } @@ -83,7 +94,7 @@ class MemcachedCacheDriver extends AbstractCacheDriver implements IMemoryCacheDr if ($this->memcached->getResultCode() === Memcached::RES_SUCCESS) { $return = $value; } else { - Logger::log('Memcached \'get\' failed with ' . $this->memcached->getResultMessage(), Logger::ALL); + $this->logger->debug('Memcached \'get\' failed', ['result' => $this->memcached->getResultMessage()]); } return $return; diff --git a/src/Core/Cache/ProfilerCache.php b/src/Core/Cache/ProfilerCache.php new file mode 100644 index 0000000000..04271e7c69 --- /dev/null +++ b/src/Core/Cache/ProfilerCache.php @@ -0,0 +1,155 @@ +cache = $cache; + $this->profiler = $profiler; + } + + /** + * {@inheritDoc} + */ + public function getAllKeys($prefix = null) + { + $time = microtime(true); + + $return = $this->cache->getAllKeys($prefix); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + $time = microtime(true); + + $return = $this->cache->get($key); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function set($key, $value, $ttl = Cache::FIVE_MINUTES) + { + $time = microtime(true); + + $return = $this->cache->set($key, $value, $ttl); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function delete($key) + { + $time = microtime(true); + + $return = $this->cache->delete($key); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function clear($outdated = true) + { + $time = microtime(true); + + $return = $this->cache->clear($outdated); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function add($key, $value, $ttl = Cache::FIVE_MINUTES) + { + if ($this->cache instanceof IMemoryCacheDriver) { + $time = microtime(true); + + $return = $this->cache->add($key, $value, $ttl); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public function compareSet($key, $oldValue, $newValue, $ttl = Cache::FIVE_MINUTES) + { + if ($this->cache instanceof IMemoryCacheDriver) { + $time = microtime(true); + + $return = $this->cache->compareSet($key, $oldValue, $newValue, $ttl); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public function compareDelete($key, $value) + { + if ($this->cache instanceof IMemoryCacheDriver) { + $time = microtime(true); + + $return = $this->cache->compareDelete($key, $value); + + $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + + return $return; + } else { + return false; + } + } +} diff --git a/src/Core/Cache/RedisCacheDriver.php b/src/Core/Cache/RedisCacheDriver.php index ea4eb4390a..951f51dc13 100644 --- a/src/Core/Cache/RedisCacheDriver.php +++ b/src/Core/Cache/RedisCacheDriver.php @@ -4,6 +4,7 @@ namespace Friendica\Core\Cache; use Exception; use Friendica\Core\Cache; +use Friendica\Core\Config\Configuration; use Redis; /** @@ -20,20 +21,23 @@ class RedisCacheDriver extends AbstractCacheDriver implements IMemoryCacheDriver private $redis; /** - * @param string $redis_host - * @param int $redis_port - * @param int $redis_db (Default = 0, maximum is 15) - * @param string? $redis_pw * @throws Exception */ - public function __construct($redis_host, $redis_port, $redis_db = 0, $redis_pw = null) + public function __construct(string $hostname, Configuration $config) { if (!class_exists('Redis', false)) { throw new Exception('Redis class isn\'t available'); } + parent::__construct($hostname); + $this->redis = new Redis(); + $redis_host = $config->get('system', 'redis_host'); + $redis_port = $config->get('system', 'redis_port'); + $redis_pw = $config->get('system', 'redis_password'); + $redis_db = $config->get('system', 'redis_db', 0); + if (!$this->redis->connect($redis_host, $redis_port)) { throw new Exception('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available'); } diff --git a/src/Core/Lock.php b/src/Core/Lock.php index a45490bf39..04dfaa1c5e 100644 --- a/src/Core/Lock.php +++ b/src/Core/Lock.php @@ -7,116 +7,28 @@ namespace Friendica\Core; -use Friendica\Factory\CacheDriverFactory; -use Friendica\Core\Cache\IMemoryCacheDriver; +use Friendica\BaseObject; +use Friendica\Core\Cache\ICacheDriver; +use Friendica\Core\Lock\ILockDriver; /** - * @brief This class contain Functions for preventing parallel execution of functions + * This class contain Functions for preventing parallel execution of functions */ -class Lock +class Lock extends BaseObject { - /** - * @var Lock\ILockDriver; - */ - static $driver = null; - - public static function init() - { - $lock_driver = Config::get('system', 'lock_driver', 'default'); - - try { - switch ($lock_driver) { - case 'memcache': - case 'memcached': - case 'redis': - $cache_driver = CacheDriverFactory::create($lock_driver); - if ($cache_driver instanceof IMemoryCacheDriver) { - self::$driver = new Lock\CacheLockDriver($cache_driver); - } - break; - - case 'database': - self::$driver = new Lock\DatabaseLockDriver(); - break; - - case 'semaphore': - self::$driver = new Lock\SemaphoreLockDriver(); - break; - - default: - self::useAutoDriver(); - } - } catch (\Exception $exception) { - Logger::log('Driver \'' . $lock_driver . '\' failed - Fallback to \'useAutoDriver()\''); - self::useAutoDriver(); - } - } - - /** - * @brief This method tries to find the best - local - locking method for Friendica - * - * The following sequence will be tried: - * 1. Semaphore Locking - * 2. Cache Locking - * 3. Database Locking - * - */ - private static function useAutoDriver() { - - // 1. Try to use Semaphores for - local - locking - if (function_exists('sem_get')) { - try { - self::$driver = new Lock\SemaphoreLockDriver(); - return; - } catch (\Exception $exception) { - Logger::log('Using Semaphore driver for locking failed: ' . $exception->getMessage()); - } - } - - // 2. Try to use Cache Locking (don't use the DB-Cache Locking because it works different!) - $cache_driver = Config::get('system', 'cache_driver', 'database'); - if ($cache_driver != 'database') { - try { - $lock_driver = CacheDriverFactory::create($cache_driver); - if ($lock_driver instanceof IMemoryCacheDriver) { - self::$driver = new Lock\CacheLockDriver($lock_driver); - } - return; - } catch (\Exception $exception) { - Logger::log('Using Cache driver for locking failed: ' . $exception->getMessage()); - } - } - - // 3. Use Database Locking as a Fallback - self::$driver = new Lock\DatabaseLockDriver(); - } - - /** - * Returns the current cache driver - * - * @return Lock\ILockDriver; - */ - private static function getDriver() - { - if (self::$driver === null) { - self::init(); - } - - return self::$driver; - } - /** * @brief Acquires a lock for a given name * - * @param string $key Name of the lock + * @param string $key Name of the lock * @param integer $timeout Seconds until we give up - * @param integer $ttl The Lock lifespan, must be one of the Cache constants + * @param integer $ttl The Lock lifespan, must be one of the Cache constants * * @return boolean Was the lock successful? + * @throws \Exception */ - public static function acquire($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES) + public static function acquire($key, $timeout = 120, $ttl = ICacheDriver::FIVE_MINUTES) { - return self::getDriver()->acquireLock($key, $timeout, $ttl); + return self::getClass(ILockDriver::class)->acquireLock($key, $timeout, $ttl); } /** @@ -124,19 +36,22 @@ class Lock * * @param string $key Name of the lock * @param bool $override Overrides the lock to get releases + * * @return void + * @throws \Exception */ public static function release($key, $override = false) { - self::getDriver()->releaseLock($key, $override); + return self::getClass(ILockDriver::class)->releaseLock($key, $override); } /** * @brief Releases all lock that were set by us * @return void + * @throws \Exception */ public static function releaseAll() { - self::getDriver()->releaseAll(); + self::getClass(ILockDriver::class)->releaseAll(); } } diff --git a/src/Core/Lock/AbstractLockDriver.php b/src/Core/Lock/AbstractLockDriver.php index 0aedeeb1b0..71ad08d088 100644 --- a/src/Core/Lock/AbstractLockDriver.php +++ b/src/Core/Lock/AbstractLockDriver.php @@ -1,7 +1,6 @@ cache->delete($cachekey); } else { @@ -83,15 +81,17 @@ class CacheLockDriver extends AbstractLockDriver public function isLocked($key) { $cachekey = self::getLockKey($key); - $lock = $this->cache->get($cachekey); + $lock = $this->cache->get($cachekey); return isset($lock) && ($lock !== false); } /** - * @param string $key The original key - * @return string The cache key used for the cache + * @param string $key The original key + * + * @return string The cache key used for the cache */ - private static function getLockKey($key) { + private static function getLockKey($key) + { return "lock:" . $key; } } diff --git a/src/Core/Lock/DatabaseLockDriver.php b/src/Core/Lock/DatabaseLockDriver.php index a8269bc92e..e9e0500bb4 100644 --- a/src/Core/Lock/DatabaseLockDriver.php +++ b/src/Core/Lock/DatabaseLockDriver.php @@ -3,7 +3,7 @@ namespace Friendica\Core\Lock; use Friendica\Core\Cache; -use Friendica\Database\DBA; +use Friendica\Database\Database; use Friendica\Util\DateTimeFormat; /** @@ -18,11 +18,17 @@ class DatabaseLockDriver extends AbstractLockDriver */ private $pid; + /** + * @var Database The database connection of Friendica + */ + private $dba; + /** * @param null|int $pid The Id of the current process (null means determine automatically) */ - public function __construct($pid = null) + public function __construct(Database $dba, $pid = null) { + $this->dba = $dba; $this->pid = isset($pid) ? $pid : getmypid(); } @@ -32,13 +38,13 @@ class DatabaseLockDriver extends AbstractLockDriver public function acquireLock($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES) { $got_lock = false; - $start = time(); + $start = time(); do { - DBA::lock('locks'); - $lock = DBA::selectFirst('locks', ['locked', 'pid'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]); + $this->dba->lock('locks'); + $lock = $this->dba->selectFirst('locks', ['locked', 'pid'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]); - if (DBA::isResult($lock)) { + if ($this->dba->isResult($lock)) { if ($lock['locked']) { // We want to lock something that was already locked by us? So we got the lock. if ($lock['pid'] == $this->pid) { @@ -46,16 +52,16 @@ class DatabaseLockDriver extends AbstractLockDriver } } if (!$lock['locked']) { - DBA::update('locks', ['locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')], ['name' => $key]); + $this->dba->update('locks', ['locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')], ['name' => $key]); $got_lock = true; } } else { - DBA::insert('locks', ['name' => $key, 'locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]); + $this->dba->insert('locks', ['name' => $key, 'locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]); $got_lock = true; $this->markAcquire($key); } - DBA::unlock(); + $this->dba->unlock(); if (!$got_lock && ($timeout > 0)) { usleep(rand(100000, 2000000)); @@ -76,7 +82,7 @@ class DatabaseLockDriver extends AbstractLockDriver $where = ['name' => $key, 'pid' => $this->pid]; } - $return = DBA::delete('locks', $where); + $return = $this->dba->delete('locks', $where); $this->markRelease($key); @@ -88,7 +94,7 @@ class DatabaseLockDriver extends AbstractLockDriver */ public function releaseAll() { - $return = DBA::delete('locks', ['pid' => $this->pid]); + $return = $this->dba->delete('locks', ['pid' => $this->pid]); $this->acquiredLocks = []; @@ -100,9 +106,9 @@ class DatabaseLockDriver extends AbstractLockDriver */ public function isLocked($key) { - $lock = DBA::selectFirst('locks', ['locked'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]); + $lock = $this->dba->selectFirst('locks', ['locked'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]); - if (DBA::isResult($lock)) { + if ($this->dba->isResult($lock)) { return $lock['locked'] !== false; } else { return false; diff --git a/src/Core/Lock/ILockDriver.php b/src/Core/Lock/ILockDriver.php index 7df5b3f87a..43793d6287 100644 --- a/src/Core/Lock/ILockDriver.php +++ b/src/Core/Lock/ILockDriver.php @@ -1,6 +1,7 @@ hostname = $baseURL->getHostname(); + $this->config = $config; + $this->dba = $dba; + $this->profiler = $profiler; + $this->logger = $logger; + } + /** * This method creates a CacheDriver for the given cache driver name * - * @param string $driver The name of the cache driver * @return ICacheDriver The instance of the CacheDriver * @throws \Exception The exception if something went wrong during the CacheDriver creation */ - public static function create($driver) { + public function create() + { + $driver = $this->config->get('system', 'cache_driver', self::DEFAULT_DRIVER); switch ($driver) { case 'memcache': - $memcache_host = Config::get('system', 'memcache_host'); - $memcache_port = Config::get('system', 'memcache_port'); - - return new Cache\MemcacheCacheDriver($memcache_host, $memcache_port); + $cache = new Cache\MemcacheCacheDriver($this->hostname, $this->config); break; - case 'memcached': - $memcached_hosts = Config::get('system', 'memcached_hosts'); - - return new Cache\MemcachedCacheDriver($memcached_hosts); + $cache = new Cache\MemcachedCacheDriver($this->hostname, $this->config, $this->logger); break; case 'redis': - $redis_host = Config::get('system', 'redis_host'); - $redis_port = Config::get('system', 'redis_port'); - $redis_pw = Config::get('system', 'redis_password'); - $redis_db = Config::get('system', 'redis_db', 0); - - return new Cache\RedisCacheDriver($redis_host, $redis_port, $redis_db, $redis_pw); + $cache = new Cache\RedisCacheDriver($this->hostname, $this->config); break; - case 'apcu': - return new Cache\APCuCache(); + $cache = new Cache\APCuCache($this->hostname); break; - default: - return new Cache\DatabaseCacheDriver(); + $cache = new Cache\DatabaseCacheDriver($this->hostname, $this->dba); + } + + $profiling = $this->config->get('system', 'profiling', false); + + // In case profiling is enabled, wrap the ProfilerCache around the current cache + if (isset($profiling) && $profiling !== false) { + return new Cache\ProfilerCache($cache, $this->profiler); + } else { + return $cache; } } } diff --git a/src/Factory/LockDriverFactory.php b/src/Factory/LockDriverFactory.php new file mode 100644 index 0000000000..d31728be6c --- /dev/null +++ b/src/Factory/LockDriverFactory.php @@ -0,0 +1,128 @@ +cacheDriver = $cacheDriver; + $this->config = $config; + $this->dba = $dba; + $this->logger = $logger; + } + + public function create() + { + $lock_driver = $this->config->get('system', 'lock_driver', self::DEFAULT_DRIVER); + + try { + switch ($lock_driver) { + case 'memcache': + case 'memcached': + case 'redis': + if ($this->cacheDriver instanceof IMemoryCacheDriver) { + return new Lock\CacheLockDriver($this->cacheDriver); + } + break; + + case 'database': + return new Lock\DatabaseLockDriver($this->dba); + break; + + case 'semaphore': + return new Lock\SemaphoreLockDriver(); + break; + + default: + return self::useAutoDriver(); + } + } catch (\Exception $exception) { + $this->logger->alert('Driver \'' . $lock_driver . '\' failed - Fallback to \'useAutoDriver()\'', ['exception' => $exception]); + return self::useAutoDriver(); + } + } + + /** + * @brief This method tries to find the best - local - locking method for Friendica + * + * The following sequence will be tried: + * 1. Semaphore Locking + * 2. Cache Locking + * 3. Database Locking + * + * @return Lock\ILockDriver + */ + private function useAutoDriver() + { + + // 1. Try to use Semaphores for - local - locking + if (function_exists('sem_get')) { + try { + return new Lock\SemaphoreLockDriver(); + } catch (\Exception $exception) { + $this->logger->debug('Using Semaphore driver for locking failed.', ['exception' => $exception]); + } + } + + // 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') { + try { + if ($this->cacheDriver instanceof IMemoryCacheDriver) { + return new Lock\CacheLockDriver($this->cacheDriver); + } + } catch (\Exception $exception) { + $this->logger->debug('Using Cache driver for locking failed.', ['exception' => $exception]); + } + } + + // 3. Use Database Locking as a Fallback + return new Lock\DatabaseLockDriver($this->dba); + } +} diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 475f5cceed..55a2c6845b 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -2,7 +2,9 @@ use Dice\Dice; use Friendica\App; +use Friendica\Core\Cache; use Friendica\Core\Config; +use Friendica\Core\Lock\ILockDriver; use Friendica\Database\Database; use Friendica\Factory; use Friendica\Util; @@ -116,4 +118,19 @@ return [ ['createDev', [], Dice::CHAIN_CALL], ] ], + Cache\ICacheDriver::class => [ + 'instanceOf' => Factory\CacheDriverFactory::class, + 'call' => [ + ['create', [], Dice::CHAIN_CALL], + ], + ], + Cache\IMemoryCacheDriver::class => [ + 'instanceOf' => Cache\ICacheDriver::class, + ], + ILockDriver::class => [ + 'instanceOf' => Factory\LockDriverFactory::class, + 'call' => [ + ['create', [], Dice::CHAIN_CALL], + ], + ], ]; diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index 4f0275493c..7421f16a09 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -5,56 +5,10 @@ namespace Friendica\Test; -use Friendica\Database\Database; -use Friendica\Test\Util\Database\StaticDatabase; - /** * Abstract class used by tests that need a database. */ abstract class DatabaseTest extends MockedTest { - protected function setUp() - { - parent::setUp(); - - StaticDatabase::statConnect($_SERVER); - // Rollbacks every DB usage (in case the test couldn't call tearDown) - StaticDatabase::statRollback(); - // Start the first, outer transaction - StaticDatabase::getGlobConnection()->beginTransaction(); - } - - protected function tearDown() - { - // Rollbacks every DB usage so we don't commit anything into the DB - StaticDatabase::statRollback(); - - parent::tearDown(); - } - - /** - * Loads a given DB fixture for this DB test - * - * @param string $fixture The path to the fixture - * @param Database $dba The DB connection - * - * @throws \Exception - */ - protected function loadFixture(string $fixture, Database $dba) - { - $this->assertFileExists($fixture); - - $data = include $fixture; - - foreach ($data as $tableName => $rows) { - if (!is_array($rows)) { - $dba->p('TRUNCATE TABLE `' . $tableName . '``'); - continue; - } - - foreach ($rows as $row) { - $dba->insert($tableName, $row); - } - } - } + use DatabaseTestTrait; } diff --git a/tests/DatabaseTestTrait.php b/tests/DatabaseTestTrait.php new file mode 100644 index 0000000000..49dc999b69 --- /dev/null +++ b/tests/DatabaseTestTrait.php @@ -0,0 +1,58 @@ +beginTransaction(); + + parent::setUp(); + } + + protected function tearDown() + { + // Rollbacks every DB usage so we don't commit anything into the DB + StaticDatabase::statRollback(); + + parent::tearDown(); + } + + /** + * Loads a given DB fixture for this DB test + * + * @param string $fixture The path to the fixture + * @param Database $dba The DB connection + * + * @throws \Exception + */ + protected function loadFixture(string $fixture, Database $dba) + { + $data = include $fixture; + + foreach ($data as $tableName => $rows) { + if (!is_array($rows)) { + $dba->p('TRUNCATE TABLE `' . $tableName . '``'); + continue; + } + + foreach ($rows as $row) { + $dba->insert($tableName, $row); + } + } + } +} diff --git a/tests/Util/DbaCacheMockTrait.php b/tests/Util/DbaCacheMockTrait.php index 87ab450c9e..95e7cbcb1d 100644 --- a/tests/Util/DbaCacheMockTrait.php +++ b/tests/Util/DbaCacheMockTrait.php @@ -4,8 +4,14 @@ namespace Friendica\Test\Util; trait DbaCacheMockTrait { - use DBAMockTrait; - use DateTimeFormatMockTrait; + /** + * @var + */ + protected $dba; + + public function __construct() + { + } protected function mockDelete($key, $return = true, $times = null) { diff --git a/tests/Util/VFSTrait.php b/tests/Util/VFSTrait.php index 110f24a61a..565e693c95 100644 --- a/tests/Util/VFSTrait.php +++ b/tests/Util/VFSTrait.php @@ -28,6 +28,7 @@ trait VFSTrait // create a virtual directory and copy all needed files and folders to it $this->root = vfsStream::setup('friendica', 0777, $structure); + $this->setConfigFile('dbstructure.config.php', true); $this->setConfigFile('defaults.config.php', true); $this->setConfigFile('settings.config.php', true); $this->setConfigFile('local.config.php'); diff --git a/tests/src/Core/Cache/APCuCacheDriverTest.php b/tests/src/Core/Cache/APCuCacheDriverTest.php index eac00f559a..9925b02352 100644 --- a/tests/src/Core/Cache/APCuCacheDriverTest.php +++ b/tests/src/Core/Cache/APCuCacheDriverTest.php @@ -17,7 +17,7 @@ class APCuCacheDriverTest extends MemoryCacheTest protected function getInstance() { - $this->cache = new APCuCache(); + $this->cache = new APCuCache('localhost'); return $this->cache; } diff --git a/tests/src/Core/Cache/ArrayCacheDriverTest.php b/tests/src/Core/Cache/ArrayCacheDriverTest.php index c92fb98dac..68b8d1dba1 100644 --- a/tests/src/Core/Cache/ArrayCacheDriverTest.php +++ b/tests/src/Core/Cache/ArrayCacheDriverTest.php @@ -8,7 +8,7 @@ class ArrayCacheDriverTest extends MemoryCacheTest { protected function getInstance() { - $this->cache = new ArrayCache(); + $this->cache = new ArrayCache('localhost'); return $this->cache; } diff --git a/tests/src/Core/Cache/CacheTest.php b/tests/src/Core/Cache/CacheTest.php index ef97f5a172..088715cd79 100644 --- a/tests/src/Core/Cache/CacheTest.php +++ b/tests/src/Core/Cache/CacheTest.php @@ -4,15 +4,10 @@ namespace Friendica\Test\src\Core\Cache; use Friendica\Core\Cache\MemcachedCacheDriver; use Friendica\Test\MockedTest; -use Friendica\Test\Util\AppMockTrait; -use Friendica\Test\Util\VFSTrait; use Friendica\Util\PidFile; abstract class CacheTest extends MockedTest { - use VFSTrait; - use AppMockTrait; - /** * @var int Start time of the mock (used for time operations) */ @@ -30,6 +25,7 @@ abstract class CacheTest extends MockedTest /** * Dataset for test setting different types in the cache + * * @return array */ public function dataTypesInCache() @@ -48,6 +44,7 @@ abstract class CacheTest extends MockedTest /** * Dataset for simple value sets/gets + * * @return array */ public function dataSimple() @@ -66,12 +63,6 @@ abstract class CacheTest extends MockedTest protected function setUp() { - $this->setUpVfsDir(); - $this->mockApp($this->root); - $this->app - ->shouldReceive('getHostname') - ->andReturn('friendica.local'); - parent::setUp(); $this->instance = $this->getInstance(); @@ -82,10 +73,12 @@ abstract class CacheTest extends MockedTest /** * @small * @dataProvider dataSimple + * * @param mixed $value1 a first * @param mixed $value2 a second */ - function testSimple($value1, $value2) { + function testSimple($value1, $value2) + { $this->assertNull($this->instance->get('value1')); $this->instance->set('value1', $value1); @@ -110,12 +103,14 @@ abstract class CacheTest extends MockedTest /** * @small * @dataProvider dataSimple + * * @param mixed $value1 a first * @param mixed $value2 a second * @param mixed $value3 a third * @param mixed $value4 a fourth */ - function testClear($value1, $value2, $value3, $value4) { + function testClear($value1, $value2, $value3, $value4) + { $value = 'ipsum lorum'; $this->instance->set('1_value1', $value1); $this->instance->set('1_value2', $value2); @@ -166,7 +161,8 @@ abstract class CacheTest extends MockedTest /** * @medium */ - function testTTL() { + function testTTL() + { $this->markTestSkipped('taking too much time without mocking'); $this->assertNull($this->instance->get('value1')); @@ -183,10 +179,13 @@ abstract class CacheTest extends MockedTest /** * @small + * * @param $data mixed the data to store in the cache + * * @dataProvider dataTypesInCache */ - function testDifferentTypesInCache($data) { + function testDifferentTypesInCache($data) + { $this->instance->set('val', $data); $received = $this->instance->get('val'); $this->assertEquals($data, $received, 'Value type changed from ' . gettype($data) . ' to ' . gettype($received)); @@ -194,12 +193,15 @@ abstract class CacheTest extends MockedTest /** * @small + * * @param mixed $value1 a first * @param mixed $value2 a second * @param mixed $value3 a third + * * @dataProvider dataSimple */ - public function testGetAllKeys($value1, $value2, $value3) { + public function testGetAllKeys($value1, $value2, $value3) + { if ($this->cache instanceof MemcachedCacheDriver) { $this->markTestSkipped('Memcached doesn\'t support getAllKeys anymore'); } diff --git a/tests/src/Core/Cache/DatabaseCacheDriverTest.php b/tests/src/Core/Cache/DatabaseCacheDriverTest.php index 2d29c2ad96..1aa9bf02b8 100644 --- a/tests/src/Core/Cache/DatabaseCacheDriverTest.php +++ b/tests/src/Core/Cache/DatabaseCacheDriverTest.php @@ -3,33 +3,40 @@ namespace Friendica\Test\src\Core\Cache; use Friendica\Core\Cache; -use Friendica\Factory\CacheDriverFactory; -use Friendica\Test\Util\DbaCacheMockTrait; +use Friendica\Factory\ConfigFactory; +use Friendica\Test\DatabaseTestTrait; +use Friendica\Test\Util\Database\StaticDatabase; +use Friendica\Test\Util\VFSTrait; +use Friendica\Util\ConfigFileLoader; +use Friendica\Util\Profiler; +use Psr\Log\NullLogger; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class DatabaseCacheDriverTest extends CacheTest { - use DbaCacheMockTrait; + use DatabaseTestTrait; + use VFSTrait; - public function setUp() + protected function setUp() { - $this->mockUtcNow($this->startTime); - - $this->mockConnected(); - $this->mockConnect(); - - // The first "clear" at setup - $this->mockClear(false, true, 2); + $this->setUpVfsDir(); parent::setUp(); } protected function getInstance() { - $this->cache = CacheDriverFactory::create('database'); + $logger = new NullLogger(); + $profiler = \Mockery::mock(Profiler::class); + $profiler->shouldReceive('saveTimestamp')->withAnyArgs()->andReturn(true); + + // load real config to avoid mocking every config-entry which is related to the Database class + $configFactory = new ConfigFactory(); + $loader = new ConfigFileLoader($this->root->url()); + $configCache = $configFactory->createCache($loader); + + $dba = new StaticDatabase($configCache, $profiler, $logger); + + $this->cache = new Cache\DatabaseCacheDriver('database', $dba); return $this->cache; } @@ -38,104 +45,4 @@ class DatabaseCacheDriverTest extends CacheTest $this->cache->clear(false); parent::tearDown(); } - - /** - * {@inheritdoc} - * @dataProvider dataSimple - */ - public function testSimple($value1, $value2) - { - // assertNull - $this->mockGet('value1', null, $this->startTime, 1); - - // assertEquals - $this->mockSet('value1', $value1, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockGet('value1', $value1, $this->startTime, 1); - - // assertEquals - $this->mockSet('value1', $value2, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockGet('value1', $value2, $this->startTime, 1); - - // assertEquals - $this->mockSet('value2', $value1, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockGet('value2', $value1, $this->startTime, 1); - - // assertNull - $this->mockGet('not_set', null, $this->startTime, 1); - - // assertNull - $this->mockDelete('value1', true, 1); - $this->mockGet('value1', null, $this->startTime, 1); - - parent::testSimple($value1, $value2); - } - - /** - * {@inheritdoc} - * @dataProvider dataSimple - */ - public function testClear($value1, $value2, $value3, $value4) - { - // assert Equals - $this->mockSet('1_value1', $value1, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockSet('1_value2', $value2, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockSet('2_value1', $value3, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockSet('3_value1', $value4, Cache::FIVE_MINUTES, $this->startTime, true, 1); - - $this->mockGet('1_value1', $value1, $this->startTime, 2); - $this->mockGet('1_value2', $value2, $this->startTime, 2); - $this->mockGet('2_value1', $value3, $this->startTime, 2); - $this->mockGet('3_value1', $value4, $this->startTime, 2); - - // assertTrue - $this->mockClear(true, true, 1); - $this->mockClear(false, true, 1); - - // assertEquals - $this->mockGet('1_value1', null, $this->startTime, 1); - $this->mockGet('1_value2', null, $this->startTime, 1); - $this->mockGet('2_value3', null, $this->startTime, 1); - $this->mockGet('3_value4', null, $this->startTime, 1); - - parent::testClear($value1, $value2, $value3, $value4); - } - - /** - * {@inheritdoc} - * @dataProvider dataTypesInCache - */ - public function testDifferentTypesInCache($data) - { - $this->mockSet('val', $data, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockGet('val', $data, $this->startTime, 1); - - parent::testDifferentTypesInCache($data); - } - - /** - * {@inheritdoc} - * @dataProvider dataSimple - */ - public function testGetAllKeys($value1, $value2, $value3) - { - $this->mockSet('value1', $value1, Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockSet('value2', $value2,Cache::FIVE_MINUTES, $this->startTime, true, 1); - $this->mockSet('test_value3', $value3, Cache::FIVE_MINUTES, $this->startTime, true, 1); - - $result = [ - ['k' => 'value1'], - ['k' => 'value2'], - ['k' => 'test_value3'], - ]; - - $this->mockGetAllKeys(null, $result, $this->startTime, 1); - - $result = [ - ['k' => 'test_value3'], - ]; - - $this->mockGetAllKeys('test', $result, $this->startTime, 1); - - parent::testGetAllKeys($value1, $value2, $value3); - } } diff --git a/tests/src/Core/Cache/MemcacheCacheDriverTest.php b/tests/src/Core/Cache/MemcacheCacheDriverTest.php index f8de88ac95..31aa0b4852 100644 --- a/tests/src/Core/Cache/MemcacheCacheDriverTest.php +++ b/tests/src/Core/Cache/MemcacheCacheDriverTest.php @@ -1,9 +1,9 @@ configMock + $configMock = \Mockery::mock(Configuration::class); + + $configMock ->shouldReceive('get') ->with('system', 'memcache_host') ->andReturn('localhost'); - - $this->configMock + $configMock ->shouldReceive('get') ->with('system', 'memcache_port') ->andReturn(11211); - $this->cache = CacheDriverFactory::create('memcache'); + $this->cache = new MemcacheCacheDriver('localhost', $configMock); return $this->cache; - } public function tearDown() diff --git a/tests/src/Core/Cache/MemcachedCacheDriverTest.php b/tests/src/Core/Cache/MemcachedCacheDriverTest.php index 9f0ed8d4fe..ade3886841 100644 --- a/tests/src/Core/Cache/MemcachedCacheDriverTest.php +++ b/tests/src/Core/Cache/MemcachedCacheDriverTest.php @@ -3,7 +3,9 @@ namespace Friendica\Test\src\Core\Cache; -use Friendica\Factory\CacheDriverFactory; +use Friendica\Core\Cache\MemcachedCacheDriver; +use Friendica\Core\Config\Configuration; +use Psr\Log\NullLogger; /** * @requires extension memcached @@ -12,12 +14,16 @@ class MemcachedCacheDriverTest extends MemoryCacheTest { protected function getInstance() { - $this->configMock + $configMock = \Mockery::mock(Configuration::class); + + $configMock ->shouldReceive('get') ->with('system', 'memcached_hosts') ->andReturn([0 => 'localhost, 11211']); - $this->cache = CacheDriverFactory::create('memcached'); + $logger = new NullLogger(); + + $this->cache = new MemcachedCacheDriver('localhost', $configMock, $logger); return $this->cache; } diff --git a/tests/src/Core/Cache/MemoryCacheTest.php b/tests/src/Core/Cache/MemoryCacheTest.php index 6688153b05..0bd7617dd0 100644 --- a/tests/src/Core/Cache/MemoryCacheTest.php +++ b/tests/src/Core/Cache/MemoryCacheTest.php @@ -3,8 +3,6 @@ namespace Friendica\Test\src\Core\Cache; use Friendica\Core\Cache\IMemoryCacheDriver; -use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; abstract class MemoryCacheTest extends CacheTest { @@ -17,11 +15,6 @@ abstract class MemoryCacheTest extends CacheTest { parent::setUp(); - $logger = new NullLogger(); - $this->dice->shouldReceive('create') - ->with(LoggerInterface::class) - ->andReturn($logger); - if (!($this->instance instanceof IMemoryCacheDriver)) { throw new \Exception('MemoryCacheTest unsupported'); } @@ -31,7 +24,8 @@ abstract class MemoryCacheTest extends CacheTest * @small * @dataProvider dataSimple */ - function testCompareSet($value1, $value2) { + function testCompareSet($value1, $value2) + { $this->assertNull($this->instance->get('value1')); $this->instance->add('value1', $value1); @@ -47,7 +41,8 @@ abstract class MemoryCacheTest extends CacheTest * @small * @dataProvider dataSimple */ - function testNegativeCompareSet($value1, $value2) { + function testNegativeCompareSet($value1, $value2) + { $this->assertNull($this->instance->get('value1')); $this->instance->add('value1', $value1); @@ -64,7 +59,8 @@ abstract class MemoryCacheTest extends CacheTest * @small * @dataProvider dataSimple */ - function testCompareDelete($data) { + function testCompareDelete($data) + { $this->assertNull($this->instance->get('value1')); $this->instance->add('value1', $data); @@ -78,7 +74,8 @@ abstract class MemoryCacheTest extends CacheTest * @small * @dataProvider dataSimple */ - function testNegativeCompareDelete($data) { + function testNegativeCompareDelete($data) + { $this->assertNull($this->instance->get('value1')); $this->instance->add('value1', $data); @@ -95,7 +92,8 @@ abstract class MemoryCacheTest extends CacheTest * @small * @dataProvider dataSimple */ - function testAdd($value1, $value2) { + function testAdd($value1, $value2) + { $this->assertNull($this->instance->get('value1')); $this->instance->add('value1', $value1); @@ -111,4 +109,4 @@ abstract class MemoryCacheTest extends CacheTest $this->assertEquals($value2, $received, 'Value was not overwritten by add'); $this->assertNotEquals($value1, $received, 'Value was not overwritten by any other value'); } -} \ No newline at end of file +} diff --git a/tests/src/Core/Cache/RedisCacheDriverTest.php b/tests/src/Core/Cache/RedisCacheDriverTest.php index 6e11c3b8a4..999261834f 100644 --- a/tests/src/Core/Cache/RedisCacheDriverTest.php +++ b/tests/src/Core/Cache/RedisCacheDriverTest.php @@ -3,7 +3,8 @@ namespace Friendica\Test\src\Core\Cache; -use Friendica\Factory\CacheDriverFactory; +use Friendica\Core\Cache\RedisCacheDriver; +use Friendica\Core\Config\Configuration; /** * @requires extension redis @@ -12,27 +13,27 @@ class RedisCacheDriverTest extends MemoryCacheTest { protected function getInstance() { - $this->configMock + $configMock = \Mockery::mock(Configuration::class); + + $configMock ->shouldReceive('get') ->with('system', 'redis_host') ->andReturn('localhost'); - - $this->configMock + $configMock ->shouldReceive('get') ->with('system', 'redis_port') ->andReturn(null); - $this->configMock + $configMock ->shouldReceive('get') - ->with('system', 'redis_db') + ->with('system', 'redis_db', 0) ->andReturn(3); - - $this->configMock + $configMock ->shouldReceive('get') ->with('system', 'redis_password') ->andReturn(null); - $this->cache = CacheDriverFactory::create('redis'); + $this->cache = new RedisCacheDriver('localhost', $configMock); return $this->cache; } diff --git a/tests/src/Core/Lock/APCuCacheLockDriverTest.php b/tests/src/Core/Lock/APCuCacheLockDriverTest.php index 6e185d6b9a..70796b2698 100644 --- a/tests/src/Core/Lock/APCuCacheLockDriverTest.php +++ b/tests/src/Core/Lock/APCuCacheLockDriverTest.php @@ -2,7 +2,6 @@ namespace Friendica\Test\src\Core\Lock; - use Friendica\Core\Cache\APCuCache; use Friendica\Core\Lock\CacheLockDriver; @@ -19,6 +18,6 @@ class APCuCacheLockDriverTest extends LockTest protected function getInstance() { - return new CacheLockDriver(new APCuCache()); + return new CacheLockDriver(new APCuCache('localhost')); } } diff --git a/tests/src/Core/Lock/ArrayCacheLockDriverTest.php b/tests/src/Core/Lock/ArrayCacheLockDriverTest.php index 671341718b..9b74cd5670 100644 --- a/tests/src/Core/Lock/ArrayCacheLockDriverTest.php +++ b/tests/src/Core/Lock/ArrayCacheLockDriverTest.php @@ -2,7 +2,6 @@ namespace Friendica\Test\src\Core\Lock; - use Friendica\Core\Cache\ArrayCache; use Friendica\Core\Lock\CacheLockDriver; @@ -10,7 +9,7 @@ class ArrayCacheLockDriverTest extends LockTest { protected function getInstance() { - return new CacheLockDriver(new ArrayCache()); + return new CacheLockDriver(new ArrayCache('localhost')); } public function testLockTTL() diff --git a/tests/src/Core/Lock/DatabaseLockDriverTest.php b/tests/src/Core/Lock/DatabaseLockDriverTest.php index 297e76d50b..f1c8376af1 100644 --- a/tests/src/Core/Lock/DatabaseLockDriverTest.php +++ b/tests/src/Core/Lock/DatabaseLockDriverTest.php @@ -2,117 +2,42 @@ namespace Friendica\Test\src\Core\Lock; -use Friendica\Core\Cache; use Friendica\Core\Lock\DatabaseLockDriver; -use Friendica\Test\Util\DbaLockMockTrait; +use Friendica\Factory\ConfigFactory; +use Friendica\Test\DatabaseTestTrait; +use Friendica\Test\Util\Database\StaticDatabase; +use Friendica\Test\Util\VFSTrait; +use Friendica\Util\ConfigFileLoader; +use Friendica\Util\Profiler; +use Psr\Log\NullLogger; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class DatabaseLockDriverTest extends LockTest { - use DbaLockMockTrait; + use VFSTrait; + use DatabaseTestTrait; protected $pid = 123; protected function setUp() { - $this->mockConnected(); - $this->mockConnect(); - - $this->mockReleaseAll($this->pid, 2); + $this->setUpVfsDir(); parent::setUp(); } protected function getInstance() { - return new DatabaseLockDriver($this->pid); - } + $logger = new NullLogger(); + $profiler = \Mockery::mock(Profiler::class); + $profiler->shouldReceive('saveTimestamp')->withAnyArgs()->andReturn(true); - public function testLock() - { - $this->mockIsLocked('foo', false, $this->startTime, 1); - $this->mockAcquireLock('foo', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - $this->mockIsLocked('foo', true, $this->startTime, 1); - $this->mockIsLocked('bar', false, $this->startTime, 1); + // load real config to avoid mocking every config-entry which is related to the Database class + $configFactory = new ConfigFactory(); + $loader = new ConfigFileLoader($this->root->url()); + $configCache = $configFactory->createCache($loader); - parent::testLock(); - } + $dba = new StaticDatabase($configCache, $profiler, $logger); - public function testDoubleLock() - { - $this->mockIsLocked('foo', false, $this->startTime, 1); - $this->mockAcquireLock('foo', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - $this->mockIsLocked('foo', true, $this->startTime, 1); - $this->mockAcquireLock('foo', Cache::FIVE_MINUTES, true, $this->pid, true, $this->startTime, 1); - - parent::testDoubleLock(); - } - - public function testReleaseLock() - { - $this->mockIsLocked('foo', false, $this->startTime, 1); - $this->mockAcquireLock('foo', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - $this->mockIsLocked('foo', true, $this->startTime, 1); - $this->mockReleaseLock('foo', $this->pid, 1); - $this->mockIsLocked('foo', false, $this->startTime, 1); - - parent::testReleaseLock(); - } - - public function testReleaseAll() - { - $this->mockAcquireLock('foo', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - $this->mockAcquireLock('bar', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - $this->mockAcquireLock('nice', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - - $this->mockIsLocked('foo', true, $this->startTime, 1); - $this->mockIsLocked('bar', true, $this->startTime, 1); - $this->mockIsLocked('nice', true, $this->startTime, 1); - - $this->mockReleaseAll($this->pid, 1); - - $this->mockIsLocked('foo', false, $this->startTime, 1); - $this->mockIsLocked('bar', false, $this->startTime, 1); - $this->mockIsLocked('nice', false, $this->startTime, 1); - - parent::testReleaseAll(); - } - - public function testReleaseAfterUnlock() - { - $this->mockIsLocked('foo', false, $this->startTime, 1); - $this->mockIsLocked('bar', false, $this->startTime, 1); - $this->mockIsLocked('nice', false, $this->startTime, 1); - - $this->mockAcquireLock('foo', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - $this->mockAcquireLock('bar', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - $this->mockAcquireLock('nice', Cache::FIVE_MINUTES, false, $this->pid, false, $this->startTime, 1); - - $this->mockReleaseLock('foo', $this->pid, 1); - - $this->mockIsLocked('foo', false, $this->startTime, 1); - $this->mockIsLocked('bar', true, $this->startTime, 1); - $this->mockIsLocked('nice', true, $this->startTime, 1); - - $this->mockReleaseAll($this->pid, 1); - - $this->mockIsLocked('bar', false, $this->startTime, 1); - $this->mockIsLocked('nice', false, $this->startTime, 1); - - parent::testReleaseAfterUnlock(); - } - - public function testReleaseWitTTL() - { - $this->mockIsLocked('test', false, $this->startTime, 1); - $this->mockAcquireLock('test', 10, false, $this->pid, false, $this->startTime, 1); - $this->mockIsLocked('test', true, $this->startTime, 1); - $this->mockReleaseLock('test', $this->pid, 1); - $this->mockIsLocked('test', false, $this->startTime, 1); - - parent::testReleaseWitTTL(); + return new DatabaseLockDriver($dba, $this->pid); } } diff --git a/tests/src/Core/Lock/LockTest.php b/tests/src/Core/Lock/LockTest.php index 59d6ee2d5d..5eb409539b 100644 --- a/tests/src/Core/Lock/LockTest.php +++ b/tests/src/Core/Lock/LockTest.php @@ -3,16 +3,9 @@ namespace Friendica\Test\src\Core\Lock; use Friendica\Test\MockedTest; -use Friendica\Test\Util\AppMockTrait; -use Friendica\Test\Util\VFSTrait; -use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; abstract class LockTest extends MockedTest { - use VFSTrait; - use AppMockTrait; - /** * @var int Start time of the mock (used for time operations) */ @@ -27,19 +20,8 @@ abstract class LockTest extends MockedTest protected function setUp() { - // Reusable App object - $this->setUpVfsDir(); - $this->mockApp($this->root); - $this->app - ->shouldReceive('getHostname') - ->andReturn('friendica.local'); - - $logger = new NullLogger(); - $this->dice->shouldReceive('create') - ->with(LoggerInterface::class) - ->andReturn($logger); - parent::setUp(); + $this->instance = $this->getInstance(); $this->instance->releaseAll(); } @@ -53,7 +35,8 @@ abstract class LockTest extends MockedTest /** * @small */ - public function testLock() { + public function testLock() + { $this->assertFalse($this->instance->isLocked('foo')); $this->assertTrue($this->instance->acquireLock('foo', 1)); $this->assertTrue($this->instance->isLocked('foo')); @@ -63,7 +46,8 @@ abstract class LockTest extends MockedTest /** * @small */ - public function testDoubleLock() { + public function testDoubleLock() + { $this->assertFalse($this->instance->isLocked('foo')); $this->assertTrue($this->instance->acquireLock('foo', 1)); $this->assertTrue($this->instance->isLocked('foo')); @@ -74,7 +58,8 @@ abstract class LockTest extends MockedTest /** * @small */ - public function testReleaseLock() { + public function testReleaseLock() + { $this->assertFalse($this->instance->isLocked('foo')); $this->assertTrue($this->instance->acquireLock('foo', 1)); $this->assertTrue($this->instance->isLocked('foo')); @@ -85,7 +70,8 @@ abstract class LockTest extends MockedTest /** * @small */ - public function testReleaseAll() { + public function testReleaseAll() + { $this->assertTrue($this->instance->acquireLock('foo', 1)); $this->assertTrue($this->instance->acquireLock('bar', 1)); $this->assertTrue($this->instance->acquireLock('nice', 1)); @@ -104,7 +90,8 @@ abstract class LockTest extends MockedTest /** * @small */ - public function testReleaseAfterUnlock() { + public function testReleaseAfterUnlock() + { $this->assertFalse($this->instance->isLocked('foo')); $this->assertFalse($this->instance->isLocked('bar')); $this->assertFalse($this->instance->isLocked('nice')); @@ -139,7 +126,8 @@ abstract class LockTest extends MockedTest /** * @medium */ - function testLockTTL() { + function testLockTTL() + { $this->markTestSkipped('taking too much time without mocking'); $this->assertFalse($this->instance->isLocked('foo')); diff --git a/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php b/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php index 8d32ad527d..8c723f34ed 100644 --- a/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php +++ b/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php @@ -3,7 +3,8 @@ namespace Friendica\Test\src\Core\Lock; -use Friendica\Factory\CacheDriverFactory; +use Friendica\Core\Cache\MemcacheCacheDriver; +use Friendica\Core\Config\Configuration; use Friendica\Core\Lock\CacheLockDriver; /** @@ -13,16 +14,17 @@ class MemcacheCacheLockDriverTest extends LockTest { protected function getInstance() { - $this->configMock + $configMock = \Mockery::mock(Configuration::class); + + $configMock ->shouldReceive('get') ->with('system', 'memcache_host') ->andReturn('localhost'); - - $this->configMock + $configMock ->shouldReceive('get') ->with('system', 'memcache_port') ->andReturn(11211); - return new CacheLockDriver(CacheDriverFactory::create('memcache')); + return new CacheLockDriver(new MemcacheCacheDriver('localhost', $configMock)); } } diff --git a/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php b/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php index f08ffa3817..7685d816de 100644 --- a/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php +++ b/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php @@ -3,8 +3,10 @@ namespace Friendica\Test\src\Core\Lock; -use Friendica\Factory\CacheDriverFactory; +use Friendica\Core\Cache\MemcachedCacheDriver; +use Friendica\Core\Config\Configuration; use Friendica\Core\Lock\CacheLockDriver; +use Psr\Log\NullLogger; /** * @requires extension memcached @@ -13,11 +15,15 @@ class MemcachedCacheLockDriverTest extends LockTest { protected function getInstance() { - $this->configMock + $configMock = \Mockery::mock(Configuration::class); + + $configMock ->shouldReceive('get') ->with('system', 'memcached_hosts') ->andReturn([0 => 'localhost, 11211']); - return new CacheLockDriver(CacheDriverFactory::create('memcached')); + $logger = new NullLogger(); + + return new CacheLockDriver(new MemcachedCacheDriver('localhost', $configMock, $logger)); } } diff --git a/tests/src/Core/Lock/RedisCacheLockDriverTest.php b/tests/src/Core/Lock/RedisCacheLockDriverTest.php index 21bace5018..fdc961ccc8 100644 --- a/tests/src/Core/Lock/RedisCacheLockDriverTest.php +++ b/tests/src/Core/Lock/RedisCacheLockDriverTest.php @@ -3,8 +3,9 @@ namespace Friendica\Test\src\Core\Lock; +use Friendica\Core\Cache\RedisCacheDriver; +use Friendica\Core\Config\Configuration; use Friendica\Core\Lock\CacheLockDriver; -use Friendica\Factory\CacheDriverFactory; /** * @requires extension redis @@ -13,26 +14,26 @@ class RedisCacheLockDriverTest extends LockTest { protected function getInstance() { - $this->configMock + $configMock = \Mockery::mock(Configuration::class); + + $configMock ->shouldReceive('get') ->with('system', 'redis_host') ->andReturn('localhost'); - - $this->configMock + $configMock ->shouldReceive('get') ->with('system', 'redis_port') ->andReturn(null); - $this->configMock + $configMock ->shouldReceive('get') - ->with('system', 'redis_db') + ->with('system', 'redis_db', 0) ->andReturn(3); - - $this->configMock + $configMock ->shouldReceive('get') ->with('system', 'redis_password') ->andReturn(null); - return new CacheLockDriver(CacheDriverFactory::create('redis')); + return new CacheLockDriver(new RedisCacheDriver('localhost', $configMock)); } } diff --git a/tests/src/Core/Lock/SemaphoreLockDriverTest.php b/tests/src/Core/Lock/SemaphoreLockDriverTest.php index a37fbffbed..d5aaa36ba9 100644 --- a/tests/src/Core/Lock/SemaphoreLockDriverTest.php +++ b/tests/src/Core/Lock/SemaphoreLockDriverTest.php @@ -2,6 +2,10 @@ namespace Friendica\Test\src\Core\Lock; +use Dice\Dice; +use Friendica\App; +use Friendica\BaseObject; +use Friendica\Core\Config\Configuration; use Friendica\Core\Lock\SemaphoreLockDriver; class SemaphoreLockDriverTest extends LockTest @@ -10,12 +14,21 @@ class SemaphoreLockDriverTest extends LockTest { parent::setUp(); - $this->app->shouldReceive('getHostname')->andReturn('friendica.local'); + $dice = \Mockery::mock(Dice::class)->makePartial(); - $this->configMock + $app = \Mockery::mock(App::class); + $app->shouldReceive('getHostname')->andReturn('friendica.local'); + $dice->shouldReceive('create')->with(App::class)->andReturn($app); + + $configMock = \Mockery::mock(Configuration::class); + $configMock ->shouldReceive('get') - ->with('system', 'temppath') + ->with('system', 'temppath', NULL, false) ->andReturn('/tmp/'); + $dice->shouldReceive('create')->with(Configuration::class)->andReturn($configMock); + + // @todo Because "get_temppath()" is using static methods, we have to initialize the BaseObject + BaseObject::setDependencyInjection($dice); } protected function getInstance()