Skip to content

Commit

Permalink
Introduce flush timeouts (#491)
Browse files Browse the repository at this point in the history
Adds the `WP_REDIS_FLUSH_TIMEOUT` constant (defaults to `5` seconds)
  • Loading branch information
tillkruss authored Nov 6, 2023
1 parent bdacaca commit f25f587
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 48 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</details>

<details>
<summary><code>Flushing the cache causes timeout</code></summary>

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.
</details>

<details>
<summary>Unable to flush the cache</summary>

Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

<details>
Expand All @@ -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 |
Expand All @@ -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 |

</details>

Expand Down
18 changes: 3 additions & 15 deletions includes/class-plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/**
Expand Down Expand Up @@ -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();
}

/**
Expand Down Expand Up @@ -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' );
Expand Down
58 changes: 46 additions & 12 deletions includes/class-predis.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -39,7 +40,7 @@ public function connect() {
'port' => 6379,
'database' => 0,
'timeout' => 1,
'read_timeout' => 1,
'read_timeout' => $read_timeout ?? 1,
];

$settings = [
Expand Down Expand Up @@ -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' ) ) {
Expand All @@ -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 );
Expand Down
15 changes: 3 additions & 12 deletions includes/cli/class-commands.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}

0 comments on commit f25f587

Please sign in to comment.