diff --git a/composer.json b/composer.json index 45294794..710b9cc2 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,9 @@ "bower-asset/jquery" : "^1.12", "bower-asset/jquery-timeago" : "^1.5", "bower-asset/js-cookie" : "^2.1", - "psr/simple-cache": "3.0.x-dev" + "psr/simple-cache" : "^1.0", + "sabre/cache" : "^2.0", + "naroga/redis-cache": "dev-master" }, "require-dev" : { diff --git a/composer.lock b/composer.lock index 73078ee6..cb4a0aed 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c5b40df44e9d52a91768469533768052", + "content-hash": "e7d482b23052d4e764a4f2c076865bc5", "packages": [ { "name": "bower-asset/jquery", @@ -283,27 +283,140 @@ "type": "library" }, { - "name": "psr/simple-cache", + "name": "naroga/redis-cache", "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "2d280c2aaa23a120f35d55cfde8581954a8e77fa" + "url": "https://github.com/naroga/redis-cache.git", + "reference": "c32ee4ce2efcf8292cac6b6192c17c0306320d04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/2d280c2aaa23a120f35d55cfde8581954a8e77fa", - "reference": "2d280c2aaa23a120f35d55cfde8581954a8e77fa", + "url": "https://api.github.com/repos/naroga/redis-cache/zipball/c32ee4ce2efcf8292cac6b6192c17c0306320d04", + "reference": "c32ee4ce2efcf8292cac6b6192c17c0306320d04", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=5.3.3", + "predis/predis": "^1.1", + "psr/simple-cache": "~1.0" + }, + "require-dev": { + "phpunit/php-code-coverage": ">=2.2.4", + "phpunit/phpunit": ">=3.7.38", + "satooshi/php-coveralls": ">=1.0.1" }, "default-branch": true, "type": "library", + "autoload": { + "psr-4": { + "Naroga\\RedisCache\\": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pedro Cordeiro", + "email": "pedro.cordeiro@sympla.com.br" + } + ], + "description": "A Redis driver that implements PSR-16 (Simple Cache)", + "support": { + "issues": "https://github.com/naroga/redis-cache/issues", + "source": "https://github.com/naroga/redis-cache/tree/1.2" + }, + "time": "2021-01-25T13:15:08+00:00" + }, + { + "name": "predis/predis", + "version": "v1.1.x-dev", + "source": { + "type": "git", + "url": "https://github.com/predis/predis.git", + "reference": "bb8cce7bcf0d790dd17dde01922230d411efb99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/predis/predis/zipball/bb8cce7bcf0d790dd17dde01922230d411efb99b", + "reference": "bb8cce7bcf0d790dd17dde01922230d411efb99b", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net", + "role": "Creator & Maintainer" + }, + { + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/predis/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "support": { + "issues": "https://github.com/predis/predis/issues", + "source": "https://github.com/predis/predis/tree/v1.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "time": "2023-01-10T16:48:39+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -318,7 +431,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "homepage": "http://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -332,7 +445,70 @@ "support": { "source": "https://github.com/php-fig/simple-cache/tree/master" }, - "time": "2022-04-08T16:41:45+00:00" + "time": "2017-10-23T01:57:42+00:00" + }, + { + "name": "sabre/cache", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sabre-io/cache.git", + "reference": "8120da1320b4e0288fa2ff49cebb1a8221641faa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sabre-io/cache/zipball/8120da1320b4e0288fa2ff49cebb1a8221641faa", + "reference": "8120da1320b4e0288fa2ff49cebb1a8221641faa", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/simple-cache": "^1.0" + }, + "provide": { + "psr/simple-cache-implementation": "~1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.10.0", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Sabre\\Cache\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "https://evertpot.com/", + "role": "Developer" + } + ], + "description": "Simple cache abstraction layer implementing PSR-16", + "homepage": "http://sabre.io/dav/", + "keywords": [ + "apc", + "apcu", + "cache", + "memcache", + "memcached", + "psr-16", + "sabre", + "simple-cache" + ], + "support": { + "forum": "https://groups.google.com/group/sabredav-discuss", + "issues": "https://github.com/sabre-io/cache/issues", + "source": "https://github.com/fruux/sabre-skel" + }, + "time": "2022-08-23T08:05:52+00:00" }, { "name": "shish/eventtracer-php", @@ -585,16 +761,16 @@ }, { "name": "webonyx/graphql-php", - "version": "v15.0.1", + "version": "v15.0.2", "source": { "type": "git", "url": "https://github.com/webonyx/graphql-php.git", - "reference": "15fb39ba17faa332a2001aed5c1472117ec5abc3" + "reference": "ff5bc60dad8a87b44ca739487b5de1e27c81e7ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/15fb39ba17faa332a2001aed5c1472117ec5abc3", - "reference": "15fb39ba17faa332a2001aed5c1472117ec5abc3", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/ff5bc60dad8a87b44ca739487b5de1e27c81e7ef", + "reference": "ff5bc60dad8a87b44ca739487b5de1e27c81e7ef", "shasum": "" }, "require": { @@ -610,16 +786,16 @@ "nyholm/psr7": "^1.5", "phpbench/phpbench": "^1.2", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "1.9.8", + "phpstan/phpstan": "1.9.14", "phpstan/phpstan-phpunit": "1.3.3", - "phpstan/phpstan-strict-rules": "1.4.4", + "phpstan/phpstan-strict-rules": "1.4.5", "phpunit/phpunit": "^9.5", "psr/http-message": "^1", "react/http": "^1.6", "react/promise": "^2.9", "symfony/polyfill-php81": "^1.23", "symfony/var-exporter": "^5 || ^6", - "thecodingmachine/safe": "^1.3" + "thecodingmachine/safe": "^1.3 || ^2" }, "suggest": { "psr/http-message": "To use standard GraphQL server", @@ -643,7 +819,7 @@ ], "support": { "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v15.0.1" + "source": "https://github.com/webonyx/graphql-php/tree/v15.0.2" }, "funding": [ { @@ -651,7 +827,7 @@ "type": "open_collective" } ], - "time": "2023-01-11T14:57:18+00:00" + "time": "2023-02-02T14:16:39+00:00" } ], "packages-dev": [ @@ -3778,12 +3954,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "b7a390ae3651f7ba3675d8364bff396e87931554" + "reference": "f7adbde0c6a1f761f9a005bda39b7fdbf2d16bad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/b7a390ae3651f7ba3675d8364bff396e87931554", - "reference": "b7a390ae3651f7ba3675d8364bff396e87931554", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/f7adbde0c6a1f761f9a005bda39b7fdbf2d16bad", + "reference": "f7adbde0c6a1f761f9a005bda39b7fdbf2d16bad", "shasum": "" }, "require": { @@ -3825,7 +4001,7 @@ "type": "github" } ], - "time": "2022-06-14T05:05:56+00:00" + "time": "2023-02-02T11:26:30+00:00" }, { "name": "sebastian/type", @@ -3942,12 +4118,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f7a519afe4e2763051b10856c20b5f0404914ec4" + "reference": "e0fd302dcd8b9407d3f0f289cdfe7450e08a25f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f7a519afe4e2763051b10856c20b5f0404914ec4", - "reference": "f7a519afe4e2763051b10856c20b5f0404914ec4", + "url": "https://api.github.com/repos/symfony/console/zipball/e0fd302dcd8b9407d3f0f289cdfe7450e08a25f7", + "reference": "e0fd302dcd8b9407d3f0f289cdfe7450e08a25f7", "shasum": "" }, "require": { @@ -4030,7 +4206,7 @@ "type": "tidelift" } ], - "time": "2023-01-13T09:23:11+00:00" + "time": "2023-02-02T06:55:11+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5306,7 +5482,7 @@ "minimum-stability": "dev", "stability-flags": { "shish/gqla": 20, - "psr/simple-cache": 20, + "naroga/redis-cache": 20, "scrutinizer/ocular": 20 }, "prefer-stable": false, diff --git a/core/cacheengine.php b/core/cacheengine.php index 0bdbf6cc..4ca6c2a2 100644 --- a/core/cacheengine.php +++ b/core/cacheengine.php @@ -4,238 +4,116 @@ declare(strict_types=1); namespace Shimmie2; -interface CacheEngine +use Psr\SimpleCache\CacheInterface; + +class EventTracingCache implements CacheInterface { - public function get(string $key); - public function set(string $key, $val, int $time=0): void; - public function delete(string $key): void; -} + private CacheInterface $engine; + private \EventTracer $tracer; + private int $hits=0; + private int $misses=0; -class NoCache implements CacheEngine -{ - public function get(string $key) + public function __construct(CacheInterface $engine, \EventTracer $tracer) { - return false; - } - public function set(string $key, $val, int $time=0): void - { - } - public function delete(string $key): void - { - } -} - -class MemcachedCache implements CacheEngine -{ - public ?\Memcached $memcache=null; - - public function __construct(string $args) - { - $hp = explode(":", $args); - $this->memcache = new \Memcached(); - #$this->memcache->setOption(\Memcached::OPT_COMPRESSION, False); - #$this->memcache->setOption(\Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP); - #$this->memcache->setOption(\Memcached::OPT_PREFIX_KEY, phpversion()); - $this->memcache->addServer($hp[0], (int)$hp[1]); + $this->engine = $engine; + $this->tracer = $tracer; } - public function get(string $key) + public function get($key, $default=null) { - $key = urlencode($key); + if($key === "__etc_cache_hits") return $this->hits; + if($key === "__etc_cache_misses") return $this->misses; - $val = $this->memcache->get($key); - $res = $this->memcache->getResultCode(); - - if ($res == \Memcached::RES_SUCCESS) { - return $val; - } elseif ($res == \Memcached::RES_NOTFOUND) { - return false; - } else { - error_log("Memcached error during get($key): $res"); - return false; - } - } - - public function set(string $key, $val, int $time=0): void - { - $key = urlencode($key); - - $this->memcache->set($key, $val, $time); - $res = $this->memcache->getResultCode(); - if ($res != \Memcached::RES_SUCCESS) { - error_log("Memcached error during set($key): $res"); - } - } - - public function delete(string $key): void - { - $key = urlencode($key); - - $this->memcache->delete($key); - $res = $this->memcache->getResultCode(); - if ($res != \Memcached::RES_SUCCESS && $res != \Memcached::RES_NOTFOUND) { - error_log("Memcached error during delete($key): $res"); - } - } -} - -class APCCache implements CacheEngine -{ - public function __construct(string $args) - { - // $args is not used, but is passed in when APC cache is created. - } - - public function get(string $key) - { - return apc_fetch($key); - } - - public function set(string $key, $val, int $time=0): void - { - apc_store($key, $val, $time); - } - - public function delete(string $key): void - { - apc_delete($key); - } -} - -class RedisCache implements CacheEngine -{ - private \Redis $redis; - - public function __construct(string $args) - { - $this->redis = new \Redis(); - $hp = explode(":", $args); - $this->redis->pconnect($hp[0], (int)$hp[1]); - $this->redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP); - $this->redis->setOption(\Redis::OPT_PREFIX, 'shm:'); - } - - public function get(string $key) - { - return $this->redis->get($key); - } - - public function set(string $key, $val, int $time=0): void - { - if ($time > 0) { - $this->redis->setEx($key, $time, $val); - } else { - $this->redis->set($key, $val); - } - } - - public function delete(string $key): void - { - $this->redis->del($key); - } -} - -class CacheWithStats implements \Psr\SimpleCache\CacheInterface -{ - public $engine; - public int $hits=0; - public int $misses=0; - public int $time=0; - - public function __construct(CacheEngine $c) - { - $this->engine = $c; - } - - public function get(string $key, mixed $default=null): mixed - { - global $_tracer; - $_tracer->begin("Cache Query", ["key"=>$key]); - $val = $this->engine->get($key); - if ($val !== false) { + $this->tracer->begin("Cache Query", ["key"=>$key]); + $val = $this->engine->get($key, $default); + if ($val != $default) { $res = "hit"; $this->hits++; } else { $res = "miss"; - $val = $default; $this->misses++; } - $_tracer->end(null, ["result"=>$res]); + $this->tracer->end(null, ["result"=>$res]); return $val; } - public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool + public function set($key, $value, $ttl = null) { - global $_tracer; - $_tracer->begin("Cache Set", ["key"=>$key, "ttl"=>$ttl]); - $this->engine->set($key, $value, $ttl ?? 0); - $_tracer->end(); - return true; + $this->tracer->begin("Cache Set", ["key"=>$key, "ttl"=>$ttl]); + $val = $this->engine->set($key, $value, $ttl); + $this->tracer->end(); + return $val; } - public function delete(string $key): bool + public function delete($key) { - global $_tracer; - $_tracer->begin("Cache Delete", ["key"=>$key]); - $this->engine->delete($key); - $_tracer->end(); - return true; + $this->tracer->begin("Cache Delete", ["key"=>$key]); + $val = $this->engine->delete($key); + $this->tracer->end(); + return $val; } - public function clear(): bool + public function clear() { - throw new Exception("Not implemented"); + $this->tracer->begin("Cache Clear"); + $val = $this->engine->clear(); + $this->tracer->end(); + return $val; } - public function getMultiple(iterable $keys, mixed $default = null): iterable + public function getMultiple($keys, $default = null) { - $results = []; - foreach($keys as $key) { - $results[$key] = $this->get($key, $default); - } - return $results; + $this->tracer->begin("Cache Get Multiple"); + $val = $this->engine->getMultiple($values, $default); + $this->tracer->end(); + return $val; } - public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool { - foreach($values as $key => $value) { - $this->set($key, $value, $ttl); - } - return true; + public function setMultiple($values, $ttl = null) { + $this->tracer->begin("Cache Set Multiple"); + $val = $this->engine->setMultiple($values, $ttl); + $this->tracer->end(); + return $val; } - public function deleteMultiple(iterable $keys): bool { - foreach($keys as $key) { - $this->delete($key); - } - } - public function has(string $key): bool { - $sentinel = 4345345735673; - return $this->get($key, $sentinel) != $sentinel; + public function deleteMultiple($keys) { + $this->tracer->begin("Cache Delete Multiple"); + $val = $this->engine->deleteMultiple($keys); + $this->tracer->end(); + return $val; } - public function get_hits(): int - { - return $this->hits; - } - public function get_misses(): int - { - return $this->misses; + public function has($key) { + $this->tracer->begin("Cache Has", ["key"=>$key]); + $val = $this->engine->has($key); + $this->tracer->end(null, ["exists"=>$val]); + return $val; } } -function loadCache(?string $dsn): CacheWithStats { +function loadCache(?string $dsn): CacheInterface { $matches = []; $c = null; if ($dsn && preg_match("#(.*)://(.*)#", $dsn, $matches) && !isset($_GET['DISABLE_CACHE'])) { if ($matches[1] == "memcached" || $matches[1] == "memcache") { - $c = new MemcachedCache($matches[2]); + $hp = explode(":", $matches[2]); + $memcache = new \Memcached(); + $memcache->addServer($hp[0], (int)$hp[1]); + $c = new \Sabre\Cache\Memcached($memcached); } elseif ($matches[1] == "apc") { - $c = new APCCache($matches[2]); + $c = new \Sabre\Cache\Apcu(); } elseif ($matches[1] == "redis") { - $c = new RedisCache($matches[2]); + $hp = explode(":", $matches[2]); + $redis = new \Predis\Client([ + 'scheme' => 'tcp', + 'host' => $hp[0], + 'port' => (int)$hp[1] + ], ['prefix' => 'shm:']); + $c = new \Naroga\RedisCache\Redis($redis); } } else { - $c = new NoCache(); + $c = new \Sabre\Cache\Memory(); } - return new CacheWithStats($c); + global $_tracer; + return new EventTracingCache($c, $_tracer); } diff --git a/core/util.php b/core/util.php index a83e17d8..894308d2 100644 --- a/core/util.php +++ b/core/util.php @@ -555,8 +555,8 @@ function get_debug_info_arr(): array "query_count" => $database->query_count, // "query_log" => $database->queries, "event_count" => $_shm_event_count, - "cache_hits" => $cache->get_hits(), - "cache_misses" => $cache->get_misses(), + "cache_hits" => $cache->get("__etc_cache_hits"), + "cache_misses" => $cache->get("__etc_cache_misses"), "version" => VERSION . $commit, ]; } diff --git a/ext/statsd/main.php b/ext/statsd/main.php index 33a4635d..b853f7b5 100644 --- a/ext/statsd/main.php +++ b/ext/statsd/main.php @@ -21,8 +21,8 @@ class StatsDInterface extends Extension StatsDInterface::$stats["shimmie.$type.files"] = count(get_included_files())."|c"; StatsDInterface::$stats["shimmie.$type.queries"] = $database->query_count."|c"; StatsDInterface::$stats["shimmie.$type.events"] = $_shm_event_count."|c"; - StatsDInterface::$stats["shimmie.$type.cache-hits"] = $cache->get_hits()."|c"; - StatsDInterface::$stats["shimmie.$type.cache-misses"] = $cache->get_misses()."|c"; + StatsDInterface::$stats["shimmie.$type.cache-hits"] = $cache->get("__etc_cache_hits", -1)."|c"; + StatsDInterface::$stats["shimmie.$type.cache-misses"] = $cache->get("__etc_cache_misses", -1)."|c"; } public function onPageRequest(PageRequestEvent $event)