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.
Now we use lazy builders for flag links, we need to ensure that the render cache that contains references to those builders gets cleared.
For example, if you currently view a node with a flag link, then delete that flag, then reload the node, the site crashes:
Call to a member function getLinkTypePlugin() on null in /Users/xyz/sites/drupal/modules/flag/src/FlagLinkBuilder.php on line 54
This is because the lazy builder is trying to work with a flag that's not there any more.
Adding a flag *does* clear the right caches, so we're doing half of this right! :)
Comment | File | Size | Author |
---|---|---|---|
#27 | interdiff-deleting_a_flag_should-2629426-25-27.txt | 805 bytes | edurenye |
#27 | deleting_a_flag_should-2629426-27.patch | 3.3 KB | edurenye |
| |||
#27 | deleting_a_flag_should-2629426-27-test_only.patch | 1.54 KB | edurenye |
#25 | interdiff-deleting_a_flag_should-2629426-20-25.txt | 1.53 KB | edurenye |
#25 | deleting_a_flag_should-2629426-25.patch | 2.89 KB | edurenye |
|
Comments
Comment #2
joachim CreditAttribution: joachim commentedComment #3
joachim CreditAttribution: joachim commentedComment #4
BerdirWe need to make sure that the cache tag for the flag is added to the render array in hook_entity_view().
Comment #5
martin107 CreditAttribution: martin107 commentedI think the problem is a layer deeper than that
ActionLinkTypeBase:buildLink() is it better that we declare the appropriate cache tags every time we build a link?
In that case we need a cache tag for both the flag and the entity.
I may be missing something!
Comment #6
joachim CreditAttribution: joachim commented> ActionLinkTypeBase:buildLink() is it better that we declare the appropriate cache tags every time we build a link?
No, because that is called by a lazy builder. My understanding at least is that the lazy builder is to account for the flag link changing between flagged and unflagged, but that whether the link is shown at all or not is something for which the entity cache should be cleared, so the lazy builder's anchor is no longer present in the render array.
Comment #7
martin107 CreditAttribution: martin107 commentedThank for the response...
I need to lay out the chain of thought .... I am missing something.
The reason for using the place holder :-
To separate the thing subject to rapid change ( the flag link )
away from the thing that would otherwise be cached ... the article - or whatever.
I get that but
By linking the #lazy_biulder element to the rapidly changing flag -
does that not invalidate the separation argument and force the article to become dependant on the flag anyway?
Regardless --- I tried implementing the suggestion....and note it does fix the problem!
I think I may have made a mistake .... it is easy to screw up the keys in a render array..
but I am going to post away just in case the problem is deeper than our understanding.
Comment #8
BerdirThe lazy builder exists because whether the flag shows flag or unflag varies per user (and if he can actually access it). We don't want to cache a version of the whole node for every user, so we use a lazy builder to calculate only that link.
Changing the flag doesn't happen often. If you do that, then might have results that we can't handle. For example, if you change the field to show not as an extra field but as a link? That's not something we can handle in a lazy builder.
So if that happens, we want to invalidate all currently cached nodes that show that flag.
The code looks OK to me. There are API's to collect and merge cacheability metadata but for something that's as simple as this, doing it directly is fine I think.
Comment #9
martin107 CreditAttribution: martin107 commentedI went looking for a reason why the patch has no effect.
\Drupal\Core\Render\Renderer:doRender()
Line 308 is where some #lazy_builder integrity checks begin
Here is a code fragment from line 323, if I read it correctly our new #cache key is automatically be overridden.
Comment #10
socketwench CreditAttribution: socketwench commentedIf I'm reading this right, this issue is stuck at "needs work"?
Comment #11
martin107 CreditAttribution: martin107 commentedYep, we have a patch that has no effect.
Speaking for myself ... I am still mulling things over ...
lazy builder can become stale for all sorts of reasons, which are particular to the unique circumstance.
The canonical way of lazy builder invalidation seems to assume things which don't work as soon as you want to say I know best - this is the way you optimise for our case..
As far as I have got ... it sounds like we need to provide a good trail blazing example of why and how core need to become more flexible.
Sorry if it sound like a council of despair ... I am sure we will get there :)
Comment #12
edurenye CreditAttribution: edurenye at MD Systems GmbH commentedI added a test for this.
Comment #15
edurenye CreditAttribution: edurenye at MD Systems GmbH commentedFixed moving out the lazy_builder.
Moved before checks so it will work also for disabling flag, so I removed the cache resets and added disable test also.
Comment #16
joachim CreditAttribution: joachim commentedCan you compare with the test that #2617384: missing test coverage for flag deletion adds please?
Comment #17
Berdir#2617384: missing test coverage for flag deletion has been committed. But I'm not sure we should merge with that. That test is in AdminUI, it's about testing the UI. This is specifically to test render cache invalidation when editing/deleting a flag.
That said, looking through the tests, enable/disable is apparently already tested in FlagEnableDisableTest, so I think we don't need that. Given that, if only delete is remaining, then merging it into that test would work for me.
@joachim: ?
Comment #18
BerdirNote that we moved it before we check if it's enabled and its setting and have it outside of the lazy builder. It has by design a different effect in there and doesn't bubble up.
We went through all resetCache() calls and removed all that we could. You might want to move some into separate issues.
This one is pointless since we switched to a lazy builder. invalidating the entity cache after flagging doesn't help us in any way and is unneeded.
this is duplicated anyway by the code in pre (or post) Save and double so by the new cache tag that we add to the output.
I think it would be nice to remove the one in preSave() too but that would require that we also add a list cache tag to the view alter. Which would be a small overhead since we'd invalidate everything unless we define a per-entity type list cache tag for flags that we invalidate. Also, other ways of displaying the flag would need that cache tag too.
This was never needed, config entities have no static cache. Also a wrong comment above this.
deleting the user we're currently logged in with confuses the test system. I still thinks that we are logged in as that (and has no way of knowing otherwise). on the next drupalLogin(), it then tries to log out first, which gives an access denied.
Comment #19
edurenye CreditAttribution: edurenye at MD Systems GmbH commentedMerged the tests and fixed the comment of the point 4.
Comment #20
edurenye CreditAttribution: edurenye at MD Systems GmbH commentedWrong name.
Comment #24
BerdirI don't think we need to flag. Just update the test to look for Flag this item instead.
you can remove those.
Comment #25
edurenye CreditAttribution: edurenye at MD Systems GmbH commentedDone.
Comment #26
BerdirNow you deleted too much IMHO. We want to have a positive test, and make sure that we look for a specific string (like Flag this item) before deleting and then the same after deleting.
assertNo* assertions are always problematic. E.g. the string you're not expecting could change and then you wouldn't assert anything anymore.
Comment #27
edurenye CreditAttribution: edurenye at MD Systems GmbH commentedOk, I readded the positive test and a test that actually fails.
Comment #29
BerdirI think this is good to go then...
Comment #31
socketwench CreditAttribution: socketwench at FFW commentedThanks everyone!
Comment #32
socketwench CreditAttribution: socketwench at FFW commented