-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add object caching for WP_Theme_JSON::compute_style_properties
#5567
base: trunk
Are you sure you want to change the base?
Add object caching for WP_Theme_JSON::compute_style_properties
#5567
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @pereirinha for the PR. Left one feedback.
$args = func_get_args(); | ||
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); | ||
$cache = wp_cache_get( $cache_key, 'wp-styles' ); | ||
|
||
if ( $cache ) { | ||
return $cache; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Certainly, you can add the code after line 1944 to avoid creating a cache for empty( $styles )
. Please insert the code in the appropriate location to achieve this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch, @mukeshpanchal27.
@pereirinha I did some benchmarking for TT4 theme home page using XHProf but i can't i can't find any improvement. Did i miss anything? |
Thanks, @mukeshpanchal27, for picking it up. Can the reason why you don't see improvements might be that you aren't using Memcached? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for digging into this, @pereirinha. Looking at the code path that generates these style properties, I wonder if we would be better off moving caching of this data to wp_add_global_styles_for_blocks()
, which is the main caller of this method. One challenge here is that there are a number of external factors that we might need to account for in order to invalidate the cache (e.g., updating the theme, core version bumps, etc.), so limiting the surface area to a specific use case might be safer. Curious what you think?
@@ -1925,6 +1925,14 @@ protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) { | |||
* @return array Returns the modified $declarations. | |||
*/ | |||
protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) { | |||
$args = func_get_args(); | |||
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); | |||
$cache = wp_cache_get( $cache_key, 'wp-styles' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably need to add a check for development mode before making use of caches, similar to how WP_Theme::get_block_patterns
does here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: If we introduce a development mode check, we might end up in a loop of discussions similar to what we had in #59719. Would it be advisable to prioritize and address that ticket first to determine the best way forward, making it easier to adopt the solution in other places? What are your thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question—thanks for asking, @mukeshpanchal27. I don't think we need that discussion to block forward progress on this ticket since it's not proposing that we make these caches persistent via transients. In this case, adding a development mode check just ensures that anyone who is developing while using an object cache would still get fresh results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mukeshpanchal27 @joemcgill, thanks for these inputs.
I just pushed an update to bail processing empty $styles
and ensure we don't leverage cache in development mode.
I'm also adding an expiry time for the cache keys — a day is a reasonable value. This will help the system to clean up old data that it's not used often, in case there's a need for resources.
I just enabled the Memcached through ENV and now i got the improvement after the changes. ![]() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ready for you to circle back, as I pushed an update addressing your feedback. And I do have a few thoughts here.
We might not even need to avoid the cache at all. You'll notice when we are generating the cache key, we use the function arguments to generate it — in a single request, we run this function many times with different arguments. When you change a value in any of the arguments, a new cache key will be generated. We won't use cached data if there's no match in the hash, hence, even if you're using cache, this strategy won't impact the development of a theme.
$args = func_get_args(); | ||
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); | ||
$cache = wp_cache_get( $cache_key, 'wp-styles' ); | ||
|
||
if ( $cache ) { | ||
return $cache; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch, @mukeshpanchal27.
@@ -1925,6 +1925,14 @@ protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) { | |||
* @return array Returns the modified $declarations. | |||
*/ | |||
protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) { | |||
$args = func_get_args(); | |||
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); | |||
$cache = wp_cache_get( $cache_key, 'wp-styles' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mukeshpanchal27 @joemcgill, thanks for these inputs.
I just pushed an update to bail processing empty $styles
and ensure we don't leverage cache in development mode.
I'm also adding an expiry time for the cache keys — a day is a reasonable value. This will help the system to clean up old data that it's not used often, in case there's a need for resources.
…operties` changes
$args = func_get_args(); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.Changed | ||
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); | ||
$cache = wp_cache_get( $cache_key, 'wp-styles' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is still being called if the theme is in development mode. Could you please review the approach taken in this section of the code and consider applying a similar approach here?
@@ -1934,6 +1934,16 @@ protected static function compute_style_properties( $styles, $settings = array() | |||
return $declarations; | |||
} | |||
|
|||
$can_use_cached = ! wp_is_development_mode( 'theme' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, we may be able to avoid the wp_is_development_mode()
check here entirely. Here's why:
- The cache key depends on the given method arguments. This means it'll always be "fresh". For example, if a style changes, a new cache key will be used.
- The only exception is the call to
wp_get_typography_font_size_value()
below, which relies on other data (e.g.wp_get_global_settings()
). So maybe we can cache everything except that one? Would that still be beneficial for performance?
That would mean that we cache the result of the foreach loop, except for the font-size
CSS property we simply store the $value
without calling wp_get_typography_font_size_value()
.
After that loop, even if we got the result from cache, we can cycle through the loop of the results and call wp_get_typography_font_size_value()
for any entry that is using the font-size
CSS property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the check for the development mode here doesn't bring any value. For that reason, I removed it.
As for your concern around wp_get_global_settings()
for the case when users tweak values, I think that we can get away with making that part of the recipe for the cache key. Here's how.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When and how is this cache invalidated? Adding caching is great, but if not correctly cache invalidated when file is change, this will make more problems than it solves.
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
…ges, it invalidates the cache
@spacedmonkey, the cache is created based on the requested arguments. A single page load will use several different cache entries, and we are using these arguments to create a md5 hash. Now we will also include the The cleanup will happen daily so that unused data gets purged. The caveat is that, even if there are no changes, the cache will have to be regenerated once a day. |
@felixarntz @mukeshpanchal27 this is ready for you to circle back. I appreciate your patience here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @pereirinha.
This does make a big impact on performance, and I think we're really close. Even after reviewing this part of the WP_Theme_JSON
system for a while, I'm not confident that I understand all of the potential side effects that could cause these values to become stale. Would appreciate thoughts from @tellthemachines or someone who worked on the original system in Gutenberg in case there's something we're missing.
I also still wonder if it would be better to move this caching strategy up to WP_Theme_JSON::get_styles_for_block
or further up the call stack, but don't think that's a blocker to getting this improvement in for early testing.
I think this data belongs in the theme_json
cache group, but that group is specifically marked to be non-persistent because of the man ways this data can become stale. Curious what @spacedmonkey thinks about alternatives for cache groups. here.
$args = array( func_get_args(), wp_get_global_settings() ); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.Changed | ||
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion/nitpick: rename $args
to $cach_args
for readability.
$args = array( func_get_args(), wp_get_global_settings() ); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.Changed | |
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); | |
$cache_args = array( func_get_args(), wp_get_global_settings() ); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.Changed | |
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $cache_args ) ); |
// The global settings can include dynamic data related to typography. We need evaluate it so that the cache is invalidated when it changes. | ||
$args = array( func_get_args(), wp_get_global_settings() ); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.Changed | ||
$cache_key = 'compute_style_properties_' . md5( wp_json_encode( $args ) ); | ||
$cache = wp_cache_get( $cache_key, 'wp-styles' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than introducing a new cache group, I think this should be a part of the existing theme_json
group. Would need to change it below where this cache value is set, as well.
$cache = wp_cache_get( $cache_key, 'wp-styles' ); | |
$cache = wp_cache_get( $cache_key, 'theme_json' ); |
@@ -2000,6 +2009,8 @@ protected static function compute_style_properties( $styles, $settings = array() | |||
} | |||
} | |||
|
|||
wp_cache_set( $cache_key, $declarations, 'wp-styles', DAY_IN_SECONDS ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure about the best expiration time here. If there is a side effect that we've not accounted for in the cache key value, showing stale styles for a full day without flushing the cache is pretty long. Maybe limiting this to once an hour for now would be safer?
wp_cache_set( $cache_key, $declarations, 'wp-styles', DAY_IN_SECONDS ); | |
wp_cache_set( $cache_key, $declarations, 'theme_json', HOUR_IN_SECONDS ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theme json is a none presient cache, so there is no need for an expiry here.
Thanks for the ping! I can't see any obvious issues with this approach and changing global styles still works well in my local dev environment. Perhaps @andrewserong or @ramonjd might have additional thoughts as iirc they worked on previous caching-related bugs. |
Thanks for the ping! I can't see any obvious issues with it, either, but I'm a little cautious about caching in general for these classes just because it can be really hard to predict what sorts of things might go wrong. For example, I see there's no guard against development mode. Will that make it hard for theme and or core developers working locally who are experimenting with making changes in the processing logic (outside of just changes within their theme)? I agree with this comment about reducing the cache expiry time down to 1 hour: #5567 (comment) that feels safer to me in case there are any issues for folks during a WP version upgrade. Actually, that'd probably be my main question: would this cache get automatically flushed when a site is upgraded? It's likely that processing logic will change between releases, for example the fluid typography rules can be tweaked, or there could be additions to this function in the future. Very cool that adding caching here can improve performance! At first I was wondering if it might introduce a performance hit with the extra calls to |
Also thanks for the ping! Love caching stuff when we can, especially huge buckets of style gloop. I'd lean on @oandregal's experience and opinion here. I believe he worked on caching theme.json output before. E.g., WordPress/gutenberg#45543 |
@@ -1934,6 +1934,15 @@ protected static function compute_style_properties( $styles, $settings = array() | |||
return $declarations; | |||
} | |||
|
|||
// The global settings can include dynamic data related to typography. We need evaluate it so that the cache is invalidated when it changes. | |||
$args = array( func_get_args(), wp_get_global_settings() ); // phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.Changed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way the theme.json code is organized is as follows:
WP_Theme_JSON
: private class that deals with atheme.json
structure. It doesn't depend on anything else.WP_Theme_JSON_Resolver
: private class that pulls data (theme.json files, theme.json from database) and merges it together appropriately (get_merged_data
). It depends on theWP_Theme_JSON
class.- Public API: functions such as
wp_get_global_settings
,wp_get_global_styles
, etc. They use and depend on the two previous private classes.
By using a public function such as wp_get_global_settings
in the WP_Theme_JSON
class, this code is creating a circular dependency. This code is saying: when processing any theme.json structure, read all existing theme.json (core, blocks, theme, user) first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback @oandregal. This makes perfect sense. I think we would either need to add the caching to the public function that relies on WP_Theme_JSON::compute_style_properties
or find a different approach to invalidation within WP_Theme_JSON
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback @oandregal. I'll have a closer look at this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've left a comment https://github.com/WordPress/wordpress-develop/pull/5567/files#r1452068125 explaining my reasoning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks fine to me
Add object caching to improve the performance of
WP_Theme_JSON::compute_style_properties
when supported.Trac ticket: 59595
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.