Skip to content

Commit f25f587

Browse files
authored
Introduce flush timeouts (#491)
Adds the `WP_REDIS_FLUSH_TIMEOUT` constant (defaults to `5` seconds)
1 parent bdacaca commit f25f587

File tree

6 files changed

+71
-48
lines changed

6 files changed

+71
-48
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Renamed `.redis-write-test.tmp` test file to `object-cache.tmp`
1010
- Call `redis_object_cache_error` action before `wp_die()`
1111
- Allow `WP_REDIS_PLUGIN_PATH` to be defined elsewhere
12+
- Added experimental flush timeout (defaults to `5` seconds)
1213

1314
## 2.4.4
1415

FAQ.md

+8
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ This can happen when Redis Server runs out of memory and no `maxmemory-policy` w
138138
Alternatively, you can set the `WP_REDIS_MAXTTL` constant to something relatively low (like `3600` seconds) and flush the cache.
139139
</details>
140140

141+
<details>
142+
<summary><code>Flushing the cache causes timeout</code></summary>
143+
144+
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.
145+
146+
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.
147+
</details>
148+
141149
<details>
142150
<summary>Unable to flush the cache</summary>
143151

README.md

+10-9
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ The Redis Object Cache plugin comes with vast set of configuration options. If y
3636
| `WP_REDIS_PATH` | | The path to the unix socket of the Redis server |
3737
| `WP_REDIS_SCHEME` | `tcp` | The scheme used to connect: `tcp` or `unix` |
3838
| `WP_REDIS_DATABASE` | `0` | The database used by the cache: `0-15` |
39-
| `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". |
40-
| `WP_REDIS_PASSWORD` | | The password of the Redis server. Supports Redis ACLs arrays: `['user', 'password']` |
39+
| `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" |
40+
| `WP_REDIS_PASSWORD` | | The password of the Redis server, supports Redis ACLs arrays: `['user', 'password']` |
4141
| `WP_REDIS_MAXTTL` | `0` | The maximum time-to-live of cache keys |
42-
| `WP_REDIS_CLIENT` | | The client used to communicate with Redis. Defaults to `phpredis` when installed, otherwise `predis`. Supports `phpredis`, `predis`, `relay` |
42+
| `WP_REDIS_CLIENT` | | The client used to communicate with Redis (defaults to `phpredis` when installed, otherwise `predis`), supports `phpredis`, `predis`, `relay` |
4343
| `WP_REDIS_TIMEOUT` | `1` | The connection timeout in seconds |
44-
| `WP_REDIS_READ_TIMEOUT` | `1` | The timeout in seconds when reading/writing |
44+
| `WP_REDIS_READ_TIMEOUT` | `1` | The timeout in seconds when reading/writing |
4545
| `WP_REDIS_IGNORED_GROUPS` | `[]` | Groups that should not be cached between requests in Redis |
4646

4747
<details>
@@ -50,7 +50,8 @@ The Redis Object Cache plugin comes with vast set of configuration options. If y
5050
| Configuration constant | Default | Description |
5151
| ------------------------------------ | ----------- | --------------------------------------------- |
5252
| `WP_CACHE_KEY_SALT` | | Deprecated. Replaced by `WP_REDIS_PREFIX` |
53-
| `WP_REDIS_RETRY_INTERVAL` | | The number of milliseconds between retries |
53+
| `WP_REDIS_FLUSH_TIMEOUT` | `5` | Experimental. The timeout in seconds when flushing |
54+
| `WP_REDIS_RETRY_INTERVAL` | | The number of milliseconds between retries (PhpRedis only) |
5455
| `WP_REDIS_GLOBAL_GROUPS` | `[]` | Additional groups that are considered global on multisite networks |
5556
| `WP_REDIS_METRICS_MAX_TIME` | `3600` | The maximum number of seconds metrics should be stored |
5657
| `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
6970

7071
Options that exist, but **should not**, **may break without notice** in future releases and **won't receive any support** whatsoever from our team:
7172

72-
| Configuration constant | Default | Description |
73-
| ----------------------------- | ----------- | ------------------------------------------------------------------- |
73+
| Configuration constant | Default | Description |
74+
| ----------------------------- | ----------- | --------------------------------------------------------------------- |
7475
| `WP_REDIS_GRACEFUL` | `false` | Prevents exceptions from being thrown, but will cause data corruption |
75-
| `WP_REDIS_SELECTIVE_FLUSH` | `false` | Uses terribly slow Lua script for flushing |
76-
| `WP_REDIS_UNFLUSHABLE_GROUPS` | `[]` | Uses terribly slow Lua script to prevent groups from being flushed |
76+
| `WP_REDIS_SELECTIVE_FLUSH` | `false` | Uses terribly slow Lua script for flushing |
77+
| `WP_REDIS_UNFLUSHABLE_GROUPS` | `[]` | Uses terribly slow Lua script to prevent groups from being flushed |
7778

7879
</details>
7980

includes/class-plugin.php

+3-15
Original file line numberDiff line numberDiff line change
@@ -946,13 +946,7 @@ public function do_admin_actions() {
946946
);
947947

948948
if ( $result ) {
949-
try {
950-
$predis = new Predis();
951-
$predis->flush();
952-
} catch ( Exception $exception ) {
953-
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
954-
error_log( $exception );
955-
}
949+
(new Predis)->flush();
956950
}
957951

958952
/**
@@ -982,13 +976,7 @@ public function do_admin_actions() {
982976
$result = $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
983977

984978
if ( $result ) {
985-
try {
986-
$predis = new Predis();
987-
$predis->flush();
988-
} catch ( Exception $exception ) {
989-
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
990-
error_log( $exception );
991-
}
979+
(new Predis)->flush();
992980
}
993981

994982
/**
@@ -1534,7 +1522,7 @@ public function on_deactivation( $plugin ) {
15341522
wp_unschedule_event( $timestamp, 'rediscache_discard_metrics' );
15351523
}
15361524

1537-
wp_cache_flush();
1525+
(new Predis)->flush();
15381526

15391527
if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
15401528
$wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );

includes/class-predis.php

+46-12
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ class Predis {
2222
/**
2323
* Connect to Redis.
2424
*
25+
* @param int|null $read_timeout The read timeout in seconds.
2526
* @return void
2627
*/
27-
public function connect() {
28+
public function connect( $read_timeout = null ) {
2829
// Load bundled Predis library.
2930
if ( ! class_exists( '\Predis\Client' ) ) {
3031
require_once WP_REDIS_PLUGIN_PATH . '/dependencies/predis/predis/autoload.php';
@@ -39,7 +40,7 @@ public function connect() {
3940
'port' => 6379,
4041
'database' => 0,
4142
'timeout' => 1,
42-
'read_timeout' => 1,
43+
'read_timeout' => $read_timeout ?? 1,
4344
];
4445

4546
$settings = [
@@ -128,13 +129,26 @@ public function connect() {
128129
}
129130

130131
/**
131-
* Invalidate all items in the cache.
132+
* Flushes the entire Redis database using the `WP_REDIS_FLUSH_TIMEOUT`.
132133
*
133-
* @return bool True on success, false on failure.
134+
* @param bool $throw_exception Whether to throw exception on error.
135+
* @return bool
134136
*/
135-
public function flush() {
137+
public function flush( $throw_exception = false ) {
138+
$flush_timeout = defined( 'WP_REDIS_FLUSH_TIMEOUT' )
139+
? intval(WP_REDIS_FLUSH_TIMEOUT)
140+
: 5;
141+
136142
if ( is_null( $this->redis ) ) {
137-
$this->connect();
143+
try {
144+
$this->connect( $flush_timeout );
145+
} catch ( Exception $exception ) {
146+
if ( $throw_exception ) {
147+
throw $exception;
148+
}
149+
150+
return false;
151+
}
138152
}
139153

140154
if ( defined( 'WP_REDIS_CLUSTER' ) ) {
@@ -143,23 +157,43 @@ public function flush() {
143157
$this->redis->flushdb( $master );
144158
}
145159
} catch ( Exception $exception ) {
160+
if ( $throw_exception ) {
161+
throw $exception;
162+
}
163+
146164
return false;
147165
}
148-
} else {
149-
try {
150-
$this->redis->flushdb();
151-
} catch ( Exception $exception ) {
152-
return false;
166+
167+
return true;
168+
}
169+
170+
try {
171+
$this->redis->flushdb();
172+
} catch ( Exception $exception ) {
173+
if ( $throw_exception ) {
174+
throw $exception;
153175
}
176+
177+
return false;
154178
}
155179

156180
return true;
157181
}
158182

183+
/**
184+
* Flushes the entire Redis database using the `WP_REDIS_FLUSH_TIMEOUT`
185+
* and will throw an exception if anything goes wrong.
186+
*
187+
* @return bool
188+
*/
189+
public function flushOrFail() {
190+
return $this->flush( true );
191+
}
192+
159193
/**
160194
* Builds a clean connection array out of redis clusters array.
161195
*
162-
* @return array
196+
* @return array
163197
*/
164198
protected function build_cluster_connection_array() {
165199
$cluster = array_values( WP_REDIS_CLUSTER );

includes/cli/class-commands.php

+3-12
Original file line numberDiff line numberDiff line change
@@ -173,20 +173,11 @@ public function update_dropin() {
173173
*/
174174
protected function flush_redis() {
175175
try {
176-
$predis = new Predis();
177-
$predis->connect();
176+
return (new Predis)->flushOrFail();
178177
} catch ( Exception $exception ) {
179-
return $exception->getMessage();
180-
}
178+
error_log( $exception ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
181179

182-
try {
183-
$predis->flush();
184-
} catch ( Exception $exception ) {
185-
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
186-
error_log( $exception );
180+
return $exception->getMessage();
187181
}
188-
189-
return true;
190182
}
191-
192183
}

0 commit comments

Comments
 (0)