From 42c63725fb2dad00d37756a725b493c6803babdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C4=87kowiak?= Date: Mon, 6 Feb 2023 15:32:25 +0100 Subject: [PATCH 1/5] allow using memcached extension --- object-cache.php | 92 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/object-cache.php b/object-cache.php index d2c1847..ef9e1f9 100644 --- a/object-cache.php +++ b/object-cache.php @@ -171,6 +171,8 @@ class WP_Object_Cache { var $size_total = 0; var $slow_op_microseconds = 0.005; // 5 ms + private $using_memcached = false; + function add( $id, $data, $group = 'default', $expire = 0 ) { $key = $this->key( $id, $group ); @@ -204,7 +206,11 @@ function add( $id, $data, $group = 'default', $expire = 0 ) { $size = $this->get_data_size( $data ); $this->timer_start(); - $result = $mc->add( $key, $data, false, $expire ); + if ($this->using_memcached) { + $result = $mc->add( $key, $data, $expire); + } else { + $result = $mc->add( $key, $data, false, $expire ); + } $elapsed = $this->timer_stop(); $comment = ''; @@ -291,7 +297,11 @@ function decr( $id, $n = 1, $group = 'default' ) { function close() { foreach ( $this->mc as $bucket => $mc ) { - $mc->close(); + if ($this->using_memcached) { + $mc->quit(); + } else { + $mc->close(); + } } } @@ -336,9 +346,12 @@ function get_max_flush_number( $group ) { $values = array(); $size = 19; // size of microsecond timestamp serialized foreach ( $this->default_mcs as $i => $mc ) { - $flags = false; $this->timer_start(); - $values[ $i ] = $mc->get( $key, $flags ); + if ($this->using_memcached) { + $values[ $i ] = $mc->get( $key ); + } else { + $values[ $i ] = $mc->get( $key, false ); + } $elapsed = $this->timer_stop(); if ( empty( $values[ $i ] ) ) { @@ -359,7 +372,11 @@ function get_max_flush_number( $group ) { foreach ( $this->default_mcs as $i => $mc ) { if ( $values[ $i ] < $max ) { $this->timer_start(); - $mc->set( $key, $max, false, $expire ); + if ($this->using_memcached) { + $mc->set( $key, $max, $expire ); + } else { + $mc->set( $key, $max, false, $expire ); + } $elapsed = $this->timer_stop(); $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $elapsed, 'replication_repair' ); } @@ -374,7 +391,11 @@ function set_flush_number( $value, $group ) { $size = 19; foreach ( $this->default_mcs as $i => $mc ) { $this->timer_start(); - $mc->set( $key, $value, false, $expire ); + if ($this->using_memcached) { + $mc->set( $key, $value, $expire ); + } else { + $mc->set( $key, $value, false, $expire ); + } $elapsed = $this->timer_stop(); $this->group_ops_stats( 'set_flush_number', $key, $group, $size, $elapsed, 'replication' ); } @@ -462,7 +483,9 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { $key = $this->key( $id, $group ); $mc = $this->get_mc( $group ); $found = true; - + if ($group == 'transient' && !$force && defined('\WP_FORCE_UPDATE_TRANSIENT') && \WP_FORCE_UPDATE_TRANSIENT) { + $force = true; + } if ( isset( $this->cache[ $key ] ) && ( ! $force || in_array( $group, $this->no_mc_groups ) ) ) { if ( isset( $this->cache[ $key ][ 'value' ] ) && is_object( $this->cache[ $key ][ 'value' ] ) ) { $value = clone $this->cache[ $key ][ 'value' ]; @@ -484,14 +507,18 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { } else { $flags = false; $this->timer_start(); - $value = $mc->get( $key, $flags ); + if ($this->using_memcached) { + $value = $mc->get( $key ); + } else { + $value = $mc->get( $key, $flags ); + } $elapsed = $this->timer_stop(); // Value will be unchanged if the key doesn't exist. if ( false === $flags ) { $found = false; $value = false; - } elseif ( false === $value && ( $flags & 0xFF01 ) === 0x01 ) { + } elseif (!$this->using_memcached && false === $value && ( $flags & 0xFF01 ) === 0x01 ) { /* * The lowest byte is used for flags. * 0x01 means the value is serialized (MMC_SERIALIZED). @@ -739,7 +766,11 @@ function set( $id, $data, $group = 'default', $expire = 0 ) { $size = $this->get_data_size( $data ); $this->timer_start(); - $result = $mc->set( $key, $data, false, $expire ); + if ($this->using_memcached) { + $result = $mc->set( $key, $data, $expire ); + } else { + $result = $mc->set( $key, $data, false, $expire ); + } $elapsed = $this->timer_stop(); $this->group_ops_stats( 'set', $key, $group, $size, $elapsed ); @@ -971,6 +1002,11 @@ function __construct() { global $memcached_servers; + // Use Memcached extension. + if (defined('\WP_MEMCACHED_EXT') && \WP_MEMCACHED_EXT === 'memcached' && class_exists("\Memcached")) { + $this->using_memcached = true; + } + if ( isset( $memcached_servers ) ) { $buckets = $memcached_servers; } else { @@ -984,7 +1020,17 @@ function __construct() { } foreach ( $buckets as $bucket => $servers ) { - $this->mc[ $bucket ] = new Memcache(); + if ($this->using_memcached) { + $this->mc[ $bucket ] = new \Memcached(); + //These settings are explained at http://php.net/manual/en/memcached.addservers.php#118940 + $this->mc[ $bucket ]->setOption(\Memcached::OPT_CONNECT_TIMEOUT, 100); + $this->mc[ $bucket ]->setOption(\Memcached::OPT_REMOVE_FAILED_SERVERS, true); + $this->mc[ $bucket ]->setOption(\Memcached::OPT_RETRY_TIMEOUT, 1); + $this->mc[ $bucket ]->setOption(\Memcached::OPT_SERVER_FAILURE_LIMIT, 2); + $this->mc[ $bucket ]->setOption(\Memcached::OPT_DISTRIBUTION, \Memcached::DISTRIBUTION_CONSISTENT); + } else { + $this->mc[ $bucket ] = new Memcache(); + } foreach ( $servers as $i => $server ) { if ( 'unix://' == substr( $server, 0, 7 ) ) { @@ -1005,13 +1051,29 @@ function __construct() { } } - $this->mc[ $bucket ]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) ); - $this->mc[ $bucket ]->setCompressThreshold( 20000, 0.2 ); + if ($this->using_memcached) { + $this->mc[ $bucket ]->addServer( $node, $port, true ); + } else { + $this->mc[ $bucket ]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) ); + $this->mc[ $bucket ]->setCompressThreshold( 20000, 0.2 ); + } // Prepare individual connections to servers in default bucket for flush_number redundancy if ( 'default' === $bucket ) { - $this->default_mcs[ $i ] = new Memcache(); - $this->default_mcs[ $i ]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) ); + if ($this->using_memcached) { + $this->default_mcs[ $i ] = new \Memcached(); + //These settings are explained at http://php.net/manual/en/memcached.addservers.php#118940 + $this->default_mcs[ $i ]->setOption(\Memcached::OPT_CONNECT_TIMEOUT, 100); + $this->default_mcs[ $i ]->setOption(\Memcached::OPT_REMOVE_FAILED_SERVERS, true); + $this->default_mcs[ $i ]->setOption(\Memcached::OPT_RETRY_TIMEOUT, 1); + $this->default_mcs[ $i ]->setOption(\Memcached::OPT_SERVER_FAILURE_LIMIT, 2); + $this->default_mcs[ $i ]->setOption(\Memcached::OPT_DISTRIBUTION, \Memcached::DISTRIBUTION_CONSISTENT); + + $this->default_mcs[ $i ]->addServer( $node, $port, true ); + } else { + $this->default_mcs[ $i ] = new Memcache(); + $this->default_mcs[ $i ]->addServer( $node, $port, true, 1, 1, 15, true, array( $this, 'failure_callback' ) ); + } } } } From 4a96387034ad97c7f47b8bfe7b5975c0e987818a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C4=87kowiak?= Date: Mon, 6 Feb 2023 15:59:41 +0100 Subject: [PATCH 2/5] get multiple fix --- object-cache.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/object-cache.php b/object-cache.php index ef9e1f9..0913579 100644 --- a/object-cache.php +++ b/object-cache.php @@ -655,7 +655,11 @@ public function get_multiple( $keys, $group = 'default', $force = false ) { if ( $uncached_keys ) { $this->timer_start(); $uncached_keys_list = array_values( $uncached_keys ); - $values = $mc->get( $uncached_keys_list ); + if ($this->using_memcached){ + $values = $mc->getMulti( $uncached_keys_list ); + } else { + $values = $mc->get( $uncached_keys_list ); + } $elapsed = $this->timer_stop(); $this->group_ops_stats( 'get_multiple', $uncached_keys_list, $group, null, $elapsed ); From e206dff8b2b2fd925dce9e352d80e84fbd38861a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C4=87kowiak?= Date: Thu, 16 Feb 2023 12:52:16 +0100 Subject: [PATCH 3/5] account for not found item --- object-cache.php | 55 +++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/object-cache.php b/object-cache.php index 0913579..8e99c05 100644 --- a/object-cache.php +++ b/object-cache.php @@ -513,30 +513,37 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { $value = $mc->get( $key, $flags ); } $elapsed = $this->timer_stop(); - - // Value will be unchanged if the key doesn't exist. - if ( false === $flags ) { - $found = false; - $value = false; - } elseif (!$this->using_memcached && false === $value && ( $flags & 0xFF01 ) === 0x01 ) { - /* - * The lowest byte is used for flags. - * 0x01 means the value is serialized (MMC_SERIALIZED). - * The second lowest indicates data type: 0 is string, 1 is bool, 3 is long, 7 is double. - * `null` is serialized into a string, thus $flags is 0x0001 - * Empty string will correspond to $flags = 0x0000 (not serialized). - * For `false` or `true` $flags will be 0x0100 - * - * See: - * - https://github.com/websupport-sk/pecl-memcache/blob/2a5de3c5d9c0bd0acbcf7e6e0b7570f15f89f55b/php7/memcache_pool.h#L61-L76 - PHP 7.x - * - https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache_pool.h#L57-L76 - PHP 8.x - * - * In PHP 8, they changed the way memcache_get() handles `null`: - * https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache.c#L2175-L2177 - * - * If the return value is `null`, it is silently converted to `false`. We can only rely upon $flags to find out whether `false` is real. - */ - $value = null; + + if ($this->using_memcached) { + if (false === $value && $mc->getResultCode() == \Memcached::RES_NOTFOUND) { + $found = false; + $value = false; + } + } else { + // Value will be unchanged if the key doesn't exist. + if ( false === $flags ) { + $found = false; + $value = false; + } elseif (false === $value && ( $flags & 0xFF01 ) === 0x01 ) { + /* + * The lowest byte is used for flags. + * 0x01 means the value is serialized (MMC_SERIALIZED). + * The second lowest indicates data type: 0 is string, 1 is bool, 3 is long, 7 is double. + * `null` is serialized into a string, thus $flags is 0x0001 + * Empty string will correspond to $flags = 0x0000 (not serialized). + * For `false` or `true` $flags will be 0x0100 + * + * See: + * - https://github.com/websupport-sk/pecl-memcache/blob/2a5de3c5d9c0bd0acbcf7e6e0b7570f15f89f55b/php7/memcache_pool.h#L61-L76 - PHP 7.x + * - https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache_pool.h#L57-L76 - PHP 8.x + * + * In PHP 8, they changed the way memcache_get() handles `null`: + * https://github.com/websupport-sk/pecl-memcache/blob/ccf702b14b18fce18a1863e115a7b4c964df952e/src/memcache.c#L2175-L2177 + * + * If the return value is `null`, it is silently converted to `false`. We can only rely upon $flags to find out whether `false` is real. + */ + $value = null; + } } $this->cache[ $key ] = [ From b77d7d5c7e84491607e48ccf92e3da3b15f836d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C4=87kowiak?= Date: Thu, 9 Mar 2023 16:02:02 +0100 Subject: [PATCH 4/5] Changed constant name, added some missing class variables declarations, updated readme. --- object-cache.php | 46 +++++++++++++++++++++++++--------------------- readme.txt | 3 +++ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/object-cache.php b/object-cache.php index 8e99c05..abacbfd 100644 --- a/object-cache.php +++ b/object-cache.php @@ -167,11 +167,20 @@ class WP_Object_Cache { var $connection_errors = array(); + var $time_start = 0; var $time_total = 0; var $size_total = 0; var $slow_op_microseconds = 0.005; // 5 ms - private $using_memcached = false; + var $cache_hits = 0; + var $cache_misses = 0; + + var $global_prefix = ''; + var $blog_prefix = ''; + + var $using_memcached = false; + + private $key_salt = ''; function add( $id, $data, $group = 'default', $expire = 0 ) { $key = $this->key( $id, $group ); @@ -483,7 +492,7 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { $key = $this->key( $id, $group ); $mc = $this->get_mc( $group ); $found = true; - if ($group == 'transient' && !$force && defined('\WP_FORCE_UPDATE_TRANSIENT') && \WP_FORCE_UPDATE_TRANSIENT) { + if ($group == 'transient' && !$force && defined('WP_MEMCACHED_FORCE_TRANSIENT_UPDATE') && WP_MEMCACHED_FORCE_TRANSIENT_UPDATE) { $force = true; } if ( isset( $this->cache[ $key ] ) && ( ! $force || in_array( $group, $this->no_mc_groups ) ) ) { @@ -515,7 +524,7 @@ function get( $id, $group = 'default', $force = false, &$found = null ) { $elapsed = $this->timer_stop(); if ($this->using_memcached) { - if (false === $value && $mc->getResultCode() == \Memcached::RES_NOTFOUND) { + if (false === $value && $mc->getResultCode() == Memcached::RES_NOTFOUND) { $found = false; $value = false; } @@ -993,8 +1002,6 @@ function failure_callback( $host, $port ) { function salt_keys( $key_salt ) { if ( strlen( $key_salt ) ) { $this->key_salt = $key_salt . ':'; - } else { - $this->key_salt = ''; } } @@ -1014,7 +1021,7 @@ function __construct() { global $memcached_servers; // Use Memcached extension. - if (defined('\WP_MEMCACHED_EXT') && \WP_MEMCACHED_EXT === 'memcached' && class_exists("\Memcached")) { + if (defined('WP_MEMCACHED_EXT') && WP_MEMCACHED_EXT === 'memcached' && class_exists("Memcached")) { $this->using_memcached = true; } @@ -1032,13 +1039,13 @@ function __construct() { foreach ( $buckets as $bucket => $servers ) { if ($this->using_memcached) { - $this->mc[ $bucket ] = new \Memcached(); + $this->mc[ $bucket ] = new Memcached(); //These settings are explained at http://php.net/manual/en/memcached.addservers.php#118940 - $this->mc[ $bucket ]->setOption(\Memcached::OPT_CONNECT_TIMEOUT, 100); - $this->mc[ $bucket ]->setOption(\Memcached::OPT_REMOVE_FAILED_SERVERS, true); - $this->mc[ $bucket ]->setOption(\Memcached::OPT_RETRY_TIMEOUT, 1); - $this->mc[ $bucket ]->setOption(\Memcached::OPT_SERVER_FAILURE_LIMIT, 2); - $this->mc[ $bucket ]->setOption(\Memcached::OPT_DISTRIBUTION, \Memcached::DISTRIBUTION_CONSISTENT); + $this->mc[ $bucket ]->setOption(Memcached::OPT_CONNECT_TIMEOUT, 100); + $this->mc[ $bucket ]->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true); + $this->mc[ $bucket ]->setOption(Memcached::OPT_RETRY_TIMEOUT, 1); + $this->mc[ $bucket ]->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 2); + $this->mc[ $bucket ]->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT); } else { $this->mc[ $bucket ] = new Memcache(); } @@ -1072,13 +1079,13 @@ function __construct() { // Prepare individual connections to servers in default bucket for flush_number redundancy if ( 'default' === $bucket ) { if ($this->using_memcached) { - $this->default_mcs[ $i ] = new \Memcached(); + $this->default_mcs[ $i ] = new Memcached(); //These settings are explained at http://php.net/manual/en/memcached.addservers.php#118940 - $this->default_mcs[ $i ]->setOption(\Memcached::OPT_CONNECT_TIMEOUT, 100); - $this->default_mcs[ $i ]->setOption(\Memcached::OPT_REMOVE_FAILED_SERVERS, true); - $this->default_mcs[ $i ]->setOption(\Memcached::OPT_RETRY_TIMEOUT, 1); - $this->default_mcs[ $i ]->setOption(\Memcached::OPT_SERVER_FAILURE_LIMIT, 2); - $this->default_mcs[ $i ]->setOption(\Memcached::OPT_DISTRIBUTION, \Memcached::DISTRIBUTION_CONSISTENT); + $this->default_mcs[ $i ]->setOption(Memcached::OPT_CONNECT_TIMEOUT, 100); + $this->default_mcs[ $i ]->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true); + $this->default_mcs[ $i ]->setOption(Memcached::OPT_RETRY_TIMEOUT, 1); + $this->default_mcs[ $i ]->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 2); + $this->default_mcs[ $i ]->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT); $this->default_mcs[ $i ]->addServer( $node, $port, true ); } else { @@ -1091,9 +1098,6 @@ function __construct() { global $blog_id, $table_prefix; - $this->global_prefix = ''; - $this->blog_prefix = ''; - if ( function_exists( 'is_multisite' ) ) { $this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix; $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ); diff --git a/readme.txt b/readme.txt index 84d1304..22021fc 100644 --- a/readme.txt +++ b/readme.txt @@ -80,6 +80,9 @@ users userslugs widget ` += How can I use Memcached PHP extension? = + +Add `define('WP_MEMCACHED_EXT', 'memcached');` to wp-config.php above `/* That's all, stop editing! Happy blogging. */`. == Changelog == From 6b0a106201b9c9864bea73162ebf75f78fadd0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C4=87kowiak?= Date: Fri, 10 Mar 2023 10:31:17 +0100 Subject: [PATCH 5/5] bump version, update contributors --- object-cache.php | 4 ++-- readme.txt | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/object-cache.php b/object-cache.php index abacbfd..d63da47 100644 --- a/object-cache.php +++ b/object-cache.php @@ -3,9 +3,9 @@ /* Plugin Name: Memcached Description: Memcached backend for the WP Object Cache. -Version: 4.0.0 +Version: 4.1.0 Plugin URI: https://wordpress.org/plugins/memcached/ -Author: Ryan Boren, Denis de Bernardy, Matt Martz, Andy Skelton +Author: Ryan Boren, Denis de Bernardy, Matt Martz, Andy Skelton, apkmirror.com, Maciej Mackowiak Install this file to wp-content/object-cache.php */ diff --git a/readme.txt b/readme.txt index 22021fc..8ffe6e3 100644 --- a/readme.txt +++ b/readme.txt @@ -1,9 +1,9 @@ === Memcached Object Cache === -Contributors: ryan, sivel, andy, nacin, barry, ethitter, nickdaugherty, batmoo, simonwheatley, jenkoian, bor0, aidvu +Contributors: ryan, sivel, andy, nacin, barry, ethitter, nickdaugherty, batmoo, simonwheatley, jenkoian, bor0, aidvu, apkmirror.com, Maciej Mackowiak Tags: cache, memcached Requires at least: 5.3 Tested up to: 6.0 -Stable tag: 4.0.0 +Stable tag: 4.1.0 Requires PHP: 7.4.0 Use memcached and the PECL memcache extension to provide a backing store for the WordPress object cache. @@ -86,6 +86,11 @@ Add `define('WP_MEMCACHED_EXT', 'memcached');` to wp-config.php above `/* That's == Changelog == += 4.1.0 = +* Add support for PHP Memcached extension. +* Add option to always force an update of the transients local cache from the persistent cache. +* Add missing class variables declarations. + = 4.0.0 = * Add preemptive filter pre_wp_cache_get * Add flush_number replication to prevent accidental flush due to flush_number eviction, server rotation, etc.