Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Updated: Comment #0
Problem/Motivation
If we want to improve the page cache by removing the "content" cache tag (see #2124957: Replace 'content' cache tag with 'rendered' and use it sparingly), then we must ensure that cache tags exist for all user-modifiable pieces of content on the page. Including menus. And menus don't (yet) set cache tags in their render arrays.
Proposed resolution
Set cache tags.
Remaining tasks
Blocked on #2167039: Regression: page cache tags broken, also: some routes incorrectly use _controller -> No page object in template variables -> Fatal in seven_preprocess_page().Write test coverage.
User interface changes
None.
API changes
None.
Comment | File | Size | Author |
---|---|---|---|
#30 | menu_cache_tags-2179083-30.patch | 13.28 KB | Wim Leers |
Comments
Comment #1
Wim LeersFun fact: it is apparently useless to set cache tags on the main and secondary menu (configurable in
menu.settings.yml
using themain_links
andsecondary_links
keys).Why? Because they never end up in "the page array". They're defined in
template_preprocess_page()
($variables['main_menu'] = array(… render array …)
), and therefor are rendered directly by Twig templates. I think that changing how all that works is a whole new can of worms and therefor probably out of scope for this issue.Comment #2
Wim LeersThere are two aspects:
Ensuring cache tags are set
There are two cases:
menu_tree()
/menu_tree_output()
(which includes all of the menu blocks, i.e.\Drupal\system\Plugin\Block\SystemMenuBlock
)drupal_prepare_page()
.It's the cleanest solution I could think of. If somebody else sees a cleaner solution, I'm all ears. This is the most questionable part of the patch.
Ensuring cache tags are marked as invalid when menus change
There are again two cases:
Menu
entities\Drupal\system\Entity\Menu
config entity has receivedpostSave()
andpostDelete()
methods that invalidate the relevant cache tags.This is completely analogous to what the
\Drupal\user\Entity\Role
config entity does.MenuLink
entitiesmenu_cache_clear()
, which ensured cache entries tagged with a certain menu were deleted … but only in thecache_menu
cache bin!Instead of having a procedural function for this, I replaced the callers of that function with a
Cache::invalidateTags()
call. One less procedural function to be called.I've added test coverage for both cases.
Unfortunately, this is still blocked on #2167039: Regression: page cache tags broken, also: some routes incorrectly use _controller -> No page object in template variables -> Fatal in seven_preprocess_page(), and this in turn blocks #2124957: Replace 'content' cache tag with 'rendered' and use it sparingly. Let us please get #2167039 fixed ASAP, so we can continue here!
Comment #3
Wim LeersStraight reroll to track HEAD.
Comment #4
Wim Leers#2167039: Regression: page cache tags broken, also: some routes incorrectly use _controller -> No page object in template variables -> Fatal in seven_preprocess_page() was committed. Unpostponing.
Comment #5
moshe weitzman CreditAttribution: moshe weitzman commentedOnly nitpick I founf is that PageCacheIntegrationTest should have the word 'Tags' in it since it isn't a thorough test of whole page cache system.
Comment #7
vijaycs85Looks like menu_main_menu() and menu_main_menu() does exactly same calculation what we do inside this if statements. Would be great, if we can avoid and make use of main. Something like this - https://gist.github.com/vijaycs85/8690309
So this patch will become below
Comment #8
vijaycs85Updates:
2179083-menu-cache-tag-8-reroll.patch is just reroll
2179083-menu-cache-tag-8-with-changes.patch has changes mentioned in #7
Comment #9
vijaycs85Comment #12
Wim Leers#8 introduced a new public API function, which is completely unrelated to the problem at hand. If such off-topic refactoring is done, let's at least only introduce a private API function so that there is no API change. The function name also was highly peculiarly capitalized, fixed that (s/
menu_get_Links_source
/_menu_get_links_source
/ :)Renamed the new test to address #5.
Fixed the test failures (dumb copy/paste mistake that I made).
Comment #14
dawehnerWOW, you even can use "Links" without breaking something. Let's better name it to 'links' to stay sane.
Is there any reason we create a new test even we do have PageCacheTest already?
Comment #15
Wim Leers#14:
PageCacheTest
solely tests whether the page cache itself works. This new test (since renamed toPageCacheTagsIntegrationTest
, per Moshe's request) tests whether the integration of cache tags with the page cache is working correctly. It should eventually become a pretty large test, that covers many scenarios, to guarantee our page-level cache tags are reliable.The tests fail because they assumed the presence of a core bug that was fixed in #2180375: menu_link_rebuild_defaults() is creating duplicates of a few menu links. Fixed. They now no longer rely on forms, but just modify entities directly, which means less code, faster tests, same coverage.
Comment #16
nielsvm CreditAttribution: nielsvm commentedTEST PREPARATION:
Warning for co-testers: because of the cache-control policy set on the outgoing responses your browser will try to fetch it from local cache till expiry reached, therefore always refresh using "control+shift+R" to assure a forced re-fetch.
WITHOUT menu_cache_tags-2179083-15.patch APPLIED:
.. stopped tests as main original problem was confirmed to me, which emphasizes why this patch is so important.
WITH PATCH APPLIED AND AFTER CLEARING CACHES:
I had "node/2" that had "Test #1" as title and the link was as tab in primary links. I created "node/4" with a link that had the tabbed link from "Test #1" as parent, which means that - as long as the menu tree isn't rendered on any of the pages - the parent page should NOT expire, as the child was not rendered on it.
FOOTER MENU + BLOCK WITH LINKS RENDERED IN THEM:
NEXT: I will take a stab at writing test cases for all documented test steps above, however, I'll timebox my effort and might decide to stop it, lets hope it'll works out.
Comment #17
nielsvm CreditAttribution: nielsvm commentedThere is one important thing that I didn't mention in my last comment:
bullet #6 causes more paths to be expired than theoretically necessary but the costs of runtime-level detection whether a sibling menu link is rendered somewhere in the HTML is barely doable. Therefore having this patch behave the way it currently does far outweighs the cost of currently common scenarios where sites run with low TTLs and expiry of many pages is still MUCH cheaper than constant full-site expiry through a way too low TTL.
In other words, its a theoretic thing I noticed but this patch is totally RTBC in my opinion.
Comment #18
Wim LeersThanks, Niels!
Marking critical since the parent issue is critical and this blocks the parent.
Comment #19
dawehnerIs there any way to not couple the very specific details of having specific menus on basically every page onto the drupal_prepare_page() method which feels like part of an API or at least architecture.
Comment #20
Wim Leers#19: there's not any way that I know of. I don't like it either. Note that I'm not *creating* this problem, it's a problem that already exists. I'm merely fixing the implementation to at least set the appropriate cache tags. The real problem is that the theme system is rendering menus directly, which should not happen at all. At least now we'll have proper cache tags for those menus, and test coverage, which will help when that problem gets fixed.
Moving the special-case rendering of those menus out of the theme system in this issue is completely out of scope.
Comment #21
catchYeah that issue is #1869476: Convert global menus (primary links, secondary links) into blocks, while it's ugly hard coding those menus, it's not the fault of this patch. (not committing this yet because I haven't reviewed it).
Comment #22
catchtherefore.
Could use a @todo to remove this when they're blocks.
The default is a parameter to the function, so it's not really hardcoded?
Looks like this is checking block cache tags, but not those from the secondary/main menus?
Comment #23
catchComment #24
Wim LeersComment #26
Wim Leers24: menu_cache_tags-2179083-24.patch queued for re-testing.
Comment #28
Wim Leers24: menu_cache_tags-2179083-24.patch queued for re-testing.
Comment #30
Wim LeersStraight reroll.
Comment #31
Wim LeersBack to RTBC per #17. Everything since was just to keep up with HEAD and fix docs.
To answer #22.3:
Correct, because I cannot actually check those in there, since it's not the standard profile. There's a separate test for doing the necessary integration testing in the Standard profile, which includes the secondary/main menus:
PageCacheTagsIntegrationTest
.Comment #32
catchThanks for docs fixes/re-rolls. Committed/pushed to 8.x, thanks!
Comment #33
Wim LeersHurray! :)