diff --git a/CHANGELOG.md b/CHANGELOG.md index 01b65cd3..a91b55ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Renamed `.redis-write-test.tmp` test file to `object-cache.tmp` - Call `redis_object_cache_error` action before `wp_die()` - Allow `WP_REDIS_PLUGIN_PATH` to be defined elsewhere +- Added experimental flush timeout (defaults to `5` seconds) ## 2.4.4 diff --git a/FAQ.md b/FAQ.md index cef2ee38..57f9a992 100644 --- a/FAQ.md +++ b/FAQ.md @@ -138,6 +138,14 @@ This can happen when Redis Server runs out of memory and no `maxmemory-policy` w Alternatively, you can set the `WP_REDIS_MAXTTL` constant to something relatively low (like `3600` seconds) and flush the cache. +
+Flushing the cache causes timeout + +This can happen when the dataset in Redis Server is quite large. Consider increasing `WP_REDIS_READ_TIMEOUT` and `WP_REDIS_FLUSH_TIMEOUT` to 5-10 seconds. + +Alternatively, starting with Redis 6.2, setting the `lazyfree-lazy-user-flush` in the `redis.conf` configuration directive to `yes` changes the default flush mode to be asynchronous. +
+
Unable to flush the cache diff --git a/README.md b/README.md index 20594f70..1b6d3f92 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,12 @@ The Redis Object Cache plugin comes with vast set of configuration options. If y | `WP_REDIS_PATH` | | The path to the unix socket of the Redis server | | `WP_REDIS_SCHEME` | `tcp` | The scheme used to connect: `tcp` or `unix` | | `WP_REDIS_DATABASE` | `0` | The database used by the cache: `0-15` | -| `WP_REDIS_PREFIX` | | The prefix used for all cache keys to avoid data collisions, replaces `WP_CACHE_KEY_SALT`. Should be human readable, not a "salt". | -| `WP_REDIS_PASSWORD` | | The password of the Redis server. Supports Redis ACLs arrays: `['user', 'password']` | +| `WP_REDIS_PREFIX` | | The prefix used for all cache keys to avoid data collisions (replaces `WP_CACHE_KEY_SALT`), should be human readable and not a "salt" | +| `WP_REDIS_PASSWORD` | | The password of the Redis server, supports Redis ACLs arrays: `['user', 'password']` | | `WP_REDIS_MAXTTL` | `0` | The maximum time-to-live of cache keys | -| `WP_REDIS_CLIENT` | | The client used to communicate with Redis. Defaults to `phpredis` when installed, otherwise `predis`. Supports `phpredis`, `predis`, `relay` | +| `WP_REDIS_CLIENT` | | The client used to communicate with Redis (defaults to `phpredis` when installed, otherwise `predis`), supports `phpredis`, `predis`, `relay` | | `WP_REDIS_TIMEOUT` | `1` | The connection timeout in seconds | -| `WP_REDIS_READ_TIMEOUT` | `1` | The timeout in seconds when reading/writing | +| `WP_REDIS_READ_TIMEOUT` | `1` | The timeout in seconds when reading/writing | | `WP_REDIS_IGNORED_GROUPS` | `[]` | Groups that should not be cached between requests in Redis |
@@ -50,7 +50,8 @@ The Redis Object Cache plugin comes with vast set of configuration options. If y | Configuration constant | Default | Description | | ------------------------------------ | ----------- | --------------------------------------------- | | `WP_CACHE_KEY_SALT` | | Deprecated. Replaced by `WP_REDIS_PREFIX` | -| `WP_REDIS_RETRY_INTERVAL` | | The number of milliseconds between retries | +| `WP_REDIS_FLUSH_TIMEOUT` | `5` | Experimental. The timeout in seconds when flushing | +| `WP_REDIS_RETRY_INTERVAL` | | The number of milliseconds between retries (PhpRedis only) | | `WP_REDIS_GLOBAL_GROUPS` | `[]` | Additional groups that are considered global on multisite networks | | `WP_REDIS_METRICS_MAX_TIME` | `3600` | The maximum number of seconds metrics should be stored | | `WP_REDIS_IGBINARY` | `false` | Whether to use the igbinary PHP extension for serialization | @@ -69,11 +70,11 @@ The Redis Object Cache plugin comes with vast set of configuration options. If y Options that exist, but **should not**, **may break without notice** in future releases and **won't receive any support** whatsoever from our team: -| Configuration constant | Default | Description | -| ----------------------------- | ----------- | ------------------------------------------------------------------- | +| Configuration constant | Default | Description | +| ----------------------------- | ----------- | --------------------------------------------------------------------- | | `WP_REDIS_GRACEFUL` | `false` | Prevents exceptions from being thrown, but will cause data corruption | -| `WP_REDIS_SELECTIVE_FLUSH` | `false` | Uses terribly slow Lua script for flushing | -| `WP_REDIS_UNFLUSHABLE_GROUPS` | `[]` | Uses terribly slow Lua script to prevent groups from being flushed | +| `WP_REDIS_SELECTIVE_FLUSH` | `false` | Uses terribly slow Lua script for flushing | +| `WP_REDIS_UNFLUSHABLE_GROUPS` | `[]` | Uses terribly slow Lua script to prevent groups from being flushed |
diff --git a/includes/class-plugin.php b/includes/class-plugin.php index 047d980c..2ed728a7 100644 --- a/includes/class-plugin.php +++ b/includes/class-plugin.php @@ -946,13 +946,7 @@ public function do_admin_actions() { ); if ( $result ) { - try { - $predis = new Predis(); - $predis->flush(); - } catch ( Exception $exception ) { - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log - error_log( $exception ); - } + (new Predis)->flush(); } /** @@ -982,13 +976,7 @@ public function do_admin_actions() { $result = $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' ); if ( $result ) { - try { - $predis = new Predis(); - $predis->flush(); - } catch ( Exception $exception ) { - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log - error_log( $exception ); - } + (new Predis)->flush(); } /** @@ -1534,7 +1522,7 @@ public function on_deactivation( $plugin ) { wp_unschedule_event( $timestamp, 'rediscache_discard_metrics' ); } - wp_cache_flush(); + (new Predis)->flush(); if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) { $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' ); diff --git a/includes/class-predis.php b/includes/class-predis.php index 2fe11c5d..3cb2339e 100644 --- a/includes/class-predis.php +++ b/includes/class-predis.php @@ -22,9 +22,10 @@ class Predis { /** * Connect to Redis. * + * @param int|null $read_timeout The read timeout in seconds. * @return void */ - public function connect() { + public function connect( $read_timeout = null ) { // Load bundled Predis library. if ( ! class_exists( '\Predis\Client' ) ) { require_once WP_REDIS_PLUGIN_PATH . '/dependencies/predis/predis/autoload.php'; @@ -39,7 +40,7 @@ public function connect() { 'port' => 6379, 'database' => 0, 'timeout' => 1, - 'read_timeout' => 1, + 'read_timeout' => $read_timeout ?? 1, ]; $settings = [ @@ -128,13 +129,26 @@ public function connect() { } /** - * Invalidate all items in the cache. + * Flushes the entire Redis database using the `WP_REDIS_FLUSH_TIMEOUT`. * - * @return bool True on success, false on failure. + * @param bool $throw_exception Whether to throw exception on error. + * @return bool */ - public function flush() { + public function flush( $throw_exception = false ) { + $flush_timeout = defined( 'WP_REDIS_FLUSH_TIMEOUT' ) + ? intval(WP_REDIS_FLUSH_TIMEOUT) + : 5; + if ( is_null( $this->redis ) ) { - $this->connect(); + try { + $this->connect( $flush_timeout ); + } catch ( Exception $exception ) { + if ( $throw_exception ) { + throw $exception; + } + + return false; + } } if ( defined( 'WP_REDIS_CLUSTER' ) ) { @@ -143,23 +157,43 @@ public function flush() { $this->redis->flushdb( $master ); } } catch ( Exception $exception ) { + if ( $throw_exception ) { + throw $exception; + } + return false; } - } else { - try { - $this->redis->flushdb(); - } catch ( Exception $exception ) { - return false; + + return true; + } + + try { + $this->redis->flushdb(); + } catch ( Exception $exception ) { + if ( $throw_exception ) { + throw $exception; } + + return false; } return true; } + /** + * Flushes the entire Redis database using the `WP_REDIS_FLUSH_TIMEOUT` + * and will throw an exception if anything goes wrong. + * + * @return bool + */ + public function flushOrFail() { + return $this->flush( true ); + } + /** * Builds a clean connection array out of redis clusters array. * - * @return array + * @return array */ protected function build_cluster_connection_array() { $cluster = array_values( WP_REDIS_CLUSTER ); diff --git a/includes/cli/class-commands.php b/includes/cli/class-commands.php index 3c816f96..7628f616 100644 --- a/includes/cli/class-commands.php +++ b/includes/cli/class-commands.php @@ -173,20 +173,11 @@ public function update_dropin() { */ protected function flush_redis() { try { - $predis = new Predis(); - $predis->connect(); + return (new Predis)->flushOrFail(); } catch ( Exception $exception ) { - return $exception->getMessage(); - } + error_log( $exception ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log - try { - $predis->flush(); - } catch ( Exception $exception ) { - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log - error_log( $exception ); + return $exception->getMessage(); } - - return true; } - }