-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Cache] Add ability to configure predis client. #28175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
effe292
to
05c319f
Compare
Hello, thanks for the PR. Here is the options reference: To understand better, which options are useful in your case? |
Hi @nicolas-grekas, No problem, I'd be happy to provide some more info! The predis docs are a little off in the wiki. The readme.md specifies there are 3 options, the one mentioned in the wiki, plus either See: https://github.com/nrk/predis#client-configuration The |
Thanks for the links. Right now, Predis is skipped when ext-redis is installed. |
Do you reckon it would be better to throw an exception in that case, or to enforce the use of predis instead? This could be could be done expanding uppon the if statement that checks for the existence of ext-redis. It could check for ext-redis as well as predis options and default to predis if they are present. It's possible someone could have ext-redis available, but would prefer to use predis for the clustering capability. But still use ext-redis for other redis instances that are not clustered. Allowing the |
Setting the |
0f08f1b
to
2fe2f53
Compare
I haven't got much experience with the redis extension, does the I agree, I think having the code work out what you're trying to use and fail on an invalid configuration setup is ideal. I've updated the code to reflect the more automated configuration approach as well as added tests to check this config. If a predis option is specified and predis is not available it will throw a |
2fe2f53
to
5ee41a7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some more comment to help move forward.
LGTM afterwards, thanks.
@@ -118,8 +118,17 @@ public static function createConnection($dsn, array $options = array()) | |||
$params += $options + self::$defaultConnectionOptions; | |||
if (null === $params['class'] && !\extension_loaded('redis') && !class_exists(\Predis\Client::class)) { | |||
throw new CacheException(sprintf('Cannot find the "redis" extension, and "predis/predis" is not installed: %s', $dsn)); | |||
} elseif (isset($params['predis_options']) && !class_exists(\Predis\Client::class)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we prefer replacing such "elseif" by an "if" (can be done when the previous "if" break the code flow, with throw or return typically. (same below)
@@ -118,8 +118,17 @@ public static function createConnection($dsn, array $options = array()) | |||
$params += $options + self::$defaultConnectionOptions; | |||
if (null === $params['class'] && !\extension_loaded('redis') && !class_exists(\Predis\Client::class)) { | |||
throw new CacheException(sprintf('Cannot find the "redis" extension, and "predis/predis" is not installed: %s', $dsn)); | |||
} elseif (isset($params['predis_options']) && !class_exists(\Predis\Client::class)) { | |||
throw new CacheException(sprintf('DSN contains "predis_options" and "predis/predis" is not installed: %s', $dsn)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The option can come from the $options argument, so the message should not tell about the DSN I'd say.
What about `Invalid connection option: "predis_options" provided but "predis/predis" is not installed.', $dsn));
throw new CacheException(sprintf('DSN contains "predis_options" and "predis/predis" is not installed: %s', $dsn)); | ||
} elseif (isset($params['predis_options']) || !\extension_loaded('redis')) { | ||
if (null !== $params['class'] && !is_a($params['class'], \Predis\Client::class, true)) { | ||
throw new InvalidArgumentException(sprintf('Cannot find the "redis" extension and expected an instance of %s, instead got: %s', \Predis\Client::class, $params['class'])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the message is confusing: when the extension is installed, we should tell otherwise.
throw new InvalidArgumentException(sprintf('Cannot find the "redis" extension and expected an instance of %s, instead got: %s', \Predis\Client::class, $params['class'])); | ||
} | ||
|
||
$class = null === $params['class'] ? \Predis\Client::class : $params['class']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$class = $params['class'] ?? \Predis\Client::class;
(same below)
@@ -167,7 +176,10 @@ public static function createConnection($dsn, array $options = array()) | |||
$params['scheme'] = $scheme; | |||
$params['database'] = $params['dbindex'] ?: null; | |||
$params['password'] = $auth; | |||
$redis = new $class((new Factory())->create($params)); | |||
|
|||
$predisOptions = false !== isset($params['predis_options']) && true === \is_array($params['predis_options']) ? $params['predis_options'] : array(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need for the boolean comparisons:
$predisOptions = isset($params['predis_options']) && \is_array($params['predis_options']) ? $params['predis_options'] : array();
but shouldn't we throw when predis_options is not an array?
5794698
to
1ac9b3e
Compare
Great, I've made those suggested changes now and cleaned up a few of the exception messages a bit! |
@@ -9,6 +9,7 @@ CHANGELOG | |||
* added sub-second expiry accuracy for backends that support it | |||
* added support for phpredis 4 `compression` and `tcp_keepalive` options | |||
* added automatic table creation when using Doctrine DBAL with PDO-based backends | |||
* added support for a `predis_options` query parameter array in the redis dsn. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DSN
|
||
$predisOptions = $params['predis_options'] ?? array(); | ||
|
||
if (!\is_array($predisOptions)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not doing this kind of checks anywhere in the code base, not sure if we want it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you recommend instead? If we don't check and throw an exception predis will, I would imagine this would suffice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Letting predis handle the situation seems better.
1ac9b3e
to
1671186
Compare
I've made the suggested changes in the code and the Was the line doing |
dcff981
to
a260177
Compare
When the predis driver is created, there is no current way to configure the predis client options (such as setting the cluster mode to redis). This merge allows you to add a predis_options parameter to the dsn to allow configuration of options.
a260177
to
2458735
Compare
I'm not sure what's caused the tests to fail for travic-ci there. Any ideas? Seems to be something to do with the Process component? |
@fabpot Were the changes made suitable for what you mentioned? |
See proposal in #28300 (comment) |
Having done some more debugging on this, I do not believe my merge is yet complete so please do not accept it yet. There is an issue where passing in a single host to predis causes it to ignore the clustering setup completely, using the default stream connection instead of creating a cluster. (For example, aws elasticache redis clusters that have a single entry endpoint.) I'm not yet quite sure on how to approach this, possibly if clustering is expected, to pass in an array of hosts (an array of 1 host works), instead of a single string to the factory. It also appears the predis factory does not support creating a redis cluster connection. The current tests pass only because when calling the code to get the I think this changes the scope of changes required to the |
Closing in favor of #28713 |
… via DSN (nicolas-grekas) This PR was merged into the 4.2-dev branch. Discussion ---------- [Cache] added support for connecting to Redis clusters via DSN | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Replaces #28300 and #28175 This PR allows configuring a cluster of Redis servers using all available options of either the phpredis extension or the Predis package: - the `redis_cluster=0/1` boolean option configures whether the client should use the Redis cluster protocol; - several hosts can be provided using a syntax very similar to #28598, enabling consistent hashing distribution of keys; - `failover=error/distribute/slaves` can be set to direct reads at slave servers; - extra options are passed as is to the driver (e.g. `profile=2.8`) - Predis per-server settings are also possible, using e.g. `host[localhost][alias]=foo` in the query string, or `host[localhost]=alias%3Dfoo` (ie PHP query arrays or urlencoded key/value pairs) Commits ------- a42e877 [Cache] added support for connecting to Redis clusters via DSN
When using
predis/predis
, there is currently no way to set any options as the 2nd parameter in thePredis\Client
constructor. This merge allows you to define these options with apredis_options
query parameter in the dsn.Example: