Problem/Motivation
There is a caching issue when adding a menu link to a node for the first time along, or changing the menu link title on an existing node. On saving the change the breadcrumb does not show the updated path (i.e the new menu link title, or the new breadcrumb if a menu link has been added) until the cache is cleared.
Steps to reproduce:
- Create a node without a menu link
- Edit the node and add a menu link, while changing the menu link title to something different as well as changing the url alias
- Save the node
EDIT: Tried reproducing on a simplytest.me site and couldn't reproduce it in the same way so wondering if it's something to do with using workbench moderation as well in the mix?
Proposed resolution
I recently upgraded from alpha1 to beta3 which introduced this bug. I believe alpha 1 had a url.path cache context which would've stopped this happening but I think adding the node/menu link to cache context would be a better solution?
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | 2827653-cache-meta-data-20.patch | 12.47 KB | bucefal91 |
Comments
Comment #2
acbramley commentedComment #3
rphair commentedComment #4
rphair commentedThanks @acbramley for the specific method of reproducing the problem. I am still coming up to speed on D8 caching and trying to clear some time in my schedule for making a more detailed study of it (the official documentation out there at this time isn't enough to provide that).
Any recommendations from someone with more experience with D8 caching would be welcome in the meantime. I have tried to keep the caching rules in one place in the code along with comments about why those were decided upon. Certainly they are not set in stone & could be replaced with revised code, or tags & context, suggested here.
Comment #5
acbramley commented@rphair thanks, it is definitely an interesting edge case which most people probably won't hit but it happened to be how one of our behat tests was running. The caching rules there at the moment all make sense to me as well.
Comment #6
kevinquillen commentedCame here to find this issue.
I initially posted about it here:
http://drupal.stackexchange.com/questions/222615/breadcrumbs-are-cached
4k4 mentions:
Comment #7
kevinquillen commentedThanks to 4k4, changing this will cause the cache to update if a menu link entity is updated:
I moved
$entity = ...out of the translated block so this could be possible.I tested this locally and was unable to reproduce my original issue regarding breadcrumb title not updating.
I'm not able to make an official patch at the moment, will later today when I get a chance.
Comment #8
kevinquillen commentedComment #9
rphair commentedthrough the code at least, you have connected with the recent translation issue, for which the fix caused another pathological case (2831084).
Looking up the full entity, thought only to be needed to translate the menu title, was only done on multilingual sites for performance reasons. Yet thanks to you & 4k4 we now know it must be done every time, in order to add the menu link content as a cacheable dependency.
I would be ready to update the module that way right now, if not for the bug pointed out in that issue which would then affect every site: specifically, views added to a menu only through their config settings won't have a way of finding their entity through their metadata. In fact I don't think such views even have MenuLinkContent (that's why I originally wrote it to read the settings from the menu plugin definition).
So we need both:
Please correct me wherever I may be wrong. I am summarising this in a comment to the thread on drupal stackexchange to see if we can get a full solution.
Comment #10
kevinquillen commentedI am not on a translated site or using any locale stuff, I am probably not the best test in that case. I also am not well versed in the new Cache subsystem but would love to know more (about it).
Comment #11
rphair commentedactually I can't comment on that StackExchange thread due to their high "reputation" bar, so posted the request as a separate "answer" here.
Comment #12
kevinquillen commentedI just gave you a boost. You're at 54 now.
Comment #13
rphair commenteddear @kevinquillen - your code above, with the entity lookup brought out of the "translated?" condition, should make the problem visible on your own site. According to the related issue, you should see it on any views that you mount on a menu through the Views UI rather than the Menu UI, as well as any builtin views (like Archive & Glossary).
Thanks for the boost up, will clean up that thread accordingly & watch for any answers from 4k4 & others...
Comment #14
kevinquillen commentedHmm, I don't have any Views yet that set a menu link. I only have one View outputting a block. Probably why I don't see it.
Comment #15
k4 commentedI've found the following menu link providers:
MenuLinkContent
This is the content entity mentioned in #6. Used for menu links entered in ui in node and menu forms. Caching is solved acc. #7.
ViewsMenuLink
Depends on the view config, so the config entity from the view needs to be the cache dependency.
LoginLogoutMenuLink
The title is generated dynamically: "log out" if the current user is authenticated, otherwise "log in".
InaccessibleMenuLink
The title is always 'Inaccessible'.
DestinationMenuLink
Modifies the menu link to add a destination, doesn't change the title.
And finally there are menu links provided in yaml files. Didn't found the class for this.
So it seems that only 2 menu link provider plugins depend on entities which can be added as cache dependencies.
I'm afraid that is all I can help here, because I've never worked with menus in D8.
Comment #16
rphair commentedInstead of using the Drupal functions directly to get the active trail, we can use the menu tree functions like this sandbox version of an earlier module release. That will return all of the menu link providers above, via
MenuLinkTreeElement->link, which can each then be added as a cacheable dependency.This will be a bit of a rewrite so will push out as dev version as soon as possible. Once sure this fixes the caching problem then we will have to address the translation issue from scratch, since none of those plugin items are translatable objects (unless the menu tree itself is already translated or translatable).
Comment #17
k4 commentedThe sandbox version looks good. It works for all menu link plugins, also for future ones. But caching can still be a problem. Although the menu link interface has cacheable dependencies, there is only a placeholder in MenuLinkBase:
None of the essential menu link plugins define a more specific getCacheTags() method. So if I didn't miss something, this code doesn't work:
$breadcrumb->addCacheableDependency($element->link);For the MenuLinkContent plugin you could think about this code, but this doesn't work either:
What is missing, is the getCacheTags() method in the class MenuLinkContent, which gets the tags from the protected entity. Hope there is somewhere a solution, but I'm out of options.
Comment #18
acbramley commentedAll of this sounds like a really good case for some tests! :)
Thanks to all of you for your work on the issue so far, I hadn't thought adding the entity would be the straight forward fix as the bug was occurring at specific changes with combinations of the menu link, node, and url but good to see it's fixing other issues.
Comment #19
rphair commented#17 was the solution I was just about to get working on (
$breadcrumb->addCacheableDependency($element->link);). Though we know it won't fix the problem immediately, I wonder if it would still be acceptable to revise the code as such and mark this issue as "blocked" pending a full caching implementation forMenuLinkBase& its descendants.We had to do something similar for another issue (2820206) after relating it explicitly to an unresolved core issue. I'm not in the Drupal community long enough to know to what extent contrib modules are expected to work around bugs or incompletions in core, so any veterans please feel free to chime in. In the meantime I'll look for or file a core issue about this as well.
Comment #20
bucefal91 commentedGuys, I kind of see the problem in the code and understand your discussion and concern about cacheability metadata, but I couldn't reproduce the issue. I came here from #2831084: Undefined index: entity_id in the MenuBasedBreadcrumbBuilder::build() method so my original concern was that ugly PHP notice.
I would propose to keep it reasonable: the job of menu_breadcrumb module is to build breadcrumb and not to bookkeep internal cacheability/data structure of whoever/whatever implementations of
MenuLinkInterface.I propose the following: let menu_breadcrumb just take care of building the breadcrumb, along the way polling all "MenuLink" participants of the breadcrumb for cacheabilty, but going deeper than invoking
::addCacheableDependency()shouldn't be menu_breadcrumb's business. If some MenuLink does not property report its cacheable metadata then it's a bug in that code and again, none of menu_breadcrumb business.I attach a patch where I follow this approach. The "heart" of breadcrumb building went from:
to
The 2nd way looks much clearer and logical to me: just get a MenuLink plugin instance, query the plugin instance for a link, add its cacheability metadata into our breadcrumb. Done.
Along the way I introduced a small Unit test that makes sure MenuLink plugin instances are actually queried for their cacheability metadata when breadcrumb is being built. I had to alter a few things here and there within
MenuBasedBreadcrumbBuilderto make it more adapted for unit testing, but 90% of those "here and there" changes was really cosmetic (going from specific class to an interface or alike).As I said, for some reason I couldn't reproduce the bug on my localhost, so I am not 100% sure I've really addressed the issue, but I did test overall breadcrumb building in this alternative version and it worked fine.
If somebody could share more precise steps to reproduce, I'd be happy to follow them and double-check on that end.
Comment #21
rphair commentedI'm also somewhat away from my coding environment. The cleaner code to get at those plugin instances is helpful & I've asked loads of people on Drupal forums how to do exactly that, so thanks very much.
Yet if memory serves me correctly (while I am getting my environment back) those plugin instances are not translatable. In this model, how would we extract the translated menu title?
Comment #22
bucefal91 commentedHi, rphair!
The plugin instances the
foreachiterates over will be implementations ofMenuLinkInterface. Looking at phpdoc ofMenuLinkInterface::getTitle()we see:So, if some implementation does not return the correct translation then it's a bug in that implementation :)
But to be sure, let's examine all existing implementations of
MenuLinkInterface.InaccessibleMenuLinkgoes asTranslation-safe
MenuLinkDefaultgoes asTranslation safety: not really sure. I couldn't quickly find how to create a menu item that will use this implementation to check it in practice, however, I would believe that Plugin API is smart enough to be translation aware. If you know how to create a menu item that maps into this implementation, please, let me know :) Otherwise I'll test by triggering the process somehow programmatically.
MenuLinkContentTranslation safe
LoginLogoutMenuLinkTranslation safe
ViewsMenuLinkTranslation safe: I just tested it and asserted the returned menu link title is translated.
So in theory it must be translated and in practice I've already tried all but 1 implementation and confirmed it is actually translated.
Comment #23
rphair commentedthanks so much @bucefal91, this is thrilling & I can't wait to check it in, just for now wanted all to know that I agree with your points. I apologise to have so little time to progress this right now & as soon as possible with get this into dev, then a bit of testing on my end, then new Beta release: hopefully all in short succession. Will also post this to translation issues reported so far, so those cases can be re-tested.
Comment #25
rphair commentedsorry about holiday-related delays, have tested OK here & will keep an eye on it pending a release candidate if continues to hold up in community testing; patch included in 8.x-1.0-beta6. Also this definitively fixes https://www.drupal.org/node/2831084 since there is no need for the entity lookup. Thanks for the great community support on this one.
Comment #26
bucefal91 commentedThanks to you! :) I know it's holidays season and often module maintainers even in normal "business" seasons are slow on reviewing/accepting patches, so I am pleased to see such involvement from your side :)
Comment #27
acbramley commentedThanks for all the work guys! The latest stable version has fixed any issues I had.
This module would benefit a lot from automated tests though :)
Comment #29
kevinquillen commentedIt seems like this issue is back in 8.x-1.1, I am updating the menu link title on a node form and saving. The breadcrumb does not reflect the change until I clear the site cache.
Comment #30
rphair commentedIt can't be "back in 8.x-1.1" since none of the caching code changed from 8.x-1.0 ... though reports of breadcrumb caching coming and going have happened in the meantime. As far as I can tell these are due to changes on the back end.
I'm attaching another issue where this problem manifested, then disappeared in a further Drupal version, during this recent period without any caching changes to Menu Breadcrumb.
I would welcome a report of Menu Breadcrumb using the wrong caching strategy, after the comprehensive review above, but without that I would conclude that this problem is upstream.
Comment #31
ruloweb commentedIt happens to me the same than Kevin, Breadcrumb is not updated until I clear cache. I tried in a fresh Drupal 8.3.2 installation using simpletest.me
I will try later with different versions of Drupal.
Thanks!
Comment #32
k4 commented#30: Menu Breadcrumb is using the correct caching strategy. But the MenuLinkContent plugin still returns no cache tags. Just checked this in 8.4 HEAD. So this affects all D8 versions.
Menu Breadcrumb could add the tags with some code like this, until this is fixed in core:
Comment #33
rphair commentedthanks @k4, will put this into dev as soon as I have a chance to properly test what I am doing; others please stay tuned...
Comment #34
rphair commentedopened up separate issue (#2887053: Manually add cache tags for MenuLinkContent) to treat this workaround separately from regular logic of the module (which I believe is robust). Please test on affected sites, either with dev version or with 8.x-1.2 plus patch at 2887053->#3. If reports of fixing all caching problems will release as 8.x-1.3.
Comment #35
rphair commentednearly 2 weeks with no reported problems from dev users, so have released this single change as 8.x-1.3. Dear @k4, when you notice that this problem is fixed upstream please drop us a line & I will strip out the relevant patch, thanks again...
Comment #37
awolfey commentedI'm having this issue again, logged in and anon, on a multilingual site. I'll have to look into it more, but wanted to at least report it.
Comment #38
rphair commentedAs soon as we either find a test case that reproduces this problem in a current version, or get confirmation here that comment #32 is no longer true (i.e. MenuLinkContent plugin is finally returning cache tags), I could try backing out the patch for child issue #2887053 from dev to see if Drupal 8.5 will finally do the right thing on its own.
Any scrutiny of the code in that patch would also be welcome in case there is a better approach. Also anyone who knows a core issue corresponding to the missing cache tags please post here. To attract all these kinds of attention I'm opening this issue up again.
Though this module isn't responsible for fixing caching fundamentals it has been nice to have that workaround in place to support this module's adoption. Still I would prefer to remove the patch if it's no longer doing the right thing.
Comment #39
rphair commentedIssue 2977598 changes the caching rules to invalidate cached breadcrumbs when either the path or the active trail changes. I believe this should encompass the cache issues reported here: please reopen if not.
Comment #40
k4 commented#39: I don't think there is a connection. Issue 2977598 reverts the change New 'path parent' cache context for breadcrumbs. This issue on the other hand is about cache tags, so that the breadcrumbs can be invalidated in cache when menu links are modified in database, for example change the menu link title as described in the steps to reproduce.
Comment #41
rphair commentedThanks @k4 - you are right - and I have stated badly my desire to draw out any remaining caching problems for this module now that a few versions of Drupal have come out since this issue was opened long ago. I hope anyone seeing cache problems will therefore link it here even if a new issue is opened, so the people have been helpful so far can help certify the cache behaviour of the module going forward.