-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
redis: use atomic operations everywhere #37758
Conversation
@@ -54,18 +54,19 @@ public function getCache() { | |||
|
|||
public function get($key) { | |||
$result = $this->getCache()->get($this->getPrefix() . $key); | |||
if ($result === false && !$this->getCache()->exists($this->getPrefix() . $key)) { |
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 check is useless. Redis does not support boolean types, so getting a false
means it did not exist.
@@ -82,6 +83,7 @@ public function remove($key) { | |||
} | |||
|
|||
public function clear($prefix = '') { | |||
// TODO: this is slow and would fail with Redis cluster | |||
$prefix = $this->getPrefix() . $prefix . '*'; | |||
$keys = $this->getCache()->keys($prefix); | |||
$deleted = $this->getCache()->del($keys); |
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 will fail for redis cluster and a large number of keys. The solution is to replace this with a lua script and run it on all masters.
->exec(); | ||
return $result !== false; | ||
} | ||
$this->getCache()->unwatch(); |
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.
5 statements here vs 1 now. Ditto for cad
.
lib/private/Memcache/Redis.php
Outdated
|
||
return $this->getCache()->eval( | ||
'if redis.call("get", KEYS[1]) == ARGV[1] then | ||
return redis.call("del", KEYS[1]) |
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.
Using unlink
here is a premature optimization that's probably harmful. In fact, unless the values are huge, unlink
internally calls del
after checking the size, so we just did those checks for no reason...
This removes a lot of acrobatics in the code and does each operation atomically using a lua script. This also reduces several round trips to the server, and the scripts are compiled and cached server-side. Notably, since all operations work only on a single key (except clear, which is broken anyway and shouldn't be used), they will continue to function and be atomic for Redis cluster. Signed-off-by: Varun Patil <[email protected]>
3671f8d
to
39e805f
Compare
Signed-off-by: Robin Appelman <[email protected]>
Added a unit test to validate the script hashes to make sure we don't forget to update them if we ever change the scripts |
Good point, thanks! |
Bump? |
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 pretty neat. I didn't knew that redis allowed to run lua script
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.
Minor comments, otherwise this seems like a nice improvement.
Is there any minimum version requirement for this on the redis side?
Thanks, will fix these tomorrow.
Redis 2.6, released ~10 years ago. |
Signed-off-by: Varun Patil <[email protected]>
CI failure unrelated |
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.
Let's get this in for 28 👍
Massive kudos for this patch @pulsejet. We ran into a production instance with incomprehensive file locking issues with a Redis cluster memcache backend. Sporadically unlocked files were still locked inside a single request. Only with this patch we could eliminate the issue. @AndyScherzinger this is not only a performance improvement but fixes Redis cluster inconsistencies. Would this change be ok to backport? |
Great! A possible reason I see is that |
@ChristophWurst yes, backporting would be fine from my pov 👍 |
/backport to stable27 |
/backport to stable26 |
@ChristophWurst any specific reason why not backporting to 25 also? |
Wrongly got the impression 25 is EOL but it's only 24 |
I think I'll backport the 26 backport because there are conflicts to resolve |
This removes a lot of acrobatics in the code and does each operation
atomically using a lua script. This also reduces several round trips
to the server, and the scripts are compiled and cached server-side.
Notably, since all operations work only on a single key (except clear,
which is broken anyway and shouldn't be used), they will continue to
function and be atomic for Redis cluster.
EDIT: I did some simple benchmark with a single client process. Looks like locking is approximately 2x faster (probably even better with multiple clients). This is with Redis on the same host, so gains will be better when latency is higher between the servers.
On a side note, using DB for locking is a joke: