From edbfd1109bbf8a5323e41198b551c10c703b93b9 Mon Sep 17 00:00:00 2001 From: Dennis Date: Sun, 6 Apr 2025 15:43:53 +0200 Subject: [PATCH] Add APCu cache driver (#9828) --- config/defaults.inc.php | 4 +- .../database_attachments/config.inc.php.dist | 2 +- program/lib/Roundcube/cache/apcu.php | 136 ++++++++++++++++++ program/lib/Roundcube/rcube.php | 2 +- program/lib/Roundcube/rcube_cache.php | 2 +- 5 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 program/lib/Roundcube/cache/apcu.php diff --git a/config/defaults.inc.php b/config/defaults.inc.php index aa9314d7c..42a8d5f54 100644 --- a/config/defaults.inc.php +++ b/config/defaults.inc.php @@ -240,7 +240,7 @@ $config['imap_disabled_caps'] = []; // This is used to relate IMAP session with Roundcube user sessions $config['imap_log_session'] = false; -// Type of IMAP indexes cache. Supported values: 'db', 'apc', 'redis' and 'memcache' or 'memcached'. +// Type of IMAP indexes cache. Supported values: 'db', 'apc', 'apcu', 'redis' and 'memcache' or 'memcached'. $config['imap_cache'] = null; // Enables messages cache. Only 'db' cache is supported. @@ -460,7 +460,7 @@ $config['oauth_password_claim'] = null; // LDAP // ---------------------------------- -// Type of LDAP cache. Supported values: 'db', 'apc' and 'memcache' or 'memcached'. +// Type of LDAP cache. Supported values: 'db', 'apc', 'apcu' and 'memcache' or 'memcached'. $config['ldap_cache'] = 'db'; // Lifetime of LDAP cache. Possible units: s, m, h, d, w diff --git a/plugins/database_attachments/config.inc.php.dist b/plugins/database_attachments/config.inc.php.dist index 29f6e78a0..5b3f556d3 100644 --- a/plugins/database_attachments/config.inc.php.dist +++ b/plugins/database_attachments/config.inc.php.dist @@ -4,7 +4,7 @@ $config = []; // By default this plugin stores attachments in filesystem // and copies them into sql database. -// You can change it to use 'memcache', 'memcached', 'redis' or 'apc'. +// You can change it to use 'memcache', 'memcached', 'redis', 'apc' or 'apcu'. // ----------------------------------------------------------- // WARNING: Remember to set max_allowed_packet in database or // config to match with expected max attachment size. diff --git a/program/lib/Roundcube/cache/apcu.php b/program/lib/Roundcube/cache/apcu.php new file mode 100644 index 000000000..9fc681661 --- /dev/null +++ b/program/lib/Roundcube/cache/apcu.php @@ -0,0 +1,136 @@ + | + | Author: Aleksander Machniak | + +-----------------------------------------------------------------------+ +*/ + +/** + * Interface implementation class for accessing APCu cache + */ +class rcube_cache_apcu extends rcube_cache +{ + /** + * Indicates if APCu module is enabled and in a required version + * + * @var bool + */ + protected $enabled; + + public function __construct($userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false) + { + parent::__construct($userid, $prefix, $ttl, $packed, $indexed); + + $rcube = rcube::get_instance(); + + $this->type = 'apcu'; + $this->enabled = function_exists('apcu_exists'); // APCu required (pecl install apcu) + $this->debug = $rcube->config->get('apcu_debug'); + } + + /** + * Remove cache records older than ttl + */ + #[Override] + public function expunge() + { + // No need for GC, entries are expunged automatically + } + + /** + * Remove expired records of all caches + */ + #[Override] + public static function gc() + { + // No need for GC, entries are expunged automatically + } + + /** + * Reads cache entry. + * + * @param string $key Cache internal key name + * + * @return mixed Cached value + */ + #[Override] + protected function get_item($key) + { + if (!$this->enabled) { + return false; + } + + $data = apcu_fetch($key); + + if ($this->debug) { + $this->debug('get', $key, $data); + } + + return $data; + } + + /** + * Adds entry into memcache/apc/apcu/redis DB. + * + * @param string $key Cache internal key name + * @param mixed $data Serialized cache data + * + * @return bool True on success, False on failure + */ + #[Override] + protected function add_item($key, $data) + { + if (!$this->enabled) { + return false; + } + + if (apcu_exists($key)) { + apcu_delete($key); + } + + $result = apcu_store($key, $data, $this->ttl); + + if ($this->debug) { + $this->debug('set', $key, $data, $result); + } + + return $result; + } + + /** + * Deletes entry from memcache/apc/apcu/redis DB. + * + * @param string $key Cache internal key name + * + * @return bool True on success, False on failure + */ + #[Override] + protected function delete_item($key) + { + if (!$this->enabled) { + return false; + } + + $result = apcu_delete($key); + + if ($this->debug) { + $this->debug('delete', $key, null, $result); + } + + return $result; + } +} diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 43d56f5ad..500495160 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -215,7 +215,7 @@ class rcube * Initialize and get user cache object * * @param string $name Cache identifier - * @param string $type Cache type ('db', 'apc', 'memcache', 'redis') + * @param string $type Cache type ('db', 'apc', 'apcu', 'memcache', 'redis') * @param string|int $ttl Expiration time for cache items * @param bool $packed Enables/disables data serialization * @param bool $indexed Use indexed cache diff --git a/program/lib/Roundcube/rcube_cache.php b/program/lib/Roundcube/rcube_cache.php index 23accc8ce..100ec8a92 100644 --- a/program/lib/Roundcube/rcube_cache.php +++ b/program/lib/Roundcube/rcube_cache.php @@ -46,7 +46,7 @@ class rcube_cache /** * Object factory * - * @param string $type Engine type ('db', 'memcache', 'apc', 'redis') + * @param string $type Engine type ('db', 'memcache', 'apc', 'apcu', 'redis') * @param int $userid User identifier * @param string $prefix Key name prefix * @param int|string $ttl Expiration time of memcache/apc items