Skip to content

Commit ab8919d

Browse files
committed
fixup
1 parent d8a7702 commit ab8919d

4 files changed

Lines changed: 200 additions & 1 deletion

File tree

3rdparty

Submodule 3rdparty updated 631 files

config/config.sample.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,31 @@
17911791
*/
17921792
'memcache_customprefix' => 'mycustomprefix',
17931793

1794+
/**
1795+
* Connection details for the Key-Value store used for in-memory caching.
1796+
* For example when using Redis or Valkey.
1797+
*
1798+
* For enhanced security, it is recommended to configure ACLs in
1799+
* the cache server and configure the user and password (or TLS certificate).
1800+
* Alternatively, you can also configure the cache server to just use a password.
1801+
* See https://valkey.io/topics/security/ for more information when using Valkey.
1802+
*/
1803+
'memcache.kvstore' => [
1804+
// Server setup when using a single server or Sentinel setup
1805+
'server' => [
1806+
'host' => 'localhost', // can also be a Unix domain socket: '/tmp/cache.sock'
1807+
'port' => 6379, // not used for Unix domain sockets
1808+
// The protocol used for connecting to the cache server. Supported values are 'tcp', 'tls', and 'unix'.
1809+
'protocol' => 'tcp',
1810+
],
1811+
// When using a server cluster the seed servers to use.
1812+
// The format of each seed entry is the same as the `server` entry above.
1813+
'seeds' => [
1814+
[],
1815+
[],
1816+
]
1817+
],
1818+
17941819
/**
17951820
* Connection details for Redis to use for memory caching in a single server configuration.
17961821
*
@@ -1800,6 +1825,8 @@
18001825
*
18011826
* We also support Redis SSL/TLS encryption as of version 6.
18021827
* See https://redis.io/topics/encryption for more information.
1828+
*
1829+
* @deprecated 34.0.0 use `memcache.kvstore` instead which supports also Valkey.
18031830
*/
18041831
'redis' => [
18051832
'host' => 'localhost', // can also be a Unix domain socket: '/tmp/redis.sock'
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
/*
4+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
namespace OC\Cache;
8+
9+
use OC\SystemConfig;
10+
use OCP\Diagnostics\IEventLogger;
11+
12+
/**
13+
* Factory for creating a Key-Value store cache client for e.g. Valkey or Redis.
14+
*
15+
* @since 34.0.0
16+
*/
17+
final class KeyCacheFactory {
18+
private ?\Predis\Client $client = null;
19+
20+
public function __construct(
21+
private SystemConfig $config,
22+
private IEventLogger $eventLogger,
23+
) {
24+
}
25+
26+
public function getInstance(): \Predis\Client {
27+
if ($this->client === null) {
28+
$this->create();
29+
}
30+
31+
return $this->client;
32+
}
33+
34+
protected function createClusterClient(): array {
35+
$config = $this->config->getValue('redis.cluster', []);
36+
$seeds = $config['seeds'] ?? [];
37+
if (is_string($seeds)) {
38+
$seeds = [$seeds];
39+
}
40+
return $seeds;
41+
}
42+
43+
protected function createClient(): \Predis\Client {
44+
$config = $this->config->getValue('redis', []);
45+
if (!isset($config['host'])) {
46+
throw new \RuntimeException('Redis config is missing the "host" attribute');
47+
}
48+
$connection = [];
49+
$connection['host'] = (string)$config['host'];
50+
51+
$connection['scheme'] = $config['scheme'] ?? null;
52+
if ($connection['scheme'] === null) {
53+
$connection['scheme'] = $connection['host'][0] === '/' ? 'unix' : 'tcp';
54+
}
55+
56+
if ($connection['scheme'] !== 'unix') {
57+
$connection['port'] = (int)($config['port'] ?? 6379);
58+
}
59+
60+
$options = [];
61+
62+
return new \Predis\Client($connection, $options);
63+
}
64+
65+
private function create(): void {
66+
$isCluster = in_array('redis.cluster', $this->config->getKeys(), true);
67+
return $isCluster
68+
? $this->createClusterClient()
69+
: $this->createClient();
70+
71+
$timeout = $config['timeout'] ?? 0.0;
72+
$readTimeout = $config['read_timeout'] ?? 0.0;
73+
74+
$auth = null;
75+
if (isset($config['password']) && (string)$config['password'] !== '') {
76+
if (isset($config['user']) && (string)$config['user'] !== '') {
77+
$auth = [$config['user'], $config['password']];
78+
} else {
79+
$auth = $config['password'];
80+
}
81+
}
82+
83+
// # TLS support
84+
// # https://github.com/phpredis/phpredis/issues/1600
85+
$connectionParameters = $this->getSslContext($config);
86+
$persistent = $this->config->getValue('redis.persistent', true);
87+
88+
// cluster config
89+
if ($isCluster) {
90+
if (!isset($config['seeds'])) {
91+
throw new \Exception('Redis cluster config is missing the "seeds" attribute');
92+
}
93+
94+
// Support for older phpredis versions not supporting connectionParameters
95+
if ($connectionParameters !== null) {
96+
$this->instance = new \RedisCluster(null, $config['seeds'], $timeout, $readTimeout, $persistent, $auth, $connectionParameters);
97+
} else {
98+
$this->instance = new \RedisCluster(null, $config['seeds'], $timeout, $readTimeout, $persistent, $auth);
99+
}
100+
101+
if (isset($config['failover_mode'])) {
102+
$this->instance->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, $config['failover_mode']);
103+
}
104+
} else {
105+
$this->instance = new \Redis();
106+
107+
$host = $config['host'] ?? '127.0.0.1';
108+
$port = $config['port'] ?? ($host[0] !== '/' ? 6379 : 0);
109+
110+
$this->eventLogger->start('connect:redis', 'Connect to redis and send AUTH, SELECT');
111+
// Support for older phpredis versions not supporting connectionParameters
112+
if ($connectionParameters !== null) {
113+
// Non-clustered redis requires connection parameters to be wrapped inside `stream`
114+
$connectionParameters = [
115+
'stream' => $this->getSslContext($config)
116+
];
117+
if ($persistent) {
118+
/**
119+
* even though the stubs and documentation don't want you to know this,
120+
* pconnect does have the same $connectionParameters argument connect has
121+
*
122+
* https://github.com/phpredis/phpredis/blob/0264de1824b03fb2d0ad515b4d4ec019cd2dae70/redis.c#L710-L730
123+
*
124+
* @psalm-suppress TooManyArguments
125+
*/
126+
$this->instance->pconnect($host, $port, $timeout, null, 0, $readTimeout, $connectionParameters);
127+
} else {
128+
$this->instance->connect($host, $port, $timeout, null, 0, $readTimeout, $connectionParameters);
129+
}
130+
} else {
131+
if ($persistent) {
132+
$this->instance->pconnect($host, $port, $timeout, null, 0, $readTimeout);
133+
} else {
134+
$this->instance->connect($host, $port, $timeout, null, 0, $readTimeout);
135+
}
136+
}
137+
138+
139+
// Auth if configured
140+
if ($auth !== null) {
141+
$this->instance->auth($auth);
142+
}
143+
144+
if (isset($config['dbindex'])) {
145+
$this->instance->select($config['dbindex']);
146+
}
147+
$this->eventLogger->end('connect:redis');
148+
}
149+
}
150+
151+
/**
152+
* Get the ssl context config
153+
*
154+
* @param array $config the current config
155+
* @throws \UnexpectedValueException
156+
*/
157+
private function getSslContext(array $config): ?array {
158+
if (isset($config['ssl_context'])) {
159+
if (!$this->isConnectionParametersSupported()) {
160+
throw new \UnexpectedValueException(\sprintf(
161+
'php-redis extension must be version %s or higher to support ssl context',
162+
self::REDIS_EXTRA_PARAMETERS_MINIMAL_VERSION
163+
));
164+
}
165+
return $config['ssl_context'];
166+
}
167+
return null;
168+
}
169+
}

lib/private/RedisFactory.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
use OCP\Diagnostics\IEventLogger;
1212

13+
/**
14+
* @deprecated 34.0.0 - uses KeyValueCacheFactory instead
15+
*/
1316
class RedisFactory {
1417
public const REDIS_MINIMAL_VERSION = '4.0.0';
1518
public const REDIS_EXTRA_PARAMETERS_MINIMAL_VERSION = '5.3.0';

0 commit comments

Comments
 (0)