WordPress.org

Make WordPress Core

Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#16287 closed enhancement (invalid)

get_option() - store retrieved option in a global array?

Reported by: digitalnature Owned by:
Milestone: Priority: normal
Severity: normal Version: 3.1
Component: Database Keywords: get_option, database
Focuses: Cc:

Description

Hi

Themes, plugins and core call this function a lot, and there are many times when the same option gets requested more than once.

I guess this is not a problem when the user has some kind of cache plugin turned on, but most people don't, so the same database query gets to run twice or more in the same request. This saves around 30% of db calls on a typical wp site.

Is it possible to store the value of a retrieved option in a global array? and when the same option is requested again it would get pulled out from that array instead of the db.

Or at least add a action hook in that function so we can override it :)
because option_optionname filter doesn't help...

Change History (18)

comment:1 garyc403 years ago

Retrieved options are stored in WP object cache. When getting the value of an option, if the cache key for that option doesn't exist, only then does WordPress query the database. WP object cache is built-in.

Take a look at get_option() source and you can see wp_cache_get() and wp_cache_set() in there.

So I guess this is an invalid ticket.

comment:2 follow-up: digitalnature3 years ago

I did take a look. Object cache is disabled by default so that will not happen on most WP sites out there.

Also using globals for this would be faster than any file-based cache system. Eventually you could store options that have large values in cache...

Last edited 3 years ago by digitalnature (previous) (diff)

comment:3 in reply to: ↑ 2 garyc403 years ago

Replying to digitalnature:

I did take a look. Object cache is disabled by default so that will not happen on most WP sites out there.

That's not true.

Take a look at wp-settings.php, line 77 where it says wp_start_object_cache(). If there's no object-cache.php file defined in wp-content dir, the default wp-includes/cache.php is used.

The drop in file is usually for persistent cache, while the default cache.php is in-memory, per request cache.

comment:4 scribu3 years ago

  • Milestone Awaiting Review deleted
  • Resolution set to invalid
  • Status changed from new to closed

The options are already stored in a global. That's what "object cache", or in-memory-caching (the default caching mechanism in WP) does.

comment:5 digitalnature3 years ago

Well this doesn't happen on my site...

comment:6 scribu3 years ago

You can open a thread in the support forums with a more detailed explanation of your problem and add a link to it here.

comment:7 digitalnature3 years ago

@scribu: did you test it?

try for example the recent comments widget, comment out this line in default-widgets.php

add_action( 'comment_post', array(&$this, 'flush_widget_cache') );

then post a new comment. theoretically you shouldn't see it in the widget.

I don't think the default cache system works. I tested this both on localhost and on a shared host, same behaviour. But it does work when activating plugins mentioned here - http://codex.wordpress.org/Function_Reference/WP_Cache

comment:8 westi3 years ago

The built in cache works for the current page load.

To have a persistent cache accross page loads you need to use one of the object cache plugins which cache the data in memcache / apc / ...

comment:9 garyc403 years ago

Just in case you're still confused about the difference between in-memory and persistent cache in WordPress:

Remember that the default object cache is not persistent, which means when you start another request (by commenting via AJAX, or reloading the dashboard), the cache is emptied and repopulated. Think of it as a variable in a PHP file.

var_dump( $a ); // outputs null all the time, no matter how many times you reload the page
$a = 1; // this value is only valid for this request, when you reload the page, it's gone

Persistent cache on the other hand, preserve the cache across page requests. Think of it like, saving $a to a file, and loading it back in the subsequent requests:

if ( ! $a = read_from_file() )
	$a = 0;
	
$a = ++;

save_to_file( $a );

Got the difference? WP_Object_Cache aims to be an in-memory cache, which means its purpose is to save unnecessary db queries within each single request. In that sense, it's working perfectly.

If you want the persistent behavior, install one of those plugins that you mentioned.

Hope this makes sense.

Damn, I have too much time on my hand :)

comment:10 follow-up: digitalnature3 years ago

thank you very much for clarifying that, but then we're back on the purpose of the ticket :)
because the built in cache doesn't seem to work very good for the current page load.

test this yourself with get_option():

  • activate a few plugins, and add a few widgets
  • load a page, and check the db query count (I used get_num_queries() in footer)
  • in get_option(), add:
    global $__wp_settings;
    if(isset($__wp_settings[$option])) return $__wp_settings[$option];
    
  • and at the end, before returning:
    $__wp_settings[$option] = $value;
    
  • load the same page, and check db query count again

you'll notice that a global makes the page do less queries than wp cache (in my case it's 70 vs 90)

Last edited 3 years ago by digitalnature (previous) (diff)

comment:11 in reply to: ↑ 10 scribu3 years ago

Replying to digitalnature:

you'll notice that a global makes the page do less queries than wp cache (in my case it's 70 vs 90)

No, it does not. Globals are wiped out on each page load.

I think what's going on is that the options that cause extra queries for you are not autoloaded.

As already suggested, this is not a problem with WordPress but with your code.

Last edited 3 years ago by scribu (previous) (diff)

comment:12 follow-up: digitalnature3 years ago

ok, now I'm confused. garyc40 & westi said wp default cache is not persistent, so it's wiped out on each load just like the globals.

I think what's going on is that the options that cause extra queries for you are not autoloaded.

As already suggested, this is not a problem with WordPress but with your code.

I don't really have a code here. I was referring to the use of the get_option function in wp, plugins and themes...

comment:13 in reply to: ↑ 12 ; follow-up: scribu3 years ago

Replying to digitalnature:

ok, now I'm confused. garyc40 & westi said wp default cache is not persistent, so it's wiped out on each load just like the globals.

Exactly.

Additionally, right when WP starts, all options are loaded into the cache (a global, by default), so that when you call get_option() it doesn't cause an extra query.

But, this doesn't work for options that don't exist or that have the 'autoload' flag off.

comment:14 in reply to: ↑ 13 westi3 years ago

Replying to scribu:

But, this doesn't work for options that don't exist or that have the 'autoload' flag off.

But these will be cached after one db query each.

comment:15 digitalnature3 years ago

then why is gen_num_queries() reporting less queries when I use a global to do this?

comment:16 scribu3 years ago

I did the modification you suggested and it actually increased the number of queries, from 81 to 91, so there's definitely something going on with your setup.

comment:17 scribu3 years ago

That was apparently caused by the fact that the cached value wasn't unserialized.

Instead, I modified the last line of get_option() to this:

return $__wp_settings[$option] = apply_filters( 'option_' . $option, maybe_unserialize( $value ) );

Now the page is displaying properly and the query count remains the same as with a clean install, proving that the options are indeed stored in a global already.

comment:18 digitalnature3 years ago

you're right. when I add maybe_unserialize the query count will match like you said.
I think the lower query count was because other queries don't fire because wrong option values :)

thank you

Note: See TracTickets for help on using tickets.