diff options
Diffstat (limited to 'lib/private/Memcache/Memcached.php')
-rw-r--r-- | lib/private/Memcache/Memcached.php | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/lib/private/Memcache/Memcached.php b/lib/private/Memcache/Memcached.php new file mode 100644 index 00000000000..d8b624a978a --- /dev/null +++ b/lib/private/Memcache/Memcached.php @@ -0,0 +1,172 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Memcache; + +use OCP\HintException; +use OCP\IMemcache; + +class Memcached extends Cache implements IMemcache { + use CASTrait; + + /** + * @var \Memcached $cache + */ + private static $cache = null; + + use CADTrait; + + public function __construct($prefix = '') { + parent::__construct($prefix); + if (is_null(self::$cache)) { + self::$cache = new \Memcached(); + + $defaultOptions = [ + \Memcached::OPT_CONNECT_TIMEOUT => 50, + \Memcached::OPT_RETRY_TIMEOUT => 50, + \Memcached::OPT_SEND_TIMEOUT => 50, + \Memcached::OPT_RECV_TIMEOUT => 50, + \Memcached::OPT_POLL_TIMEOUT => 50, + + // Enable compression + \Memcached::OPT_COMPRESSION => true, + + // Turn on consistent hashing + \Memcached::OPT_LIBKETAMA_COMPATIBLE => true, + + // Enable Binary Protocol + \Memcached::OPT_BINARY_PROTOCOL => true, + ]; + /** + * By default enable igbinary serializer if available + * + * Psalm checks depend on if igbinary is installed or not with memcached + * @psalm-suppress RedundantCondition + * @psalm-suppress TypeDoesNotContainType + */ + if (\Memcached::HAVE_IGBINARY) { + $defaultOptions[\Memcached::OPT_SERIALIZER] + = \Memcached::SERIALIZER_IGBINARY; + } + $options = \OC::$server->getConfig()->getSystemValue('memcached_options', []); + if (is_array($options)) { + $options = $options + $defaultOptions; + self::$cache->setOptions($options); + } else { + throw new HintException("Expected 'memcached_options' config to be an array, got $options"); + } + + $servers = \OC::$server->getSystemConfig()->getValue('memcached_servers'); + if (!$servers) { + $server = \OC::$server->getSystemConfig()->getValue('memcached_server'); + if ($server) { + $servers = [$server]; + } else { + $servers = [['localhost', 11211]]; + } + } + self::$cache->addServers($servers); + } + } + + /** + * entries in XCache gets namespaced to prevent collisions between owncloud instances and users + */ + protected function getNameSpace() { + return $this->prefix; + } + + public function get($key) { + $result = self::$cache->get($this->getNameSpace() . $key); + if ($result === false and self::$cache->getResultCode() == \Memcached::RES_NOTFOUND) { + return null; + } else { + return $result; + } + } + + public function set($key, $value, $ttl = 0) { + if ($ttl > 0) { + $result = self::$cache->set($this->getNameSpace() . $key, $value, $ttl); + } else { + $result = self::$cache->set($this->getNameSpace() . $key, $value); + } + return $result || $this->isSuccess(); + } + + public function hasKey($key) { + self::$cache->get($this->getNameSpace() . $key); + return self::$cache->getResultCode() === \Memcached::RES_SUCCESS; + } + + public function remove($key) { + $result = self::$cache->delete($this->getNameSpace() . $key); + return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND; + } + + public function clear($prefix = '') { + // Newer Memcached doesn't like getAllKeys(), flush everything + self::$cache->flush(); + return true; + } + + /** + * Set a value in the cache if it's not already stored + * + * @param string $key + * @param mixed $value + * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 + * @return bool + */ + public function add($key, $value, $ttl = 0) { + $result = self::$cache->add($this->getPrefix() . $key, $value, $ttl); + return $result || $this->isSuccess(); + } + + /** + * Increase a stored number + * + * @param string $key + * @param int $step + * @return int | bool + */ + public function inc($key, $step = 1) { + $this->add($key, 0); + $result = self::$cache->increment($this->getPrefix() . $key, $step); + + if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) { + return false; + } + + return $result; + } + + /** + * Decrease a stored number + * + * @param string $key + * @param int $step + * @return int | bool + */ + public function dec($key, $step = 1) { + $result = self::$cache->decrement($this->getPrefix() . $key, $step); + + if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) { + return false; + } + + return $result; + } + + public static function isAvailable(): bool { + return extension_loaded('memcached'); + } + + private function isSuccess(): bool { + return self::$cache->getResultCode() === \Memcached::RES_SUCCESS; + } +} |