Problem/Motivation
MenuLinkInterface::isCacheable()'s docs say:
/**
* Returns whether the rendered link can be cached.
*
* The plugin class may make some or all of the data used in the Url object
* and build array dynamic. For example, it could include the current user
* name in the title, the current time in the description, or a destination
* query string. In addition the route parameters may be dynamic so an access
* check should be performed for each user.
*
* @return bool
* TRUE if the link can be cached, FALSE otherwise.
*/
public function isCacheable();
But In addition the route parameters may be dynamic so an access check should be performed for each user.
is wrong; this is independent of access checking. All this indicates, is whether the title, the route name or the route parameter (in short: the associated Url object) is dynamically changing.
So, the docs are wrong.
However, we should actually remove ::isCacheable. Because despite what all docs & comments every written about it say, which is that the Url object's properties may be dynamic, the only actual use case for it was MyAccountMenuLink, which used it to dynamically set the "enabled" flag on the menu link. That was fixed in #2294571: Redirect anonymous users to login page from an exception listener instead of in MaintenanceModeSubscriber and restrict access to the my-account link to authenticated users. So we're chugging along this complexity for no good reason.
That'd also be a big DX win, because it's impossible to understand how to use ::isCacheable(), really.
So, let's remove this to regain sanity.
#1805054: Cache localized, access filtered, URL resolved, and rendered menu trees needs this to work correctly, otherwise render caching of menus is not possible.
Proposed resolution
Remove it.
Remaining tasks
TBD
User interface changes
None.
API changes
MenuLinkInterface::isCacheable()removed.MenuLinkInterfacenow implementsCacheableDependencyInterface
Beta phase evaluation
| Issue category | Bug because the existing interface method makes no sense at worst, is misleading at best. |
|---|---|
| Issue priority | Major because it blocks the critical #2335661: Outbound path & route processors must specify cacheability metadata |
| Prioritized changes | The main goal of this issue is performance |
| Disruption | Close to zero disruption; only affects MenuLinkInterface implementations in contrib/custom code, of which there are likely still zero at this time |
| Comment | File | Size | Author |
|---|---|---|---|
| #12 | menulinkinterface_iscacheable-2479767-12.patch | 2.98 KB | wim leers |
Comments
Comment #1
wim leersComment #2
wim leersThe one instance that we used to have that used
isCacheable() { return FALSE; }:(Was removed in #2294571: Redirect anonymous users to login page from an exception listener instead of in MaintenanceModeSubscriber and restrict access to the my-account link to authenticated users.)
Comment #3
wim leersComment #4
fabianx commentedI am not 100% sure on that, yet.
MenuLinkInterface is a beast already in terms of provided functions (many many many ...), but given I can write a 'Hello Wim' menu link plugin, how would I specify the 'user' cache context upstream then?
[ And yes I realize that for this special example plugin contexts might be enough and would maybe bubble that information automatically, but even then the caller would need to be aware of it. ]
So what would needs to happen so that I can support MySpecialPlugin use case with proper cacheability? If it can be work arounded, lets remove it, but if it cannot I think its better we add CacheableDependencyInterface instead and make it cacheable by default.
Comment #5
dawehnerIts just funny how people fight for small changes which actually matters for DX, but this seriously, there aren't many people on earth which will implement that interface :)
What about just ignoring that method, mark it as deprecated, and assume menu links are cachable by default, and an make opt in CacheableDependencyInterface usage?
Comment #6
fabianx commentedOpt-in cacheable dependency interface works for me. :)
Comment #7
wim leersFirst and foremost: then we still have the problem that it's not clear the cacheability of what exactly is being described: only URL properties, or also menu link tree properties (such as whether it is enabled, expanded, resettable, deletable, translatable, the parent, the menu name)?
Let's assume we want to retain cacheability metadata.
Then opt-in means we have the situation that we have with
AccessResult: code will assume the typical case, and ignore the other.For access results, the typical case is that
CacheableDependencyInterfaceis implemented. Which means it's easy to forget that not all of them implement it.For menu links, if we do this, the typical case will be that
CacheableDependencyInterfaceis NOT implemented. Which means it's easy to forget that sometimes, it may be not cacheable. But, in this case, very, very few things will ever interact withMenuLinkInterfaceobjects, so the potential damage here is much lower.Still, wouldn't it then be better to then just always implement
CacheableDependencyInterface? That's more consistent.MenuLinkBaseis used by all menu link plugins anyway (at least in core), so the DX impact will still be zero.Comment #8
wim leersFirst, let's prove by simply removing it that zero test coverage exists for this.
Comment #9
dawehnerAlright, we can make major API changes again, cool
Comment #10
wim leersThe "first" only meant "as a first step in this issue".
Let's continue the discussion.
It's easy to support
CacheableDependencyInterface, but it will remain very confusing IMO. Nevertheless, there *are* use cases.We just have to make a choice.
Comment #11
fabianx commentedI think lets support CacheableDependencyInterface and have all things be cacheable by default.
Just bubble up whatever it returns.
That empowers developers to make a MenuLink uncacheable or add additional cacheable meta information, but is nothing that core uses by default or any class extended from the Base Menu Link and hence very transparent.
The API change from isCacheable => CacheableDependencyInterface should be okay for beta as its one of those changes you cannot know before starting on the performance work, which is traditionally (and rightfully so) late in the cycle. [i.e. all the performance optimizations we did for l() have been completely useless as that code was ripped out completely, the same for some other things.]
We currently finalize these API's by ensuring that all use cases in core can be adequately covered, but its hard (and takes some time) as you need to pretty much understand everything in core that can render something, which is obviously a lot.
Comment #12
wim leersDone.
This will be utilized during menu rendering in #1805054: Cache localized, access filtered, URL resolved, and rendered menu trees; it's much easier to add support for it there than here, so let's do it there. This was not yet being used anywhere, nor tested anywhere anyway (as demonstrated in #8), so this does not represent a regression.
Comment #13
fabianx commentedRTBC, we need a beta evaluation.
Unless core committers think that we should keep a BC layer on the interface this should be good to go.
Comment #14
wim leersComment #15
catchI don't see any use for a BC layer - this shouldn't be used by any contrib modules at all and it's an easy update if it is. However in case for some reason there's a module out there relying on this, we should have a change record.
Comment #16
wim leersCR filed: https://www.drupal.org/node/2482623
Comment #17
alexpottCommitted 67f0d68 and pushed to 8.0.x. Thanks!
Thanks for adding the beta evaluation to the issue summary.