diff --git a/Cargo.lock b/Cargo.lock index 8c077769..68bd673f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1511,9 +1511,9 @@ dependencies = [ [[package]] name = "nextcloud-config-parser" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083fbacd3a186bf4d1dde2cb80fc4b5a6656348ef7121eea202369e5f8214578" +checksum = "e2091417dbccebb684aa4682a1448517d0fc73d4b3fbe6b072f61d306e2ac96c" dependencies = [ "form_urlencoded", "itertools", diff --git a/Cargo.toml b/Cargo.toml index 81c57d9d..b04273af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ rand = { version = "0.8.5", features = ["small_rng"] } ahash = "0.8.11" flexi_logger = { version = "0.29.8", features = ["colors"] } tokio-stream = { version = "0.1.17", features = ["net"] } -nextcloud-config-parser = "0.13.1" +nextcloud-config-parser = "0.14.0" url = "2.5.4" clap = { version = "4.5.26", features = ["derive"] } sd-notify = { version = "0.4.3", optional = true } diff --git a/README.md b/README.md index 160ec494..05958f56 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,17 @@ Note that Nextcloud loads all files matching `*.config.php` in the config direct file. You can enable this same behavior by passing the `--glob-config` option. -##### +
+Using a separate redis instance for the push server + + +You can optionally use a different redis instance for communications between the Nextcloud server and the push daemon. + +This allows spreading moving the load away from the normal redis usage or use a redis setup more optimized for the specific usage (`PUBSUB` traffic instead of cache storage). + +You can configure this by setting the `notify_push_redis` config option in the `config.php` of the Nextcloud server, this accepts the same options as the normal redis configurations. + +
Connecting to redis over TLS diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f2d891b0..a389f683 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -8,12 +8,12 @@ namespace OCA\NotifyPush\AppInfo; -use OC\RedisFactory; use OCA\NotifyPush\Capabilities; use OCA\NotifyPush\CSPListener; use OCA\NotifyPush\Listener; use OCA\NotifyPush\Queue\IQueue; use OCA\NotifyPush\Queue\NullQueue; +use OCA\NotifyPush\Queue\PushRedisFactory; use OCA\NotifyPush\Queue\RedisQueue; use OCP\Activity\IManager; use OCP\AppFramework\App; @@ -41,10 +41,11 @@ public function register(IRegistrationContext $context): void { $context->registerCapability(Capabilities::class); $context->registerService(IQueue::class, function (ContainerInterface $c) { - /** @var RedisFactory $redisFactory */ - $redisFactory = $c->get(RedisFactory::class); - if ($redisFactory->isAvailable()) { - return new RedisQueue($redisFactory->getInstance()); + /** @var PushRedisFactory $factory */ + $factory = $c->get(PushRedisFactory::class); + $redis = $factory->getRedis(); + if ($redis) { + return new RedisQueue($redis); } else { return new NullQueue(); } diff --git a/lib/Queue/PushRedisFactory.php b/lib/Queue/PushRedisFactory.php new file mode 100644 index 00000000..8d91ff85 --- /dev/null +++ b/lib/Queue/PushRedisFactory.php @@ -0,0 +1,102 @@ + + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\NotifyPush\Queue; + +use OC\RedisFactory; +use OCP\IConfig; + +class PushRedisFactory { + private $instance = null; + private IConfig $config; + private RedisFactory $redisFactory; + + public function __construct( + IConfig $config, + RedisFactory $redisFactory, + ) { + $this->config = $config; + $this->redisFactory = $redisFactory; + } + + /** + * @return \Redis|\RedisCluster|null + * @throws \Exception + */ + public function getRedis() { + if ($this->instance) { + return $this->instance; + } + if ($this->config->getSystemValue('notify_push_redis', []) !== []) { + return $this->getSeparateRedis(); + } elseif ($this->redisFactory->isAvailable()) { + return $this->redisFactory->getInstance(); + } else { + return null; + } + } + + private function getSeparateRedis(): \Redis { + $config = $this->config->getSystemValue('notify_push_redis', []); + $timeout = $config['timeout'] ?? 0.0; + $readTimeout = $config['read_timeout'] ?? 0.0; + + $auth = null; + if (isset($config['password']) && (string)$config['password'] !== '') { + if (isset($config['user']) && (string)$config['user'] !== '') { + $auth = [$config['user'], $config['password']]; + } else { + $auth = $config['password']; + } + } + + $persistent = $this->config->getSystemValue('notify_push_redis.persistent', true); + + $redis = new \Redis(); + + $host = $config['host'] ?? '127.0.0.1'; + $port = $config['port'] ?? ($host[0] !== '/' ? 6379 : null); + + // Support for older phpredis versions not supporting connectionParameters + if (isset($config['ssl_context'])) { + // Non-clustered redis requires connection parameters to be wrapped inside `stream` + $connectionParameters = [ + 'stream' => $config['ssl_context'] + ]; + if ($persistent) { + /** + * even though the stubs and documentation don't want you to know this, + * pconnect does have the same $connectionParameters argument connect has + * + * https://github.com/phpredis/phpredis/blob/0264de1824b03fb2d0ad515b4d4ec019cd2dae70/redis.c#L710-L730 + * + * @psalm-suppress TooManyArguments + */ + $redis->pconnect($host, $port, $timeout, null, 0, $readTimeout, $connectionParameters); + } else { + $redis->connect($host, $port, $timeout, null, 0, $readTimeout, $connectionParameters); + } + } else { + if ($persistent) { + $redis->pconnect($host, $port, $timeout, null, 0, $readTimeout); + } else { + $redis->connect($host, $port, $timeout, null, 0, $readTimeout); + } + } + + if ($auth !== null) { + $redis->auth($auth); + } + + if (isset($config['dbindex'])) { + $redis->select($config['dbindex']); + } + + return $redis; + } +} diff --git a/src/config/nc.rs b/src/config/nc.rs index f5db191b..ca2bf013 100644 --- a/src/config/nc.rs +++ b/src/config/nc.rs @@ -18,7 +18,7 @@ pub(super) fn parse_config_file( database: Some(config.database.url().parse()?), database_prefix: Some(config.database_prefix), nextcloud_url: Some(config.nextcloud_url), - redis: Some(config.redis), + redis: Some(config.notify_push_redis.unwrap_or(config.redis)), ..PartialConfig::default() }) }