-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Cache] Add DSN, createClient & better error reporting to MemcachedAdapter #21108
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
[Cache] Add DSN, createClient & better error reporting to MemcachedAdapter #21108
Conversation
70e509a
to
6d35624
Compare
95ad534
to
6e68d2c
Compare
} | ||
if ($client->getOption(\Memcached::OPT_NO_BLOCK)) { | ||
throw new CacheException('MemcachedAdapter: OPT_NO_BLOCK must be disabled.'); | ||
} |
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.
I understand the serializer requirement, but why are we forcing binary protocol and no_block=false?
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.
Binary because we must support all chars in keys especially spaces, per psr-6.
No block because we need guarantees when reading/writing to the cache. Dealing with async would make the code complex for no benefit.
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.
Regarding binary_protocol
requirement: I'm on board with this.
But, regarding no_block
requirement: I disagree. The intended use of this option doesn't require any additional code, and, in fact, writing such code would invalidate the entire intended usage and benefits of no_block
. The expectation when it is enabled is that the user doesn't need guarantees when writing. It is a commonly used option and generally doesn't change expectations; due to the design of memcached, one can never assume a value exists as it may have been purged at any time by the LRU algorithm, and will definitely not exist after a restart of the daemon.
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.
fixed for no_block
unset($options['persistent_id'], $options['username'], $options['password']); | ||
|
||
// set client's options | ||
foreach ($options as $name => $value) { |
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.
This needs to be foreach (array_reverse($options) as $name => $value)
as seen in my PR, otherwise defaul values that affect multiple options (such as libketama_compatible
) will overwrite use-provided values.
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.
I think reverse or not, it's the responsibility of the user to order correctly. I'd better behave the same as the native setOptions so that expectations don't have to be reversed when using our lib...
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.
It's due to our code, though. The default option of libketama_compatible
in combination with $options += static::$defaultClientOptions;
means a user cannot change, for example, the hash algorithm, ever...as our default of libketama_compatible
will be applied after the user-provided option (for example ['hash' => 'something-else']
), resetting the hash to md5
. The options need to be applied in reverse order to allow the user to overwrite our defaults.
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.
To clarify: the user should, rightfully, assume that any options they provide will overwrite our defaults. The issue is, some options actually affect multiple options. Without reversing the array, the user options will not always result in a proper assignment; while reversing the options ensures user options always overwrite defaults.
It's not the order of options that is important, it's the order of default options to user options that is important. Default options must be applied first, so either the assignment needs to change:
# from this:
$options += static::$defaultClientOptions;
# to this (or something similar):
foreach (static::$defaultClientOptions as $name => $option) {
if (!array_key_exists($name, $options) {
array_unshift($options, [$name => $option]);
}
}
OR the order of options needs to be reversed:
# from this:
foreach ($options as $name => $value) {}
# to this (or something similar):
foreach (array_reverse($options) as $name => $value) {}
Otherwise options will behave unexpectedly for the user.
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.
understood, fixed also
} | ||
|
||
// set client's servers, taking care of persistent connections | ||
if (!$client->isPristine()) { |
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 shouldn't add servers after a connection is open (even when the user has added an additional server to their configuration)?
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.
Not sure to get what you mean. Do you see anything wrong with this logic?
Here it is: if the connection is not pristine (meaning it's a 2nd use persistent connection), then we check if there is any change in the list of server. If there is, we reset the list and set the new one.
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.
Ok. Had trouble following the code, but makes sense now.
A documentation PR for #20863 is already open (see issue symfony/symfony-docs#7256 and PR symfony/symfony-docs#7265) and it doesn't require much at all to update it for this PR. I will ensure it is accurate after the implementation details I've asked about are handled/clarified. Thanks! |
d2565af
to
9e899a4
Compare
I'd add a test to cover the serialize requirement: /**
* @expectedException \Symfony\Component\Cache\Exception\CacheException
* @expectedExceptionMessage MemcachedAdapter: PHP or IGBINARY must be used.
*/
public function testOptionSerializer()
{
new MemcachedAdapter(MemcachedAdapter::createConnection(array(), array('serializer' => 'json')));
} |
9e899a4
to
4e31fea
Compare
@robfrawley test case added, thanks. Note also that considering your comment about no block, and the use case here (a cache layer), I enabled no-block by default. Tests are green which means it's good for PSR-6! |
4e31fea
to
95c9777
Compare
Platform.sh is not properly building the documentation for a bunch of PRs ATM, but I think the docs are accurate with regard to this PR (updated to show correct defaults per your latest updates): give it a look here. |
$dsn = $name; | ||
|
||
if (!$container->hasDefinition($name = md5($dsn))) { | ||
$definition = new Definition(\Redis::class); | ||
$definition = new Definition('_'); |
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 about using AdapterInterface::class
to not make it harder for bundles that do static analyses of the container?
return MemcachedAdapter::createConnection($dsn, $options); | ||
} | ||
|
||
throw new InvalidArgumentException(sprintf('Unsupported DSN: %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.
Should the AbstractAdapter
really be aware of all adapters that require a connection?
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.
This is a stateless static method, no need for yet another class. This has to be somewhere :)
Other comments addressed.
} | ||
$opt = $client->getOption(\Memcached::OPT_SERIALIZER); | ||
if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { | ||
throw new CacheException('MemcachedAdapter: serializer must be PHP or IGBINARY.'); |
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.
Should we use OPT_SERIALIZER
instead of serializer
here to be consistent with the exception message below?
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 way this code is setup and documented, you pass serializer
, not opt_serializer
, so I would argue the inverse: the other exception message should be amend to:
binary_protocol must be used
if (is_string($servers)) { | ||
$servers = array($servers); | ||
} elseif (!is_array($servers)) { | ||
throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, %s given.', get_type($servers))); |
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.
gettype()
36fd01e
to
3eae606
Compare
3eae606
to
87030b4
Compare
👍 |
@nicolas-grekas Do we want the RedisAdapter to accept an array of DSNs like the MemcacheAdapter; I'm looking into bringing the RedisAdapter documentation in-line with the new MemcacheAdapter documentation, re: symfony/symfony-docs/#7265@269875795. It appears it does not at this time. |
@robfrawley it would be great yes |
Thank you @robfrawley. |
… to MemcachedAdapter (nicolas-grekas, robfrawley) This PR was merged into the 3.3-dev branch. Discussion ---------- [Cache] Add DSN, createClient & better error reporting to MemcachedAdapter | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | symfony/symfony-docs#7265 Replaces #20863 ping @robfrawley: would you mind opening a doc PR for this? Commits ------- 87030b4 [cache] Add tests for MemcachedAdapter::createClient() e109438 [Cache] Add DSN, createClient & better error reporting to MemcachedAdapter
Replaces #20863
ping @robfrawley: would you mind opening a doc PR for this?