diff --git a/.upgradenotes/MDL-69684-2024061412181460.yml b/.upgradenotes/MDL-69684-2024061412181460.yml new file mode 100644 index 00000000000..5e9bc89b4f2 --- /dev/null +++ b/.upgradenotes/MDL-69684-2024061412181460.yml @@ -0,0 +1,7 @@ +issueNumber: MDL-69684 +notes: + core: + - message: >- + Redis session cache has been improved to make a single call where two + were used before. The minimum redis version is now 2.6.12. + type: improved diff --git a/lib/classes/session/redis.php b/lib/classes/session/redis.php index 2d67ea7909e..9c36ad507c3 100644 --- a/lib/classes/session/redis.php +++ b/lib/classes/session/redis.php @@ -53,6 +53,15 @@ class redis extends handler implements SessionHandlerInterface { */ const COMPRESSION_ZSTD = 'zstd'; + /** + * Minimum version of the Redis extension required. + */ + private const REDIS_EXTENSION_MIN_VERSION = '2.2.4'; + /** + * Minimum version of the Redis extension required. + */ + private const REDIS_SERVER_MIN_VERSION = '2.6.12'; + /** @var array $host save_path string */ protected array $host = []; /** @var int $port The port to connect to */ @@ -201,13 +210,14 @@ class redis extends handler implements SessionHandlerInterface { if (empty($this->host)) { throw new exception('sessionhandlerproblem', 'error', '', null, - '$CFG->session_redis_host must be specified in config.php'); + '$CFG->session_redis_host must be specified in config.php'); } - // The session handler requires a version of Redis with the SETEX command (at least 2.0). + // The session handler requires a version of PHP Redis extension with support for SET command options (at least 2.2.4). $version = phpversion('Redis'); - if (!$version || version_compare($version, '2.0') <= 0) { - throw new exception('sessionhandlerproblem', 'error', '', null, 'redis extension version must be at least 2.0'); + if (!$version || version_compare($version, self::REDIS_EXTENSION_MIN_VERSION) <= 0) { + throw new exception('sessionhandlerproblem', 'error', '', null, + 'redis extension version must be at least ' . self::REDIS_EXTENSION_MIN_VERSION); } $result = session_set_save_handler($this); @@ -296,6 +306,13 @@ class redis extends handler implements SessionHandlerInterface { throw new $exceptionclass('Unable to select the Redis database ' . $this->database . '.'); } } + + // The session handler requires a version of Redis server with support for SET command options (at least 2.6.12). + $serverversion = $this->connection->info('server')['redis_version']; + if (version_compare($serverversion, self::REDIS_SERVER_MIN_VERSION) <= 0) { + throw new exception('sessionhandlerproblem', 'error', '', null, + 'redis server version must be at least ' . self::REDIS_SERVER_MIN_VERSION); + } return true; } catch (RedisException | RedisClusterException $e) { $redishost = $this->clustermode ? implode(',', $this->host) : $server . ':' . $port; @@ -543,12 +560,10 @@ class redis extends handler implements SessionHandlerInterface { $haswarned = false; // Have we logged a lock warning? while (!$haslock) { - - $haslock = $this->connection->setnx($lockkey, $whoami); + $haslock = $this->connection->set($lockkey, $whoami, ['nx', 'ex' => $this->lockexpire]); if ($haslock) { $this->locks[$id] = $this->time() + $this->lockexpire; - $this->connection->expire($lockkey, $this->lockexpire); return true; }