-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Store merged Theme JSON data in memory. #54742
Store merged Theme JSON data in memory. #54742
Conversation
This adds an additional static cache to `WP_Theme_JSON_Resolver_Gutenberg` that stores the merged data for each origin in memory to avoid recrating unnecessary `WP_Theme_JSON_Gutenberg` objects each time a function needs to get Theme JSON data. See https://core.trac.wordpress.org/ticket/57789
These changes were originally made in WordPress/wordpress-develop#5024. This applies the latest PR feedback and makes changes to the Gutenberg repo so we could test this in the plugin prior to a core merge. |
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.
Per my previous feedback on WordPress/wordpress-develop#5024, LGTM!
} | ||
|
||
// Map origins to block cache values. | ||
$cache_map = array( |
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.
What if we update the $blocks_cache
to have the proper keys (default
instead of core
, and custom
instead of user
) so we don't need 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.
@oandregal While I agree this would be ideal, I think this should be subject to another separate issue/PR. There may be backward compatibility implications from changing the cache names - probably not, but that would be a fix not really related to what this PR is aiming to achieve.
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.
If there's a path to making these consistent, I'm all for it, but was out of scope of what I was trying to show here, so I just worked with what existed.
@@ -597,26 +634,58 @@ public static function get_merged_data( $origin = 'custom' ) { | |||
_deprecated_argument( __FUNCTION__, '5.9.0' ); | |||
} | |||
|
|||
if ( ! in_array( $origin, WP_Theme_JSON_Gutenberg::VALID_ORIGINS, true ) ) { |
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.
Do we need this? Doesn't it work the same as it works today?
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.
Technically, I agree it's not needed now more than it was needed before, but it's a good practice to have. We should make sure developers passing invalid values to the function either explicitly fails or "forces" a useful result. Having this here ensures the latter.
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 just a bit of defensive coding to make the rest of the logic more predictable if someone passes an invalid origin value. I added it as a best practice to avoid potential bugs and edge cases.
null !== static::$merged[ $origin ] | ||
&& static::has_same_registered_blocks( $cache_map[ $origin ] ) | ||
// Ensure theme supports data is fresh before returning cached data for theme and custom origins. | ||
&& ( ! in_array( $origin, array( 'theme', 'custom' ), true ) || static::get_theme_supports_data() === static::$theme_support_data ) |
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 presume the logic above to convert unknown origins to custom
was necessary for this line. Thinking we can do this instead:
//
theme
andcustom
origins require access to theme supports,
// so the cached merged data will only be used for those origins if the current supports are equal to the cached ones.
( in_array( $origin, array( 'default', 'blocks' ), true ) || static::get_theme_supports_data() === static::$theme_support_data )
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 one of the places where an invalid $origin
value would be problematic, but there could be others. I considered writing this as you suggested, but decided it was likely better to explicitly check for the origin values where the theme supports data check is required, rather than the inverse. Both get the same result though.
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 left some minor comments – definitely not something that blocks the PR from landing.
In terms of failure scenarios, I cannot come up with any, so this change is good.
I'm conflicted about whether it makes sense to do it, though. Looking at the performance analysis you've posted here and here, the user metrics for velocity don't seem to be impacted. Though, it does double the memory consumed by caching both the single data origin (e.g., theme
) and the merged data origin (e.g., up to theme
). I feel the memory-velocity trade-off is a bit unbalanced (much memory for no gains in velocity).
At the same time, I reckon that some other scenarios we are not testing may have many more nodes (settings/styles/blocks in theme.json
), hence, they could see bigger wins.
I'm on the fence about this one. You have been the most invested in this research, so it's only fair that it's you who decide whether it makes sense or not. In any case, I appreciate the time and commitment to investigate it. Thanks!
@oandregal I agree this ends up being more of a micro optimization, but I don't see any harm in implementing it. To me it makes sense to have a merged cache alongside the individual caches. Generally, I am more on the side of prioritizing performance (time) over memory (unless of course the tradeoff is large/problematic). |
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 review, @oandregal. I'm similarly on the fence about whether this change is worth making. It does lead to some improvements by avoiding repeated calls to get the data from each origin and merge them together, but most of the time spent in all scenarios is in the initial calculation for each origin rather than the repeated calls.
That said, the most impactful change will be if we can reliably add some persistence to the caching strategy for either the merged data or individual origins and having this in place to start testing can help us discover potential issues with a non-persistent cache prior to introducing any persistence, so it may be worth testing as an experiment to discover scenarios where cache invalidation needs to be considered in the future.
@@ -597,26 +634,58 @@ public static function get_merged_data( $origin = 'custom' ) { | |||
_deprecated_argument( __FUNCTION__, '5.9.0' ); | |||
} | |||
|
|||
if ( ! in_array( $origin, WP_Theme_JSON_Gutenberg::VALID_ORIGINS, true ) ) { |
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 just a bit of defensive coding to make the rest of the logic more predictable if someone passes an invalid origin value. I added it as a best practice to avoid potential bugs and edge cases.
null !== static::$merged[ $origin ] | ||
&& static::has_same_registered_blocks( $cache_map[ $origin ] ) | ||
// Ensure theme supports data is fresh before returning cached data for theme and custom origins. | ||
&& ( ! in_array( $origin, array( 'theme', 'custom' ), true ) || static::get_theme_supports_data() === static::$theme_support_data ) |
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 one of the places where an invalid $origin
value would be problematic, but there could be others. I considered writing this as you suggested, but decided it was likely better to explicitly check for the origin values where the theme supports data check is required, rather than the inverse. Both get the same result though.
Closing for now as this didn't seem to have a bit impact on overall performance. |
What?
This adds an additional static cache to
WP_Theme_JSON_Resolver_Gutenberg
that stores the merged data for each origin in memory to avoid recreating unnecessaryWP_Theme_JSON_Gutenberg
objects each time a function needs to get Theme JSON data.Why?
See https://core.trac.wordpress.org/ticket/57789
How?
Testing Instructions
Running a profiler, like XHProf on a pageload before and after applying this change should demonstrate a reduction in calls to each of the getter methods for each origin, along with a reduction in merges. See example from the core repo: https://core.trac.wordpress.org/ticket/57789#comment:23
Testing Instructions for Keyboard
n/a
Screenshots or screencast