Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
tillkruss committed Feb 29, 2024
2 parents 856f496 + bd2d658 commit e580c96
Show file tree
Hide file tree
Showing 13 changed files with 280 additions and 160 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 2.5.1

- Added timeouts to diagnostics
- Support rare PHP installations without `sockets` extension
- Fixed rare fatal error in `show_error_and_die()`
- Fixed deprecation notice warning in `sanitize_key_part()`

## 2.5.0

- Require WordPress 4.6 or newer
- Load text-domain only when needed
- Added `WP_REDIS_DISABLE_DROPIN_CHECK` constant
- Respect `file_mod_allowed` filter and `DISALLOW_FILE_MODS` constant
- 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)
- Dropped unnecessary default ignored groups

## 2.4.4

- Improved handling of unexpected transaction results
Expand Down
27 changes: 16 additions & 11 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ When in doubt try flushing the cache, you'd be surprised how often this resolves
<details>
<summary>HELP! My site is down!1!!11!!11</summary>

The easist way to to disable Redis on your site is deleting the `wp-content/object-cache.php` drop-in file. Alternatively, you can set the `WP_REDIS_DISABLED` constant to `true` to bypass loading it.
The easiest way to to disable Redis on your site is deleting the `wp-content/object-cache.php` drop-in file. Alternatively, you can set the `WP_REDIS_DISABLED` constant to `true` to bypass loading it.
</details>

<details>
Expand Down 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 All @@ -158,16 +166,13 @@ Alternatively, you can use a desktop client like [Medis](https://getmedis.com) o
If you don't see metrics building up, or your site is not getting faster, you might have an active plugin that flushes the object cache frequently. To diagnose this issue you can use the following snippet to find the source of the cache flush:

```php
add_action(
'redis_object_cache_flush',
function( $results, $delay, $selective, $salt, $execute_time ) {
ob_start();
echo date( 'c' ) . PHP_EOL;
debug_print_backtrace();
var_dump( func_get_args() );
error_log( ABSPATH . '/redis-cache-flush.log', 3, ob_get_clean() );
}, 10, 5
);
add_action( 'redis_object_cache_flush', function( $results ) {
ob_start();
echo date( 'c' ) . PHP_EOL;
var_dump( $results );
debug_print_backtrace();
error_log( ob_get_clean(), 3, ABSPATH . '/redis-cache-flush.log' );
}, 10, 5 );
```

Once you found the plugin responsible by checking `redis-cache-flush.log`, you can contact the plugin author(s) and reporting the issue.
Expand Down
4 changes: 2 additions & 2 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Next, install the `Redis Object Cache` plugin via the WordPress Dashboard, or us

After installing and activating the plugin, go to `WordPress -> Settings -> Redis` or `Network Admin -> Settings -> Redis` on Multisite networks. There, enable the cache and check if the plugin can connect automatically.

By default the object cache will connect to Redis Server over TCP at `127.0.0.1:6379` and use database `0`,
If not, you must edit the `wp-config.php` file in your `/wp-content` directory. By default the object cache will connect to Redis Server over TCP at `127.0.0.1:6379` and use database `0`,
if you see `Status: Not connected` either ask your hosting provider for assistance, or [configure the connection yourself](https://github.com/rhubarbgroup/redis-cache/#configuration).

A good starting configuration is:
Expand All @@ -33,7 +33,7 @@ define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
```

When editing your `wp-config.php` file, it is important that `WP_REDIS_*` constants are defined high up in the file, above these lines:
When editing the `wp-config.php` file, it is important that `WP_REDIS_*` constants are defined high up in the file, above these lines:

```php
/* That's all, stop editing! Happy publishing. */
Expand Down
33 changes: 23 additions & 10 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: `predis`, `phpredis` or `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,14 +50,16 @@ 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 |
| `WP_REDIS_DISABLED` | `false` | Emergency switch to bypass the object cache without deleting the drop-in |
| `WP_REDIS_DISABLE_ADMINBAR` | `false` | Disables admin bar display |
| `WP_REDIS_DISABLE_METRICS` | `false` | Disables metrics collection and display |
| `WP_REDIS_DISABLE_BANNERS` | `false` | Disables promotional banners |
| `WP_REDIS_DISABLE_DROPIN_CHECK` | `false` | Disables the extended drop-in write test |
| `WP_REDIS_DISABLE_DROPIN_AUTOUPDATE` | `false` | Disables the drop-in auto-update |
| `WP_REDIS_SSL_CONTEXT` | `[]` | TLS connection options for `tls` or `rediss` scheme |

Expand All @@ -68,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 Expand Up @@ -108,6 +110,15 @@ define( 'WP_REDIS_SSL_CONTEXT', [

</details>

<details>
<summary>Connecting using ACL authentication</summary>

```php
define( 'WP_REDIS_PASSWORD', [ 'username', 'password' ] );
```

</details>

## Scaling

Redis Object Cache offers various replication, sharding, cluster and sentinel setups to users with advanced technical knowledge of Redis and PHP, that have consulted the [Predis](https://github.com/predis/predis), [PhpRedis](https://github.com/phpredis/phpredis) or [Relay](https://relay.so/docs) documentation.
Expand Down Expand Up @@ -143,7 +154,7 @@ define( 'WP_REDIS_IGBINARY', true );
define( 'WP_REDIS_CLIENT', 'predis' );

define( 'WP_REDIS_SERVERS', [
'tcp://127.0.0.1:6379?database=5&alias=master',
'tcp://127.0.0.1:6379?database=5&role=master',
'tcp://127.0.0.2:6379?database=5&alias=replica-01',
] );
```
Expand Down Expand Up @@ -191,6 +202,8 @@ define( 'WP_REDIS_SERVERS', [
<https://redis.io/docs/management/scaling/>

```php
define( 'WP_REDIS_CLIENT', 'phpredis' );

define( 'WP_REDIS_CLUSTER', [
'tcp://127.0.0.1:6379?alias=node-01',
'tcp://127.0.0.2:6379?alias=node-02',
Expand Down
55 changes: 36 additions & 19 deletions includes/class-plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,6 @@ public function add_actions_and_filters() {
* @return void
*/
public function init() {
load_plugin_textdomain( 'redis-cache', false, 'redis-cache/languages' );

if ( is_admin() && ! wp_next_scheduled( 'rediscache_discard_metrics' ) ) {
wp_schedule_event( time(), 'hourly', 'rediscache_discard_metrics' );
}
Expand Down Expand Up @@ -948,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 @@ -984,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 @@ -1363,6 +1349,19 @@ public function initialize_filesystem( $url, $silent = false ) {
return true;
}

/**
* Determines whether object cache file modifications are allowed.
*
* @return bool
*/
function is_file_mod_allowed() {
return apply_filters(
'file_mod_allowed',
! defined( 'DISALLOW_FILE_MODS' ) || ! DISALLOW_FILE_MODS,
'object_cache_dropin'
);
}

/**
* Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in
*
Expand All @@ -1372,12 +1371,30 @@ public function test_filesystem_writing() {
/** @var \WP_Filesystem_Base $wp_filesystem */
global $wp_filesystem;

if ( ! $this->is_file_mod_allowed() ) {
return new WP_Error( 'disallowed', __( 'File modifications are not allowed.', 'redis-cache' ) );
}

if ( ! $this->initialize_filesystem( '', true ) ) {
return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'redis-cache' ) );
}

$dropin_check = ! defined( 'WP_REDIS_DISABLE_DROPIN_CHECK' ) || ! WP_REDIS_DISABLE_DROPIN_CHECK;

if ( ! $dropin_check ) {
if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
return true;
}

if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR . '/object-cache.php' ) ) {
return new WP_Error( 'writable', __( 'Object cache drop-in is not writable.', 'redis-cache' ) );
}

return true;
}

$cachefile = WP_REDIS_PLUGIN_PATH . '/includes/object-cache.php';
$testfile = WP_CONTENT_DIR . '/.redis-write-test.tmp';
$testfile = WP_CONTENT_DIR . '/object-cache.tmp';

if ( ! $wp_filesystem->exists( $cachefile ) ) {
return new WP_Error( 'exists', __( 'Object cache file doesn’t exist.', 'redis-cache' ) );
Expand All @@ -1390,7 +1407,7 @@ public function test_filesystem_writing() {
}

if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) {
return new WP_Error( 'copy', __( 'Content directory is not writable.', 'redis-cache' ) );
return new WP_Error( 'writable', __( 'Content directory is not writable.', 'redis-cache' ) );
}

if ( ! $wp_filesystem->copy( $cachefile, $testfile, true, FS_CHMOD_FILE ) ) {
Expand Down Expand Up @@ -1505,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
66 changes: 54 additions & 12 deletions includes/class-predis.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ 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 ) {
if ( ! function_exists( 'stream_socket_client' ) ) {
return;
}

// Load bundled Predis library.
if ( ! class_exists( '\Predis\Client' ) ) {
require_once WP_REDIS_PLUGIN_PATH . '/dependencies/predis/predis/autoload.php';
Expand All @@ -39,7 +44,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 +133,30 @@ 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 ( is_null( $this->redis ) ) {
return false;
}

if ( defined( 'WP_REDIS_CLUSTER' ) ) {
Expand All @@ -143,23 +165,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
Loading

0 comments on commit e580c96

Please sign in to comment.