Networks and Sites: Revert the use of the metadata API for `*_network_options` functions.

[54080] refactored the logic in `get_network_option()`, `update_network_option()` and `delete_network_option()` to use the metadata API. However, this change resulted in issues with large multisite installs that utilize memcached having network options > 1MB in size.

This change reverts [54080] and all related follow-up changes.

Reverts [54080], [54081], and [54082].  Partially reverts [54267] and [54402].

Props pavelschoffer, rebasaurus, johnbillion, spacedmonkey, desrosj, rinatkhaziev.
Fixes #56845.
See #37181.
Built from https://develop.svn.wordpress.org/trunk@54637


git-svn-id: http://core.svn.wordpress.org/trunk@54189 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
davidbaumwald 2022-10-18 18:16:16 +00:00
parent e4fa7aaecd
commit 1664e1a05f
8 changed files with 199 additions and 94 deletions

View File

@ -86,57 +86,53 @@ class WP_Network_Query {
* Sets up the network query, based on the query vars passed.
*
* @since 4.6.0
* @since 6.1.0 Introduced the 'update_network_meta_cache' parameter.
*
* @param string|array $query {
* Optional. Array or query string of network query parameters. Default empty.
*
* @type int[] $network__in Array of network IDs to include. Default empty.
* @type int[] $network__not_in Array of network IDs to exclude. Default empty.
* @type bool $count Whether to return a network count (true) or array of network objects.
* Default false.
* @type string $fields Network fields to return. Accepts 'ids' (returns an array of network IDs)
* or empty (returns an array of complete network objects). Default empty.
* @type int $number Maximum number of networks to retrieve. Default empty (no limit).
* @type int $offset Number of networks to offset the query. Used to build LIMIT clause.
* Default 0.
* @type bool $no_found_rows Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
* @type string|array $orderby Network status or array of statuses. Accepts 'id', 'domain', 'path',
* 'domain_length', 'path_length' and 'network__in'. Also accepts false,
* an empty array, or 'none' to disable `ORDER BY` clause. Default 'id'.
* @type string $order How to order retrieved networks. Accepts 'ASC', 'DESC'. Default 'ASC'.
* @type string $domain Limit results to those affiliated with a given domain. Default empty.
* @type string[] $domain__in Array of domains to include affiliated networks for. Default empty.
* @type string[] $domain__not_in Array of domains to exclude affiliated networks for. Default empty.
* @type string $path Limit results to those affiliated with a given path. Default empty.
* @type string[] $path__in Array of paths to include affiliated networks for. Default empty.
* @type string[] $path__not_in Array of paths to exclude affiliated networks for. Default empty.
* @type string $search Search term(s) to retrieve matching networks for. Default empty.
* @type bool $update_network_cache Whether to prime the cache for found networks. Default true.
* @type bool $update_network_meta_cache Whether to prime the metadata (option) cache for found networks.
* Default true.
* @type int[] $network__in Array of network IDs to include. Default empty.
* @type int[] $network__not_in Array of network IDs to exclude. Default empty.
* @type bool $count Whether to return a network count (true) or array of network objects.
* Default false.
* @type string $fields Network fields to return. Accepts 'ids' (returns an array of network IDs)
* or empty (returns an array of complete network objects). Default empty.
* @type int $number Maximum number of networks to retrieve. Default empty (no limit).
* @type int $offset Number of networks to offset the query. Used to build LIMIT clause.
* Default 0.
* @type bool $no_found_rows Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
* @type string|array $orderby Network status or array of statuses. Accepts 'id', 'domain', 'path',
* 'domain_length', 'path_length' and 'network__in'. Also accepts false,
* an empty array, or 'none' to disable `ORDER BY` clause. Default 'id'.
* @type string $order How to order retrieved networks. Accepts 'ASC', 'DESC'. Default 'ASC'.
* @type string $domain Limit results to those affiliated with a given domain. Default empty.
* @type string[] $domain__in Array of domains to include affiliated networks for. Default empty.
* @type string[] $domain__not_in Array of domains to exclude affiliated networks for. Default empty.
* @type string $path Limit results to those affiliated with a given path. Default empty.
* @type string[] $path__in Array of paths to include affiliated networks for. Default empty.
* @type string[] $path__not_in Array of paths to exclude affiliated networks for. Default empty.
* @type string $search Search term(s) to retrieve matching networks for. Default empty.
* @type bool $update_network_cache Whether to prime the cache for found networks. Default true.
* }
*/
public function __construct( $query = '' ) {
$this->query_var_defaults = array(
'network__in' => '',
'network__not_in' => '',
'count' => false,
'fields' => '',
'number' => '',
'offset' => '',
'no_found_rows' => true,
'orderby' => 'id',
'order' => 'ASC',
'domain' => '',
'domain__in' => '',
'domain__not_in' => '',
'path' => '',
'path__in' => '',
'path__not_in' => '',
'search' => '',
'update_network_cache' => true,
'update_network_meta_cache' => true,
'network__in' => '',
'network__not_in' => '',
'count' => false,
'fields' => '',
'number' => '',
'offset' => '',
'no_found_rows' => true,
'orderby' => 'id',
'order' => 'ASC',
'domain' => '',
'domain__in' => '',
'domain__not_in' => '',
'path' => '',
'path__in' => '',
'path__not_in' => '',
'search' => '',
'update_network_cache' => true,
);
if ( ! empty( $query ) ) {
@ -247,8 +243,8 @@ class WP_Network_Query {
// $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
$_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) );
// Ignore these arguments, as the queried result will be the same regardless.
unset( $_args['fields'], $_args['update_network_cache'], $_args['update_network_meta_cache'] );
// Ignore the $fields, $update_network_cache arguments as the queried result will be the same regardless.
unset( $_args['fields'], $_args['update_network_cache'] );
$key = md5( serialize( $_args ) );
$last_changed = wp_cache_get_last_changed( 'networks' );
@ -290,7 +286,7 @@ class WP_Network_Query {
}
if ( $this->query_vars['update_network_cache'] ) {
_prime_network_caches( $network_ids, $this->query_vars['update_network_meta_cache'] );
_prime_network_caches( $network_ids );
}
// Fetch full network objects from the primed cache.

View File

@ -351,7 +351,7 @@ class WP_Site_Query {
// $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
$_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) );
// Ignore these arguments, as the queried result will be the same regardless.
// Ignore the $fields, $update_site_cache, $update_site_meta_cache argument as the queried result will be the same regardless.
unset( $_args['fields'], $_args['update_site_cache'], $_args['update_site_meta_cache'] );
$key = md5( serialize( $_args ) );

View File

@ -743,7 +743,6 @@ function wp_start_object_cache() {
'site-details',
'site-options',
'site-transient',
'site_meta',
'rss',
'users',
'useremail',

View File

@ -564,7 +564,6 @@ function switch_to_blog( $new_blog_id, $deprecated = null ) {
'site-details',
'site-options',
'site-transient',
'site_meta',
'rss',
'users',
'useremail',
@ -656,7 +655,6 @@ function restore_current_blog() {
'site-details',
'site-options',
'site-transient',
'site_meta',
'rss',
'users',
'useremail',

View File

@ -113,7 +113,7 @@ function get_active_blog_for_user( $user_id ) {
* @return int Number of active sites on the network.
*/
function get_blog_count( $network_id = null ) {
return (int) get_network_option( $network_id, 'blog_count' );
return get_network_option( $network_id, 'blog_count' );
}
/**
@ -2563,7 +2563,7 @@ function get_space_allowed() {
*
* @param int $space_allowed Upload quota in megabytes for the current blog.
*/
return (int) apply_filters( 'get_space_allowed', $space_allowed );
return apply_filters( 'get_space_allowed', $space_allowed );
}
/**

View File

@ -84,7 +84,6 @@ function clean_network_cache( $ids ) {
$network_ids = (array) $ids;
wp_cache_delete_multiple( $network_ids, 'networks' );
wp_cache_delete_multiple( $network_ids, 'site_meta' );
foreach ( $network_ids as $id ) {
/**
@ -108,44 +107,35 @@ function clean_network_cache( $ids ) {
* cache using the network group with the key using the ID of the networks.
*
* @since 4.6.0
* @since 6.1.0 Introduced the `$update_meta_cache` parameter.
*
* @param array $networks Array of network row objects.
* @param bool $update_meta_cache Whether to update site meta cache. Default true.
* @param array $networks Array of network row objects.
*/
function update_network_cache( $networks, $update_meta_cache = true ) {
function update_network_cache( $networks ) {
$data = array();
foreach ( (array) $networks as $network ) {
$data[ $network->id ] = $network;
}
wp_cache_add_multiple( $data, 'networks' );
if ( $update_meta_cache ) {
$network_ids = array_keys( $data );
update_meta_cache( 'site', $network_ids );
}
}
/**
* Adds any networks from the given IDs to the cache that do not already exist in cache.
*
* @since 4.6.0
* @since 6.1.0 Introduced the `$update_meta_cache` parameter.
* @since 6.1.0 This function is no longer marked as "private".
*
* @see update_network_cache()
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param array $network_ids Array of network IDs.
* @param bool $update_meta_cache Whether to update site meta cache. Default true.
* @param array $network_ids Array of network IDs.
*/
function _prime_network_caches( $network_ids, $update_meta_cache = true ) {
function _prime_network_caches( $network_ids ) {
global $wpdb;
$non_cached_ids = _get_non_cached_ids( $network_ids, 'networks' );
if ( ! empty( $non_cached_ids ) ) {
$fresh_networks = $wpdb->get_results( sprintf( "SELECT $wpdb->site.* FROM $wpdb->site WHERE id IN (%s)", implode( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
update_network_cache( $fresh_networks, $update_meta_cache );
update_network_cache( $fresh_networks );
}
}

View File

@ -348,12 +348,15 @@ function wp_load_alloptions( $force_cache = false ) {
* Loads and caches certain often requested site options if is_multisite() and a persistent cache is not being used.
*
* @since 3.0.0
* @since 6.1.0 Now uses update_meta_cache().
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int $network_id Optional site ID for which to query the options. Defaults to the current site.
*/
function wp_load_core_site_options( $network_id = null ) {
if ( ! is_multisite() || wp_installing() ) {
global $wpdb;
if ( ! is_multisite() || wp_using_ext_object_cache() || wp_installing() ) {
return;
}
@ -361,7 +364,20 @@ function wp_load_core_site_options( $network_id = null ) {
$network_id = get_current_network_id();
}
update_meta_cache( 'site', $network_id );
$core_options = array( 'site_name', 'siteurl', 'active_sitewide_plugins', '_site_transient_timeout_theme_roots', '_site_transient_theme_roots', 'site_admins', 'can_compress_scripts', 'global_terms_enabled', 'ms_files_rewriting' );
$core_options_in = "'" . implode( "', '", $core_options ) . "'";
$options = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM $wpdb->sitemeta WHERE meta_key IN ($core_options_in) AND site_id = %d", $network_id ) );
$data = array();
foreach ( $options as $option ) {
$key = $option->meta_key;
$cache_key = "{$network_id}:$key";
$option->meta_value = maybe_unserialize( $option->meta_value );
$data[ $cache_key ] = $option->meta_value;
}
wp_cache_set_multiple( $data, 'site-options' );
}
/**
@ -1370,10 +1386,10 @@ function update_site_option( $option, $value ) {
* Retrieves a network's option value based on the option name.
*
* @since 4.4.0
* @since 6.1.0 Now uses get_metadata().
*
* @see get_option()
* @see get_metadata()
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int $network_id ID of the network. Can be null to default to the current network ID.
* @param string $option Name of the option to retrieve. Expected to not be SQL-escaped.
@ -1381,6 +1397,8 @@ function update_site_option( $option, $value ) {
* @return mixed Value set for the option.
*/
function get_network_option( $network_id, $option, $default = false ) {
global $wpdb;
if ( $network_id && ! is_numeric( $network_id ) ) {
return false;
}
@ -1421,7 +1439,12 @@ function get_network_option( $network_id, $option, $default = false ) {
return $pre;
}
if ( ! is_multisite() ) {
// Prevent non-existent options from triggering multiple queries.
$notoptions_key = "$network_id:notoptions";
$notoptions = wp_cache_get( $notoptions_key, 'site-options' );
if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
/**
* Filters the value of a specific default network option.
*
@ -1436,21 +1459,44 @@ function get_network_option( $network_id, $option, $default = false ) {
* @param string $option Option name.
* @param int $network_id ID of the network.
*/
$default = apply_filters( "default_site_option_{$option}", $default, $option, $network_id );
return apply_filters( "default_site_option_{$option}", $default, $option, $network_id );
}
if ( ! is_multisite() ) {
/** This filter is documented in wp-includes/option.php */
$default = apply_filters( 'default_site_option_' . $option, $default, $option, $network_id );
$value = get_option( $option, $default );
} else {
$meta = get_metadata_raw( 'site', $network_id, $option );
if ( is_array( $meta ) && ! empty( $meta ) ) {
$value = array_shift( $meta );
} else {
/** This filter is documented in wp-includes/option.php */
$value = apply_filters( "default_site_option_{$option}", $default, $option, $network_id );
$cache_key = "$network_id:$option";
$value = wp_cache_get( $cache_key, 'site-options' );
/** This action is documented in wp-includes/meta.php */
$value = apply_filters( 'default_site_metadata', $value, $network_id, $option, true, 'site' );
if ( ! isset( $value ) || false === $value ) {
$row = $wpdb->get_row( $wpdb->prepare( "SELECT meta_value FROM $wpdb->sitemeta WHERE meta_key = %s AND site_id = %d", $option, $network_id ) );
// Has to be get_row() instead of get_var() because of funkiness with 0, false, null values.
if ( is_object( $row ) ) {
$value = $row->meta_value;
$value = maybe_unserialize( $value );
wp_cache_set( $cache_key, $value, 'site-options' );
} else {
if ( ! is_array( $notoptions ) ) {
$notoptions = array();
}
$notoptions[ $option ] = true;
wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
/** This filter is documented in wp-includes/option.php */
$value = apply_filters( 'default_site_option_' . $option, $default, $option, $network_id );
}
}
}
if ( ! is_array( $notoptions ) ) {
$notoptions = array();
wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
}
/**
* Filters the value of an existing network option.
*
@ -1474,10 +1520,10 @@ function get_network_option( $network_id, $option, $default = false ) {
* Existing options will not be updated.
*
* @since 4.4.0
* @since 6.1.0 Now uses add_metadata().
*
* @see add_option()
* @see add_metadata()
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int $network_id ID of the network. Can be null to default to the current network ID.
* @param string $option Name of the option to add. Expected to not be SQL-escaped.
@ -1485,6 +1531,8 @@ function get_network_option( $network_id, $option, $default = false ) {
* @return bool True if the option was added, false otherwise.
*/
function add_network_option( $network_id, $option, $value ) {
global $wpdb;
if ( $network_id && ! is_numeric( $network_id ) ) {
return false;
}
@ -1514,11 +1562,48 @@ function add_network_option( $network_id, $option, $value ) {
*/
$value = apply_filters( "pre_add_site_option_{$option}", $value, $option, $network_id );
$notoptions_key = "$network_id:notoptions";
if ( ! is_multisite() ) {
$result = add_option( $option, $value, '', 'no' );
} else {
$value = sanitize_option( $option, $value );
$result = add_metadata( 'site', $network_id, wp_slash( $option ), wp_slash( $value ), true );
$cache_key = "$network_id:$option";
// Make sure the option doesn't already exist.
// We can check the 'notoptions' cache before we ask for a DB query.
$notoptions = wp_cache_get( $notoptions_key, 'site-options' );
if ( ! is_array( $notoptions ) || ! isset( $notoptions[ $option ] ) ) {
if ( false !== get_network_option( $network_id, $option, false ) ) {
return false;
}
}
$value = sanitize_option( $option, $value );
$serialized_value = maybe_serialize( $value );
$result = $wpdb->insert(
$wpdb->sitemeta,
array(
'site_id' => $network_id,
'meta_key' => $option,
'meta_value' => $serialized_value,
)
);
if ( ! $result ) {
return false;
}
wp_cache_set( $cache_key, $value, 'site-options' );
// This option exists now.
$notoptions = wp_cache_get( $notoptions_key, 'site-options' ); // Yes, again... we need it to be fresh.
if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
unset( $notoptions[ $option ] );
wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
}
}
if ( $result ) {
@ -1560,10 +1645,8 @@ function add_network_option( $network_id, $option, $value ) {
* Removes a network option by name.
*
* @since 4.4.0
* @since 6.1.0 Now uses delete_metadata().
*
* @see delete_option()
* @see delete_metadata()
*
* @global wpdb $wpdb WordPress database abstraction object.
*
@ -1572,6 +1655,8 @@ function add_network_option( $network_id, $option, $value ) {
* @return bool True if the option was deleted, false otherwise.
*/
function delete_network_option( $network_id, $option ) {
global $wpdb;
if ( $network_id && ! is_numeric( $network_id ) ) {
return false;
}
@ -1600,7 +1685,20 @@ function delete_network_option( $network_id, $option ) {
if ( ! is_multisite() ) {
$result = delete_option( $option );
} else {
$result = delete_metadata( 'site', $network_id, wp_slash( $option ), '' );
$row = $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->sitemeta} WHERE meta_key = %s AND site_id = %d", $option, $network_id ) );
if ( is_null( $row ) || ! $row->meta_id ) {
return false;
}
$cache_key = "$network_id:$option";
wp_cache_delete( $cache_key, 'site-options' );
$result = $wpdb->delete(
$wpdb->sitemeta,
array(
'meta_key' => $option,
'site_id' => $network_id,
)
);
}
if ( $result ) {
@ -1640,10 +1738,10 @@ function delete_network_option( $network_id, $option ) {
* Updates the value of a network option that was already added.
*
* @since 4.4.0
* @since 6.1.0 Now uses update_metadata().
*
* @see update_option()
* @see update_metadata()
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int $network_id ID of the network. Can be null to default to the current network ID.
* @param string $option Name of the option. Expected to not be SQL-escaped.
@ -1651,6 +1749,8 @@ function delete_network_option( $network_id, $option ) {
* @return bool True if the value was updated, false otherwise.
*/
function update_network_option( $network_id, $option, $value ) {
global $wpdb;
if ( $network_id && ! is_numeric( $network_id ) ) {
return false;
}
@ -1700,11 +1800,33 @@ function update_network_option( $network_id, $option, $value ) {
return add_network_option( $network_id, $option, $value );
}
$notoptions_key = "$network_id:notoptions";
$notoptions = wp_cache_get( $notoptions_key, 'site-options' );
if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
unset( $notoptions[ $option ] );
wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
}
if ( ! is_multisite() ) {
$result = update_option( $option, $value, 'no' );
} else {
$value = sanitize_option( $option, $value );
$result = update_metadata( 'site', $network_id, wp_slash( $option ), wp_slash( $value ) );
$value = sanitize_option( $option, $value );
$serialized_value = maybe_serialize( $value );
$result = $wpdb->update(
$wpdb->sitemeta,
array( 'meta_value' => $serialized_value ),
array(
'site_id' => $network_id,
'meta_key' => $option,
)
);
if ( $result ) {
$cache_key = "$network_id:$option";
wp_cache_set( $cache_key, $value, 'site-options' );
}
}
if ( $result ) {

View File

@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.1-RC1-54636';
$wp_version = '6.1-RC1-54637';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.