diff --git a/README.md b/README.md index dfbad5b..9c9a411 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ return [ 'port' => 6379, 'database' => 0, ], + 'cache' => [ + 'class' => bootell\Yii2\redis\Cache::class, + ], + 'session' => [ + 'class' => bootell\Yii2\redis\Session::class, + ], ] ]; ``` diff --git a/composer.json b/composer.json index 96ac3e1..1920353 100644 --- a/composer.json +++ b/composer.json @@ -4,11 +4,11 @@ "keywords": ["yii2", "redis", "phpredis", "active-record", "cache", "session"], "license": "BSD-3-Clause", "require": { - "yiisoft/yii2-redis": "~2.0.0", - "ext-redis": ">5.0" + "yiisoft/yii2-redis": "~2.0.19", + "ext-redis": ">6.0" }, "require-dev": { - "phpunit/phpunit": "^7.5", + "phpunit/phpunit": "^9.6", "yiisoft/yii2-dev": "^2.0" }, "autoload": { @@ -27,5 +27,10 @@ "type": "composer", "url": "https://asset-packagist.org" } - ] + ], + "config": { + "allow-plugins": { + "yiisoft/yii2-composer": true + } + } } diff --git a/src/Connection.php b/src/Connection.php index 98286ec..db0a823 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -248,22 +248,33 @@ class Connection extends \yii\redis\Connection */ protected $_redis = false; + /** + * @var integer + */ + public $retries = 3; /** - * @inheritDoc + * @var int */ - public function __sleep() - { - $this->close(); - return array_keys(get_object_vars($this)); - } + public $backoffAlgo = Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER; + + /** + * @var int + */ + public $backoffBase = 500; + + /** + * @var int + */ + public $backoffCap = 750; /** * @inheritDoc */ - public function getIsActive() + public function __sleep() { - return $this->_redis !== false; + $this->close(); + return array_keys(get_object_vars($this)); } /** @@ -274,22 +285,25 @@ public function open() if ($this->_redis !== false) { return; } - $connection = $this->hostname . ':' . $this->port . ', database=' . $this->database; + $connection = $this->getConnectionString() . ', database=' . $this->database; \Yii::debug('Opening redis DB connection: ' . $connection, __METHOD__); try { $this->_redis = new Redis(); + if ($this->use_pconnect) { - $this->_redis->pconnect($this->hostname, $this->port, $this->connectionTimeout, null, 0, $this->dataTimeout); + $this->_redis->pconnect($this->getConnectionString(), $this->port, $this->connectionTimeout ?? 0, null, 0, $this->dataTimeout ?? 0); } else { - $this->_redis->connect($this->hostname, $this->port, $this->connectionTimeout, null, 0, $this->dataTimeout); + $this->_redis->connect($this->getConnectionString(), $this->port, $this->connectionTimeout ?? 0, null, 0, $this->dataTimeout ?? 0); } $this->_redis->setOption(Redis::OPT_REPLY_LITERAL, true); + $this->_redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, true); if ($this->password !== null) { - $this->executeCommand('AUTH', [$this->password]); + $this->_redis->auth(array_filter([$this->username, $this->password])); } if ($this->database !== null) { - $this->executeCommand('SELECT', [$this->database]); + $this->_redis->select($this->database); } + $this->initRetries(); $this->initConnection(); } catch (\RedisException $e) { $message = "Failed to open redis DB connection ($connection): {$e->getMessage()}"; @@ -299,6 +313,69 @@ public function open() } } + /** + * Return connection string as URI + * + * @param boolean $display + * @return string + */ + public function getConnectionString(bool $display = false) + { + if ($this->unixSocket) { + return 'unix://' . $this->unixSocket; + } + + $hostname = ($this->useSSL ? 'tls://' : 'tcp://') . $this->hostname; + + if ($display) { + $hostname = $hostname . ':' . $this->port; + } + + return $hostname; + } + + /** + * Set max retries + * + * @param integer $trys + * @return void + */ + public function setRetries(int $trys) + { + $this->retries = $trys; + } + + /** + * Set retry backoff strategy + * + * @param integer $algo + * @param integer|null $base + * @param integer $cap + * @return void + */ + public function setBackoff(int $algo, int $base = null, int $cap) + { + $this->backoffAlgo = $algo; + $this->backoffBase = $base; + $this->backoffCap = $cap; + } + + protected function initRetries() + { + $this->_redis->setOption(Redis::OPT_MAX_RETRIES, $this->retries); + $this->_redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, $this->backoffAlgo); + $this->_redis->setOption(Redis::OPT_BACKOFF_BASE, $this->backoffBase); + $this->_redis->setOption(Redis::OPT_BACKOFF_CAP, $this->backoffCap); + } + + /** + * @inheritDoc + */ + public function getIsActive() + { + return $this->_redis !== false; + } + /** * @inheritDoc */ @@ -368,6 +445,12 @@ protected function parseResponse($result) return true; } elseif ($result === false) { return null; + } elseif (is_array($result)) { + $data = []; + foreach ($result as $item) { + $data[] = self::parseResponse($item); + } + return $data; } return $result; }