Opened 14 years ago
Closed 12 years ago
#14992 closed defect (bug) (worksforme)
When Object Caching is enabled switch_to_blog causes issues with some functions
Reported by: | simonwheatley | Owned by: | |
---|---|---|---|
Milestone: | Priority: | high | |
Severity: | major | Version: | |
Component: | Multisite | Keywords: | |
Focuses: | Cc: |
Description
When you use switch_to_blog
with an Object caching plugin installed you get incorrect links from home_url
and therefore also get_permalink
.
To reproduce:
- Setup a WP Network using sub-directories with at least two sites
- Install an Object caching plugin
- Assuming that
get_option
has been called earlier, thereby loading the cache withalloptions
, useswitch_to_blog
thenget_permalink
get_permalink
returns a URL based on the site which was active before you usedswitch_to_blog
The problem seems to be that the Object cache caches alloptions
, but these aren't refreshed or switched when switch_to_blog
is used (at this point a new set of options should be made available).
I'm using this code to get around it (this is not a patch, obviously):
/** * Hooks the WP MS action switch_blog to switch some caches cache when a blog * is switched to. * * @param int $blog_id The ID of the blog which has been switched to * @param int $prev_blog_id The ID of the blog we were in before switching * @return void **/ function kcostb_switch_blog( $blog_id, $prev_blog_id ) { // Save the previous alloptions cache $prev_alloptions = wp_cache_get( 'alloptions', 'options' ); // Do we have a previously cached alloptions for the new blog? If so, // replace the current alloptions cache otherwise delete it. if ( $alloptions = wp_cache_get( "alloptions_$blog_id", 'options' ) ) wp_cache_replace( "alloptions", $alloptions, 'options' ); else wp_cache_delete( 'alloptions', 'options' ); } add_action( 'switch_blog', 'kcostb_switch_blog', null, 2 );
Questions:
- Should this be solved by switching caches (as my mini plugin does)?
- Should we simply delete the
alloptions
cache whenswitch_to_blog
is used? - Are there any other areas affected similarly?
Happy to produce a patch if the first two questions could be advised upon.
Could be related to #12040
Change History (19)
#2
@
14 years ago
- Severity changed from normal to major
Actually, switch_to_blog() should remain cheap to call. For instance, I'm using switch_to_blog() in one case to loop through all the sites in the network.
So, I think we should best leave this to be handled on a case-by-case basis.
Also, we should probably add this in the functions documentation. See #14953
#3
@
14 years ago
Switch to blog is database only. Object Cache is not database.
Strictly spoken, this is not a bug.
As scribu already reported, this function is often not properly understood and therefore probably misused in some places. And as this ticket implies some of us might need more functionality in switching blogs, not only for the database context but also for the object-cache.
I suggest to refactor switch_to_blog into switch_to_blog_database() and deprecate the older one/change it's use to switch every context available for a switch (e.g. database and then probably soon objectcache).
Then other developers can implement new and needed functionality in similar named functions like switch_to_blog_objectchache().
Just a raw idea:
function switch_to_blog($blogid) { $blogid = normalize_blogid($blogid); if (!is_valid_blogid($blogid)) return false; current_blogid = get_current_blogid(); if (!switch_to_blog_database($blogid)) { return false; } if (!switch_to_blog_objectcache($blogid)) { switch_to_blog_database($current_blogid); return false; } return $blogid; }
In contrast to my argumentation in #14953, I think that the object-cache as it is something quite related to the database, should be able to switch as well and it should be useful to provide that functionality as well ASAP. I wonder how wordpress.com is dealing with such issues, assuming that they should have a setup when this comes into play. Maybe some wpcom devs can provide some useful feedback.
#5
in reply to:
↑ 4
@
14 years ago
Replying to scribu:
Related: #15361
I believe both of these issues could be solved if the calls to wp_cache_init() are replaced with wp_start_object_cache() and the different cache providers correctly implement a wp_cache_reset() function that handles the reset accordingly, whether that is swithing to a new cache server, clearing local cache, or just doing nothing.
#7
follow-up:
↓ 10
@
14 years ago
Imho, this would be a bug in the object cache implementations. They should key things based on the blog ID, i.e.:
$this->cache[$blog_id][$key]
vs:
$this->cache[$key]
The built-in, memory-based object cache should do the same to set the example.
#10
in reply to:
↑ 7
;
follow-up:
↓ 11
@
14 years ago
Replying to Denis-de-Bernardy:
Imho, this would be a bug in the object cache implementations. They should key things based on the blog ID, i.e.:
$this->cache[$blog_id][$key]vs:
$this->cache[$key]The built-in, memory-based object cache should do the same to set the example.
So at this point, we just need a core members decision whether we want to tackle this by making it a requirement for cache implementations to also key by blog_id and lose the wp_cache_reset() and wp_cache_init() calls after the initial load OR if we just call a wp_cache_reset() method if it exists when the blog_id changes.
#11
in reply to:
↑ 10
@
14 years ago
Replying to prettyboymp:
So at this point, we just need a core members decision whether we want to tackle this by making it a requirement for cache implementations to also key by blog_id and lose the wp_cache_reset() and wp_cache_init() calls after the initial load OR if we just call a wp_cache_reset() method if it exists when the blog_id changes.
My understanding is the decision will wait until 3.2...
#12
@
14 years ago
- Keywords 3.2-early added
- Milestone changed from Awaiting Review to Future Release
- Priority changed from normal to high
I think you all are on the right track. We don't want to do it by default... expensive to flush all that. We can discuss the various options in 3.2 early!
#14
@
14 years ago
+1 for Denis-de-Bernardy's suggestion, but with reluctance as it's going to take some time for the proposed changes to filter down to third party object cache plugin providers.
#15
@
14 years ago
The memcached implementation already adds the blog id as a prefix:
http://plugins.trac.wordpress.org/browser/memcached/trunk/object-cache.php?rev=257064#L249
#16
@
14 years ago
With a persistent Object Cache (i.e. a memcached implementation):
- Create a post ID 10 in blog 2
- From site 2, switch to blog 1
- Get post 10
- Restore current blog
- Go to site 2 and amend post 10
WP will have automatically flushed the post 10 in the object cache for blog 2, but it is persisting in it's previous state in the object cache for blog 1.
I can't think of a sane solution to this problem, I think we just have to wait for the cache in blog 1 to expire. Caveat Auctor, I guess.
Separately: There is a way to add the $blog_id
to W3TC Object Cache using the built in W3TC hooks, see this forum post.
#17
@
13 years ago
It seems the switch / restore code doesn't always call wp_cache_init() to recreate the $wp_object_cache, so the key can be incorrect in certain cases. Hooking to switch_blog and doing something like
$wp_object_cache->blog_prefix = $blog_id . ':';
might help, in addition to the original poster's recommendation.
#19
@
12 years ago
- Keywords dev-feedback 3.2-early removed
- Milestone Future Release deleted
- Resolution set to worksforme
- Status changed from new to closed
Using Memcached Object Cache
This works for me right away in trunk:
php -r "require 'wp-load.php'; echo get_permalink(1); switch_to_blog(2); echo get_permalink(1);"
The more advanced use case also works right away:
// post with ID #10 exists in both blogs wp_insert_post( array( 'post_title' => 'Cash Money', 'ID' => 10, 'post_status' => 'publish' ) ); switch_to_blog( 2 ); wp_insert_post( array( 'post_title' => 'Money Cash', 'ID' => 10, 'post_status' => 'publish' ) ); switch_to_blog( 1 ); get_post( 10 ); restore_current_blog(); wp_update_post( array( 'post_title' => 'Cache Money', 'ID' => 10 ) ); $z = get_post( 10 ); echo $z->post_title . PHP_EOL; switch_to_blog( 1 ); $z = get_post( 10 ); echo $z->post_title . PHP_EOL; switch_to_blog( 2 ); $z = get_post( 10 ); echo $z->post_title . PHP_EOL; exit();
I think these problems pre-date all of the switch_to_blog work Ryan has done recently: [21485], #21595, [21628], [21403], #21434
I also ran into this problem.
I think switching caches is the way to go, but only for one level of switching.