pantheon-systems / wp-redis

WordPress Object Cache using Redis.
https://wordpress.org/plugins/wp-redis/
GNU General Public License v2.0
225 stars 68 forks source link

[WP Multisite] Allow flush command to only delete keys for single site #336

Open JanThiel opened 2 years ago

JanThiel commented 2 years ago

Running the wp cache flush Command calls redis flushdb. This clears the complete database. In a WordPress Multisite environment this call is very expensive as the complete cache for all sites within the network has to be recreated. As the complete Redis DB is flushed. Depending on the size of the network and the required operations to rebuild the cache, this might have severe side effects on all sites. For example using a Multisite Setup with Quota and large amounts of files, the dirsize_cache can take a huge time to rebuild. While the backend of the sites without a cached dirsize_cache is very slow.

I would like to propose and discuss an optimized behaviour: Flushing the cache in the context of one site should only remove the keys with the prefix of that site, as well as global stored keys. Redis as well as WP-Redis could handle as the key paths are known. Either using prefixes or the hash maps.

This will reduce the flush`s impact to the minimum as well as to the actually requested scope.

Consider this: wp cache flush --url=site1.com wp cache flush --url=another-site.com VS wp cache flush --network Currently all three calls will do the same.

I know that this proposal is kind of contrary to the described behaviour here: https://developer.wordpress.org/cli/commands/cache/flush/ But to be honest, the documented behaviour is subpar to what should be an efficient cache management. What do you think about this?

Or maybe asked from the other way around: How to you handle resetting the cache for a single site only?

Thanks and best, Jan

danielbachhuber commented 2 years ago

Hey @JanThiel,

I think we need to emulate WordPress core behavior as much as possible, unfortunately. WP Redis needs to act similarly to the other object cache plugins in order to be true drop-in replacement for the WordPress object cache.

Or maybe asked from the other way around: How to you handle resetting the cache for a single site only?

One option would be to connect to a different Redis database depending on which URL was requested. However, this would mean you'd have multiple copies of global cache groups, and modifications to one instance of a global cache group wouldn't be propagated to the other instances.

Another option would be to use native Redis cache groups by define( 'WP_REDIS_USE_CACHE_GROUPS', true );. The blog ID is included in the cache group name, so you could write some custom code to delete all Redis cache groups for a given blog ID. One word of warning is that, when enabled, the expiration value is not respected because expiration on group keys isn't a feature supported by Redis.

If you wanted to write a function and WP-CLI command to clear all groups for a given blog ID when WP_REDIS_USE_CACHE_GROUPS is true, I'd be open to looking at a pull request for that.

JanThiel commented 2 years ago

Thanks for your feedback @danielbachhuber!

I was taking chances at the wording in the WordPress docs stating "flushing the object cache will typically flush the cache for all sites". So I would not rate this as a hard requirement. But something most plugins do because this is the way redis´ flushdb works.

Anyway, I agree that de-facto standards implemented by other object cache implementations have to be in line. So a custom WP-CLI command should and would do the trick. Let me see what we can do. But that will be something for the gift bringing christmas season. For now we use the following command using redis-cli to do this manually:

redis-cli -a '<REDIS-AUTH>' -n <REDIS-DB-ID> KEYS "<WP-REDIS-SALT-PREFIX><WP-SITE-ID>:*" | xargs redis-cli -a '<REDIS-AUTH>' -n <REDIS-DB-ID> DEL
danielbachhuber commented 2 years ago

Sounds good!