diff --git a/config/config.sample.php b/config/config.sample.php index 0fbd3ffce0..84b98550fb 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1042,19 +1042,43 @@ $CONFIG = array( 'memcache.distributed' => '\OC\Memcache\Memcached', /** - * Connection details for redis to use for memory caching. + * Connection details for redis to use for memory caching in a single server configuration. * * For enhanced security it is recommended to configure Redis * to require a password. See http://redis.io/topics/security * for more information. */ -'redis' => array( +'redis' => [ 'host' => 'localhost', // can also be a unix domain socket: '/tmp/redis.sock' 'port' => 6379, 'timeout' => 0.0, 'password' => '', // Optional, if not defined no password will be used. 'dbindex' => 0, // Optional, if undefined SELECT will not run and will use Redis Server's default DB Index. -), +], + +/** + * Connection details for a Redis Cluster + * + * Only for use with Redis Clustering, for Sentinel-based setups use the single + * server configuration above, and perform HA on the hostname. + * + * Redis Cluster support requires the php module phpredis in version 3.0.0 or higher. + * + * Available failover modes: + * - \RedisCluster::FAILOVER_NONE - only send commands to master nodes (default) + * - \RedisCluster::FAILOVER_ERROR - failover to slaves for read commands if master is unavailable + * - \RedisCluster::FAILOVER_DISTRIBUTE - randomly distribute read commands across master and slaves + */ +'redis.cluster' => [ + 'seeds' => [ // provide some/all of the cluster servers to bootstrap discovery, port required + 'localhost:7000', + 'localhost:7001' + ], + 'timeout' => 0.0, + 'read_timeout' => 0.0, + 'failover_mode' => \RedisCluster::FAILOVER_DISTRIBUTE +], + /** * Server details for one or more memcached servers to use for memory caching. diff --git a/lib/private/Memcache/Redis.php b/lib/private/Memcache/Redis.php index c3184e4585..d423b134f9 100644 --- a/lib/private/Memcache/Redis.php +++ b/lib/private/Memcache/Redis.php @@ -49,8 +49,8 @@ class Redis extends Cache implements IMemcacheTTL { } public function get($key) { - $result = self::$cache->get($this->getNamespace() . $key); - if ($result === false && !self::$cache->exists($this->getNamespace() . $key)) { + $result = self::$cache->get($this->getNameSpace() . $key); + if ($result === false && !self::$cache->exists($this->getNameSpace() . $key)) { return null; } else { return json_decode($result, true); @@ -59,18 +59,18 @@ class Redis extends Cache implements IMemcacheTTL { public function set($key, $value, $ttl = 0) { if ($ttl > 0) { - return self::$cache->setex($this->getNamespace() . $key, $ttl, json_encode($value)); + return self::$cache->setex($this->getNameSpace() . $key, $ttl, json_encode($value)); } else { - return self::$cache->set($this->getNamespace() . $key, json_encode($value)); + return self::$cache->set($this->getNameSpace() . $key, json_encode($value)); } } public function hasKey($key) { - return self::$cache->exists($this->getNamespace() . $key); + return self::$cache->exists($this->getNameSpace() . $key); } public function remove($key) { - if (self::$cache->delete($this->getNamespace() . $key)) { + if (self::$cache->delete($this->getNameSpace() . $key)) { return true; } else { return false; @@ -78,7 +78,7 @@ class Redis extends Cache implements IMemcacheTTL { } public function clear($prefix = '') { - $prefix = $this->getNamespace() . $prefix . '*'; + $prefix = $this->getNameSpace() . $prefix . '*'; $it = null; self::$cache->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY); while ($keys = self::$cache->scan($it, $prefix)) { @@ -111,7 +111,7 @@ class Redis extends Cache implements IMemcacheTTL { * @return int | bool */ public function inc($key, $step = 1) { - return self::$cache->incrBy($this->getNamespace() . $key, $step); + return self::$cache->incrBy($this->getNameSpace() . $key, $step); } /** @@ -125,7 +125,7 @@ class Redis extends Cache implements IMemcacheTTL { if (!$this->hasKey($key)) { return false; } - return self::$cache->decrBy($this->getNamespace() . $key, $step); + return self::$cache->decrBy($this->getNameSpace() . $key, $step); } /** @@ -140,10 +140,10 @@ class Redis extends Cache implements IMemcacheTTL { if (!is_int($new)) { $new = json_encode($new); } - self::$cache->watch($this->getNamespace() . $key); + self::$cache->watch($this->getNameSpace() . $key); if ($this->get($key) === $old) { $result = self::$cache->multi() - ->set($this->getNamespace() . $key, $new) + ->set($this->getNameSpace() . $key, $new) ->exec(); return ($result === false) ? false : true; } @@ -159,10 +159,10 @@ class Redis extends Cache implements IMemcacheTTL { * @return bool */ public function cad($key, $old) { - self::$cache->watch($this->getNamespace() . $key); + self::$cache->watch($this->getNameSpace() . $key); if ($this->get($key) === $old) { $result = self::$cache->multi() - ->del($this->getNamespace() . $key) + ->del($this->getNameSpace() . $key) ->exec(); return ($result === false) ? false : true; } @@ -171,7 +171,7 @@ class Redis extends Cache implements IMemcacheTTL { } public function setTTL($key, $ttl) { - self::$cache->expire($this->getNamespace() . $key, $ttl); + self::$cache->expire($this->getNameSpace() . $key, $ttl); } static public function isAvailable() { diff --git a/lib/private/RedisFactory.php b/lib/private/RedisFactory.php index 3ba637c414..701e15325c 100644 --- a/lib/private/RedisFactory.php +++ b/lib/private/RedisFactory.php @@ -39,32 +39,54 @@ class RedisFactory { } private function create() { - $this->instance = new \Redis(); - // TODO allow configuring a RedisArray, see https://github.com/nicolasff/phpredis/blob/master/arrays.markdown#redis-arrays - $config = $this->config->getValue('redis', array()); - if (isset($config['host'])) { - $host = $config['host']; - } else { - $host = '127.0.0.1'; - } - if (isset($config['port'])) { - $port = $config['port']; - } else { - $port = 6379; - } - if (isset($config['timeout'])) { - $timeout = $config['timeout']; - } else { - $timeout = 0.0; // unlimited - } + if ($config = $this->config->getValue('redis.cluster', [])) { + if (!class_exists('RedisCluster')) { + throw new \Exception('Redis Cluster support is not available'); + } + // cluster config + if (isset($config['timeout'])) { + $timeout = $config['timeout']; + } else { + $timeout = null; + } + if (isset($config['read_timeout'])) { + $readTimeout = $config['read_timeout']; + } else { + $readTimeout = null; + } + $this->instance = new \RedisCluster(null, $config['seeds'], $timeout, $readTimeout); - $this->instance->connect($host, $port, $timeout); - if (isset($config['password']) && $config['password'] !== '') { - $this->instance->auth($config['password']); - } + if (isset($config['failover_mode'])) { + $this->instance->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, $config['failover_mode']); + } + } else { - if (isset($config['dbindex'])) { - $this->instance->select($config['dbindex']); + $this->instance = new \Redis(); + $config = $this->config->getValue('redis', []); + if (isset($config['host'])) { + $host = $config['host']; + } else { + $host = '127.0.0.1'; + } + if (isset($config['port'])) { + $port = $config['port']; + } else { + $port = 6379; + } + if (isset($config['timeout'])) { + $timeout = $config['timeout']; + } else { + $timeout = 0.0; // unlimited + } + + $this->instance->connect($host, $port, $timeout); + if (isset($config['password']) && $config['password'] !== '') { + $this->instance->auth($config['password']); + } + + if (isset($config['dbindex'])) { + $this->instance->select($config['dbindex']); + } } }