Skip to content

Commit 6662cb0

Browse files
committed
Options, Meta APIs: Introduce wp_prime_network_option_caches() to load multiple network options with a single database request.
WordPress's `get_network_option` function generally makes individual database requests for each network option. While some options are preloaded in `wp_load_core_site_options`, many still require single database calls to the network options table. Building on the work done in [56445], [56990], and [57013], which introduced the `wp_prime_option_caches` function, this commit adds two new functions: `wp_prime_network_option_caches` and `wp_prime_site_option_caches`. These functions enable developers to pass an array of option names, allowing caches for these options to be primed in a single object cache or database request. If an option is not found, the notoptions cache key is refreshed, preventing unnecessary repeated requests. The function `wp_prime_site_option_caches` is similar to `get_site_option`, enabling developers to retrieve network options on the current network without needing to know the current network ID. If these functions are called in a non-multisite environment, they fall back to using wp_prime_option_caches. These functions have been implemented in `wp_load_core_site_options`, `get_site_transient`, and `set_site_transient`. Props to spacedmonkey, peterwilsoncc, mukesh27, joemcgill. Fixes #61053. git-svn-id: https://develop.svn.wordpress.org/trunk@58182 602fd350-edb4-49c9-b593-d223f7449a82
1 parent c7db788 commit 6662cb0

2 files changed

Lines changed: 428 additions & 23 deletions

File tree

src/wp-includes/option.php

Lines changed: 126 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -643,50 +643,150 @@ function wp_load_alloptions( $force_cache = false ) {
643643
}
644644

645645
/**
646-
* Loads and primes caches of certain often requested network options if is_multisite().
646+
* Primes specific network options for the current network into the cache with a single database query.
647647
*
648-
* @since 3.0.0
649-
* @since 6.3.0 Also prime caches for network options when persistent object cache is enabled.
648+
* Only network options that do not already exist in cache will be loaded.
649+
*
650+
* If site is not multisite, then call wp_prime_option_caches().
651+
*
652+
* @since 6.6.0
653+
*
654+
* @see wp_prime_network_option_caches()
655+
*
656+
* @param string[] $options An array of option names to be loaded.
657+
*/
658+
function wp_prime_site_option_caches( array $options ) {
659+
wp_prime_network_option_caches( null, $options );
660+
}
661+
662+
/**
663+
* Primes specific network options into the cache with a single database query.
664+
*
665+
* Only network options that do not already exist in cache will be loaded.
666+
*
667+
* If site is not multisite, then call wp_prime_option_caches().
668+
*
669+
* @since 6.6.0
650670
*
651671
* @global wpdb $wpdb WordPress database abstraction object.
652672
*
653-
* @param int $network_id Optional. Network ID of network for which to prime network options cache. Defaults to current network.
673+
* @param int $network_id ID of the network. Can be null to default to the current network ID.
674+
* @param string[] $options An array of option names to be loaded.
654675
*/
655-
function wp_load_core_site_options( $network_id = null ) {
676+
function wp_prime_network_option_caches( $network_id, array $options ) {
656677
global $wpdb;
657678

658-
if ( ! is_multisite() || wp_installing() ) {
679+
if ( wp_installing() ) {
680+
return;
681+
}
682+
683+
if ( ! is_multisite() ) {
684+
wp_prime_option_caches( $options );
685+
return;
686+
}
687+
688+
if ( $network_id && ! is_numeric( $network_id ) ) {
659689
return;
660690
}
661691

662-
if ( empty( $network_id ) ) {
692+
$network_id = (int) $network_id;
693+
694+
// Fallback to the current network if a network ID is not specified.
695+
if ( ! $network_id ) {
663696
$network_id = get_current_network_id();
664697
}
665698

666-
$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' );
699+
$cache_keys = array();
700+
foreach ( $options as $option ) {
701+
$cache_keys[ $option ] = "{$network_id}:{$option}";
702+
}
703+
704+
$cache_group = 'site-options';
705+
$cached_options = wp_cache_get_multiple( array_values( $cache_keys ), $cache_group );
667706

668-
if ( wp_using_ext_object_cache() ) {
669-
$cache_keys = array();
670-
foreach ( $core_options as $option ) {
671-
$cache_keys[] = "{$network_id}:{$option}";
707+
$notoptions_key = "$network_id:notoptions";
708+
$notoptions = wp_cache_get( $notoptions_key, $cache_group );
709+
710+
if ( ! is_array( $notoptions ) ) {
711+
$notoptions = array();
712+
}
713+
714+
// Filter options that are not in the cache.
715+
$options_to_prime = array();
716+
foreach ( $cache_keys as $option => $cache_key ) {
717+
if (
718+
( ! isset( $cached_options[ $cache_key ] ) || false === $cached_options[ $cache_key ] )
719+
&& ! isset( $notoptions[ $option ] )
720+
) {
721+
$options_to_prime[] = $option;
672722
}
673-
wp_cache_get_multiple( $cache_keys, 'site-options' );
723+
}
674724

725+
// Bail early if there are no options to be loaded.
726+
if ( empty( $options_to_prime ) ) {
675727
return;
676728
}
677729

678-
$core_options_in = "'" . implode( "', '", $core_options ) . "'";
679-
$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 ) );
730+
$query_args = $options_to_prime;
731+
$query_args[] = $network_id;
732+
$results = $wpdb->get_results(
733+
$wpdb->prepare(
734+
sprintf(
735+
"SELECT meta_key, meta_value FROM $wpdb->sitemeta WHERE meta_key IN (%s) AND site_id = %s",
736+
implode( ',', array_fill( 0, count( $options_to_prime ), '%s' ) ),
737+
'%d'
738+
),
739+
$query_args
740+
)
741+
);
680742

681-
$data = array();
682-
foreach ( $options as $option ) {
683-
$key = $option->meta_key;
684-
$cache_key = "{$network_id}:$key";
685-
$option->meta_value = maybe_unserialize( $option->meta_value );
743+
$data = array();
744+
$options_found = array();
745+
foreach ( $results as $result ) {
746+
$key = $result->meta_key;
747+
$cache_key = $cache_keys[ $key ];
748+
$data[ $cache_key ] = maybe_unserialize( $result->meta_value );
749+
$options_found[] = $key;
750+
}
751+
wp_cache_set_multiple( $data, $cache_group );
752+
// If all options were found, no need to update `notoptions` cache.
753+
if ( count( $options_found ) === count( $options_to_prime ) ) {
754+
return;
755+
}
756+
757+
$options_not_found = array_diff( $options_to_prime, $options_found );
686758

687-
$data[ $cache_key ] = $option->meta_value;
759+
// Add the options that were not found to the cache.
760+
$update_notoptions = false;
761+
foreach ( $options_not_found as $option_name ) {
762+
if ( ! isset( $notoptions[ $option_name ] ) ) {
763+
$notoptions[ $option_name ] = true;
764+
$update_notoptions = true;
765+
}
766+
}
767+
768+
// Only update the cache if it was modified.
769+
if ( $update_notoptions ) {
770+
wp_cache_set( $notoptions_key, $notoptions, $cache_group );
771+
}
772+
}
773+
774+
/**
775+
* Loads and primes caches of certain often requested network options if is_multisite().
776+
*
777+
* @since 3.0.0
778+
* @since 6.3.0 Also prime caches for network options when persistent object cache is enabled.
779+
* @since 6.6.0 Uses wp_prime_network_option_caches().
780+
*
781+
* @param int $network_id Optional. Network ID of network for which to prime network options cache. Defaults to current network.
782+
*/
783+
function wp_load_core_site_options( $network_id = null ) {
784+
if ( ! is_multisite() || wp_installing() ) {
785+
return;
688786
}
689-
wp_cache_set_multiple( $data, 'site-options' );
787+
$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', 'WPLANG' );
788+
789+
wp_prime_network_option_caches( $network_id, $core_options );
690790
}
691791

692792
/**
@@ -2415,7 +2515,9 @@ function get_site_transient( $transient ) {
24152515
$transient_option = '_site_transient_' . $transient;
24162516
if ( ! in_array( $transient, $no_timeout, true ) ) {
24172517
$transient_timeout = '_site_transient_timeout_' . $transient;
2418-
$timeout = get_site_option( $transient_timeout );
2518+
wp_prime_site_option_caches( array( $transient_option, $transient_timeout ) );
2519+
2520+
$timeout = get_site_option( $transient_timeout );
24192521
if ( false !== $timeout && $timeout < time() ) {
24202522
delete_site_option( $transient_option );
24212523
delete_site_option( $transient_timeout );
@@ -2493,6 +2595,7 @@ function set_site_transient( $transient, $value, $expiration = 0 ) {
24932595
} else {
24942596
$transient_timeout = '_site_transient_timeout_' . $transient;
24952597
$option = '_site_transient_' . $transient;
2598+
wp_prime_site_option_caches( array( $option, $transient_timeout ) );
24962599

24972600
if ( false === get_site_option( $option ) ) {
24982601
if ( $expiration ) {

0 commit comments

Comments
 (0)