-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
WP_Theme_JSON: Invalidate blocks metadata cache when needed #3359
WP_Theme_JSON: Invalidate blocks metadata cache when needed #3359
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 for working on this one @ockham, this feels much closer to an ideal fix! It largely resolves being able to save global styles in the site editor, but I'm still encountering the issue of the Button block styling not working in TwentyTwentyTwo — so I'm wondering if there's another level of cache clearing that we need to implement to get that working, too. With this PR applied, note that the custom background colour on the Site Title block is working, however the Button blocks only have their default element styling, not the green / zero border radius styling for the button in the theme's theme.json
file:
In #3352, I found I needed to clear the resolver cache, too, so I wonder if we need to update the resolver's caching logic in a similar way as you've done here in the Theme JSON class?
Ah, good spot, @andrewserong! Turns out I only tested for user modifications (setting the background color of the button block in the Global Styles menu) but forgot to test for theme-supplied styling 😅 Anyway, this part might be harder to fix 😕 I think that the problem actually also goes back to wordpress-develop/src/wp-includes/class-wp-theme-json.php Lines 512 to 515 in 92a8bd8
|
Inside
Akin to the changes I made to |
Yeah, so I haven't found a good way to smartly update the resolver's caching logic. So I've tentatively pushed b746d24 which I believe fixes the issue -- by removing the caching altogether for the theme-supplied styling and settings 😬 I'm not sure how big the performance impact would be. I'll be AFK on Friday and Monday. @andrewserong would you mind taking the lead on this one (so we'll have a fix ready for WP 6.1 Beta 3 on Tuesday)? I guess we basically need to decide between this solution and #3352 (which would require some more action on the Gutenberg side, see #3352 (comment)). Maybe folks familiar with Global Styles can weigh in on both solutions (and this PR's performance impact). cc/ @oandregal @talldan @jorgefilipecosta 😊 |
Thanks for all the digging here, @ockham!
Yes, I'm happy to dig into this one today and on Monday and try to figure out what would be the best trade-off. It'd be great to get a fix in for Beta 3, whichever direction we do go in. I'll give this PR a test and do some more exploration 👍 |
Update: I've looked into this a bit further, and I think we might need to add in a variable to keep track of the number of blocks that have been registered to fix the resolver — or to put it differently, I think we'll achieve more reliable results for this particular caching if we generate a cache key and store it in a static variable. I've opened up #3373 as an alternative — basically, taking the same general idea as this PR, but attempting to largely preserve the caching if the number of blocks hasn't changed. This appears to be necessary because when testing this PR, it seems that by removing the resolver caching, Because we need to keep track of the number of blocks registered, I thought it might make for a good parallel between the two classes for them to share a similar approach to cache busting. It also gives us a function where we can keep the cache key generation logic explicit, to hopefully aid future debugging if need be. There's definitely more we could do to improve the caching behaviour, but so far #3373 seems to resolve the issue while largely preserving the caching as far as I can tell. Happy for any feedback if folks have time to take a look at it! |
$parent_theme->merge( static::$theme ); | ||
static::$theme = $parent_theme; | ||
} | ||
$theme_json_data = static::read_json_file( static::get_file_path_from_theme( '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.
What if we only cached the result of reading the data from the filesystem/database for every source. That's perhaps the most impactful thing we do here in terms of performance.
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 other sources may suffer the same issue (be loaded before blocks are registered).
|
||
if ( null === static::$blocks_metadata ) { | ||
static::$blocks_metadata = array(); | ||
} else { | ||
// Do we have metadata for all currently registered blocks? | ||
$blocks = array_diff_key( $blocks, static::$blocks_metadata ); | ||
if ( count( $blocks ) === 0 ) { | ||
return static::$blocks_metadata; | ||
} | ||
} | ||
|
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.
Adding an explanation I posted over at WordPress/gutenberg#44658 (comment) to clarify how this works:
This mostly leverages PHP's
array_diff_key
: It looks at the list of currently registered$blocks
and compares its keys (which are block names, e.g.core/heading
) to those of the list of blocks metadata -- which are also keyed by block name. It then returns the subset of registered$blocks
whose keys aren't instatic::$blocks_metadata
. If that subset is empty, we return the cached blocks metadata, as we can assume that we have metadata for all registered blocks already.Otherwise, we add metadata -- for that subset of registered blocks only that doesn't have them yet.
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.
Importantly, this means that cache invalidation is not based on the number of blocks, but indeed on which blocks have been registered.
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.
Unfortunately we can't rely on only the registered block names because there is at least one instance where we unregister an old version of a block and re-register a new one with the same name within the same function.
tl;dr: I think that in practice, the list of registered block names is a good enough indicator for caching.
The example that's present in Core, register_legacy_post_comments_block
, is basically used to replace occurrences of the legacy Post Comments block with its successor (the Comments block), in "legacy" (compatibility) mode.
However, while this logic was relevant to have in the Gutenberg plugin while Core still included the legacy Post Comments block, the latter is no longer the case -- it has been scrapped from Core per #3154. So the unregistering logic will never kick in.
And even if it did, the metadata we're using to (re-)register it are the same that the Post Comments block was using.
This brings us to the bigger question: Can this situation -- a block is registered, then unregistered, and then re-registered under the same name but with different metadata -- happen in principle? I think the chance is fairly low. While we've seen one precedent of block re-registration in Core (where the functionality of one block was absorbed into another, but the original block hat to be retained for back-compat), this is something that isn't going to happen very often; and if it is, we're probably going to use the same metadata to register it.
I think the same would apply to plugins. I don't think there are a lot of plugins that will register a block only to un-register and then re-register it; what's more conceivable is that a different plugin does the un-registration and re-registration bit. (Generally, I'd expect that other plugin to be from the same author -- e.g. something like WooCommerce Blocks which can be used "on top of" WooCommerce. Otherwise, it's debatable if a plugin should really touch another plugin's block namespace that way at all.)
Re-registering blocks from a different author or even Core seems so risky that I don't think we need to provision for that case: That existing block's attributes (and other metadata) basically define the block's "interface", so any replacement would have to keep that intact. This doesn't seem to leave a lot of conceivable use cases.
Based on WordPress/gutenberg#44434 (comment) (which is in turn based on the concerns expressed in #3359 (comment)), I'll revert the commit that disables caching for |
This reverts commit b746d24.
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.
It works as expected! LGTM
Co-authored-by: Tonya Mork <tonya.mork@automattic.com>
Co-authored-by: Tonya Mork <tonya.mork@automattic.com>
Thank you @hellofromtonya! I've applied your suggestions 😊 |
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 looks OK. One suggestion.
Co-authored-by: David B <dream-encode@users.noreply.github.com>
Merged into core in https://core.trac.wordpress.org/changeset/54385. |
Tentative fix for 56644. Alternative to #3352.
Props @oandregal @andrewserong @talldan for tracking this down in WordPress/gutenberg#44434.
The essential conclusion from WordPress/gutenberg#44434 is that blocks metadata is cached upon reading. However, since it's read during registration of the Template Part block, metadata is never updated after that block is registered, and thus absent for a whole slew of blocks. OTOH, Global Styles uses a sanitization mechanism that, among other things, verifies that the block that a style is applied to is actually registered. This means that styles are stripped from all blocks that happen to be registered after Template Part.
The solution I'm proposing is to make
WP_Theme_JSON::get_blocks_metadata()
's caching a bit smarter, i.e. by checking if there are any blocks that have been registered for which no blocks metadata exists in cache, and to add that missing metadata.Testing Instructions
Step-by-step reproduction instructions
(based on WordPress/gutenberg#44434, originally from https://core.trac.wordpress.org/ticket/56644).
Note that this PR doesn't fix WordPress/gutenberg#44619, which, while closely related, isn't the same issue. (Some of the comments on this PR refer to that issue; I've opted to tackle it separately, see WordPress/gutenberg#44434 (comment).)
Trac ticket: https://core.trac.wordpress.org/ticket/56644
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.