Opened 11 years ago
Closed 11 years ago
#25883 closed defect (bug) (fixed)
get_site_option cache logic for notoptions is incorrect
Reported by: |
|
Owned by: |
|
---|---|---|---|
Milestone: | 3.7.2 | Priority: | normal |
Severity: | major | Version: | 3.7 |
Component: | Options, Meta APIs | Keywords: | has-patch commit fixed-major |
Focuses: | multisite, performance | Cc: |
Description
The core function get_site_option has incorrect logic for wp_cache_get(set) for "notoptions".
Note: This is a multisite bug
Example is if you have 2 networks (A,B) and network(A) has a sitemeta record "testmetakey" and network(B) does not. If network(B) should happen to be requested first, then it save the "testmetakey" into the "notoptions" array cache. Then when network(A) makes a request for "testmetakey", it will find that "testmetakey" is in the "notoptions" cached array, and thus return nothing even though it does have a meta value for "testmetakey". Case and point is "active_sitewide_plugins"
function get_site_option( $option, $default = false, $use_cache = true ) {
global $wpdb;
// Allow plugins to short-circuit site options.
$pre = apply_filters( 'pre_site_option_' . $option, false );
if ( false !== $pre )
return $pre;
// prevent non-existent options from triggering multiple queries
$notoptions = wp_cache_get( 'notoptions', 'site-options' );
if ( isset( $notoptions[$option] ) )
return apply_filters( 'default_site_option_' . $option, $default );
if ( ! is_multisite() ) {
$default = apply_filters( 'default_site_option_' . $option, $default );
$value = get_option($option, $default);
} else {
$cache_key = "{$wpdb->siteid}:$option";
if ( $use_cache )
$value = wp_cache_get($cache_key, 'site-options');
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, $wpdb->siteid ) );
// 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 {
$notoptions[$option] = true;
wp_cache_set( 'notoptions', $notoptions, 'site-options' );
$value = apply_filters( 'default_site_option_' . $option, $default );
}
}
}
return apply_filters( 'site_option_' . $option, $value );
}
The cache_key should be using the same logic for options that are not in "notoptions":
$cache_key = "{$wpdb->siteid}:notoptions";
Patch would be:
line 760:
$notoptions = wp_cache_get( "{$wpdb->siteid}:notoptions", 'site-options' );
line 782:
wp_cache_set( "{$wpdb->siteid}:notoptions", $notoptions, 'site-options' );
Or set the notoption's cache key in some variable.
Attachments (1)
Change History (9)
#3
@
11 years ago
- Owner set to wonderboymusic
- Resolution set to fixed
- Status changed from new to closed
In 26304:
#5
@
11 years ago
- Keywords fixed-major added
- Resolution fixed deleted
- Status changed from closed to reopened
Reopening for 3.7.2.
Yeah, that cache key needs to be keyed by $wpdb->siteid.