-
Notifications
You must be signed in to change notification settings - Fork 16
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
Added ShardablePhpRedis backend #5
base: 8.x-1.x
Are you sure you want to change the base?
Conversation
@@ -4,3 +4,6 @@ services: | |||
arguments: ['@redis.factory', '@cache_tags.invalidator.checksum'] | |||
redis.factory: | |||
class: Drupal\redis\ClientFactory | |||
redis.phpredis.invalidator: | |||
class: Drupal\redis\Cache\RedisCacheTagsChecksum | |||
arguments: ['@redis.factory'] |
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 am not really sure why I wrote this, I had trouble registering the checksum invalidator into services, @Berdir please tell me if this right or if I need to remove it.
// invalid, but that's the definitive price of trying to being | ||
// consistent in all cases. | ||
$current = $this->getNextIncrement(); | ||
$this->client->set($this->getKey(['tag', $tag]), $current); |
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.
Note that I want to restore the tag cache around this, but I'd prefer to add it with a decorator around this class such as UnreliableRedisCacheTagsChecksum or StaticCacheTagsChecksum register both existing (cached and non cached) into services and let the user choose among those two, I don't care about the one to use per default.
@@ -24,7 +24,7 @@ class RedisCacheTagsChecksum implements CacheTagsChecksumInterface, CacheTagsInv | |||
* | |||
* @var array | |||
*/ | |||
protected $tagCache = array(); | |||
protected $tagCache = []; |
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.
Sorry for the noise here, just found this to be more readable, even though I don't really care about writing it back as "array()".
/** | ||
* Tests Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). | ||
*/ | ||
function testInvalidateTags() { |
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.
Since that my shared backend cannot guarantee by its algorithm that we can keep invalidated entries in all cases, I had to override the follow test cases. In all cases I at worst just removed a single line. The invalid entry keeping is an optional feature and not all backend will be able to do it as soon as you expect the backend itself to deal with expiration. I had to ugly patch my cache backend to force it to keep entries in some cases, and I'm not happy with this, because core test class forces this feature to be mandatory for all backends using it...
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.
Makes sense, I'm OK with opening an issue for this, at the very least, the base class could have a $this->keepsInvalid or so property that a subclass can easily implement?
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.
Yes, sure, this would be great!
Tests are failing on Travis but they pass on my box, I need feedback from your side. |
- If the same schema is used that the 7.x version, then there is no use | ||
in sharding it, and should be stored along the router table replacement. | ||
|
||
* Drupal\Core\Session\SessionHandler |
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 just got a lot easier with https://www.drupal.org/node/2228393, yes.
Pipelining does really bring a performance boost, but if it is the only one, it indeed might worth the shot to maintain only one implementation.
I do agree about the flush timestamp static cache being a worthy optimization for most common uses cases, but it may cause trouble in concurrency scenarios, mostly during writes, that's why I didn't implement it this way. I still didn't reach a consensus with myself about that! In real life I got from time to time a few conflicts on feature revert and cache rebuilds in Drupal 7 sites, so I need to think deeper about it before taking any decision about that.
I have no strong opinion about the createEntryHash() and expandEntry() functions, I did wrote them originally in the Drupal 7 codebase to share the business code in get and set due to the fact I do maintain multiple implementations, but now the implementation is coupled with tags and harded to factorize, so I don't really see the point of keeping it. Nevertheless if it's something you care about, why not, I'll leave the task to factorize to you then :)
The fact that I delete directly entries in get() is because there is absolutely no other way to proceed, except by writing some kind of queue somewhere, thing that I definitely try to avoid to reduce the complexity. I don't want to rely upon garbage collection because there is no visibility over the frequency of when it happens and I don't want to end up with stalled or huge queues all over the place. The delete on read model is valid and working quite well, it allows to manage some additional space on the Redis side whenever we can (it's almost opportunistic) even if we know that in most cases Drupal will rewrite the key right after loading when invalidated, I kind of like it this way. We might change this behavior to optional in order to keep the invalid entries longer, but that would be the only benefit from this.
I should probably merge that in my implementation, it does warn by raising an exception at insert time when the caller does stupid thing, that's probably for the best, and it goes along the default backend behavior.
Nope, in a sharded environement every key may arbitrarily set on an arbitrary server depending on your how your shard is configured and what is your hashing algorithm, it's definitely not possible to proceed to commands that work on multiple keys.
Yup me neither, there is no multiple implementation anymore in this backend, so it was just more natural to implement this way.
Actually my implementation seems to work fine but it clears instead of invalidating, but since the backend can't guarantee that invalid entries are kept at this stade it actually does not change anything functionnaly, maybe this could be restored later by removing the delete on read algorithm.
I'd say indeed, we should merge, and maybe let the user be able to disable the delete on read behavior using settings configuration if they wish to, and that'd be perfect. |
@@ -3,4 +3,4 @@ services: | |||
class: Drupal\redis\Cache\CacheBackendFactory | |||
arguments: ['@redis.factory', '@cache_tags.invalidator.checksum'] | |||
redis.factory: | |||
class: Drupal\redis\ClientFactory | |||
class: Drupal\redis\ClientFactory |
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.
Newline?
Just out of curiosity... do we have any idea about the extent of the performance boost we get with pipelining?
Could we set expiration time on invalidated items (http://redis.io/commands/expire) and achieve garbage collection this way? It seem this might work w/o much additional complexity, but I might be missing something.
+1 on merging. |
I've created #7 so I can try to merge this. I had to make some changes and for example use REQUEST_TIME instead of time(), as that didn't work.. the entries that were meant to expire e.g. 3 seconds in the future were already expired. Unit tests are working fine now but the new web test that I added doesn't seem to pass for me, and it passes fine on 8.x-1.x. You're welcome to try your luck on that. You can just merge it back into your PR if you can make anything work. |
I didn't do any benachmarks, indeed.
Actually it's already done, the whole point is to make all the entries volatile to avoid stalling keys. I did the explicit del calls at first glance because Drupal might sometime do thousands of get/set/del during cache wipe or cache rebuild, and it might worth the shot to clean this mess directly. |
@@ -158,7 +137,7 @@ protected function getExpiration($expire) { | |||
if ($expire == Cache::PERMANENT || $expire > $this->permTtl) { | |||
return $this->permTtl; | |||
} | |||
return $expire - REQUEST_TIME; | |||
return $expire - time(); |
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.
Drupal recommends to use REQUEST_TIME instead time()
Added ShardablePhpRedis backend for cache, fixed minor details.
This pull request is now complete, please I'd like some feedback.
@Berdir Once we agree on the majority of points and once you merge, I'll copy your repo over the drupal.org project and give you the owner rights for you to be able to maintain the 8.x branch if you wish to.