Problem/Motivation
After upgrading from 1.0-rc5 to 1.2, I've noticed that cache tags are incorrect when viewing the group and group content pages.
These are the cache tags when viewing a group content on 1.0-rc5:
X-Drupal-Cache-Tags: block_view config:block.block.seven_breadcrumbs config:block.block.seven_content config:block.block.seven_local_actions config:block.block.seven_login config:block.block.seven_messages config:block.block.seven_page_title config:block.block.seven_primary_local_tasks config:block.block.seven_secondary_local_tasks config:block.block.seven_sitebranding config:block_list config:filter.format.basic_html config:image.style.medium config:shortcut.set.default config:shortcut_set_list config:system.menu.admin config:user.role.administrator config:user.role.authenticated group:6 group_content:4156 group_content:4161 group_content:4180 group_content:4279 group_content:4376 group_content_list:entity:2394 group_content_list:entity:2753 group_content_list:entity:2772 group_content_list:entity:2933 group_content_list:entity:330 group_content_list:group:6 group_content_list:plugin:group_node:employer group_content_list:plugin:group_node:employer:entity:2394 group_content_list:plugin:group_node:employer:entity:2753 group_content_list:plugin:group_node:employer:entity:2772 group_content_list:plugin:group_node:employer:entity:2933 group_content_list:plugin:group_node:employer:entity:330 group_content_list:plugin:group_node:employer:group:6 group_content_view http_response local_task media:9080 media:9081 node:2753 node_view page_manager_route_name:entity.group_content.canonical rendered taxonomy_term:9949 user:1 user:8 user_view
These are the cache tags when viewing the same group content on 1.2:
X-Drupal-Cache-Tags: block_view config:block.block.seven_breadcrumbs config:block.block.seven_content config:block.block.seven_local_actions config:block.block.seven_login config:block.block.seven_messages config:block.block.seven_page_title config:block.block.seven_primary_local_tasks config:block.block.seven_secondary_local_tasks config:block.block.seven_sitebranding config:block_list config:devel.toolbar.settings config:filter.format.basic_html config:group.role.directory_website-a416e6833 config:group.role.directory_website-admin config:group.role.directory_website-member config:group.role.directory_website-outsider config:group_type_list config:image.style.medium config:shortcut.set.default config:shortcut_set_list config:system.menu.admin config:system.menu.devel config:user.role.administrator config:user.role.authenticated group:6 group_content:3130 group_content:4156 group_content:4161 group_content:4180 group_content:4376 group_content:610788 group_content:610789 group_content:611093 group_content:611103 group_content:611137 group_content_list:entity:1 group_content_list:entity:2394 group_content_list:entity:2753 group_content_list:entity:2772 group_content_list:entity:2933 group_content_list:group:1 group_content_list:group:119 group_content_list:group:120 group_content_list:group:121 group_content_list:group:122 group_content_list:group:123 group_content_list:group:6 group_content_list:plugin:group_membership group_content_list:plugin:group_membership:entity:1 group_content_list:plugin:group_membership:group:1 group_content_list:plugin:group_membership:group:119 group_content_list:plugin:group_membership:group:120 group_content_list:plugin:group_membership:group:121 group_content_list:plugin:group_membership:group:122 group_content_list:plugin:group_membership:group:123 group_content_list:plugin:group_node:article group_content_list:plugin:group_node:author group_content_list:plugin:group_node:campus group_content_list:plugin:group_node:career_opportunity group_content_list:plugin:group_node:course group_content_list:plugin:group_node:employer group_content_list:plugin:group_node:employer:entity:2394 group_content_list:plugin:group_node:employer:entity:2753 group_content_list:plugin:group_node:employer:entity:2772 group_content_list:plugin:group_node:employer:entity:2933 group_content_list:plugin:group_node:employer:group:6 group_content_list:plugin:group_node:event group_content_list:plugin:group_node:institution group_content_list:plugin:group_node:landing_page group_content_list:plugin:group_node:scholarship group_content_list:plugin:group_node:story group_content_list:plugin:group_node:third_party_page group_content_list:plugin:group_node:video group_content_list:plugin:group_node:virtual_experience group_content_view group_permissions http_response local_task media:9080 media:9081 node:2753 node_view page_manager_route_name:entity.group_content.canonical rendered taxonomy_term:9949 user:1 user:8 user_view
It seems that there are many tags that are not related to this page. I'm viewing an employer group content on group 6...
- Why are group_content_list:plugin:group_node: institution, scholarship, courses, etc. needed?
- Why are group_membership:group: 1, 120, 122, etc needed?
After some debugging I've noticed some potential functions that can be related to the issue above:
1. group.module - group_entity_access
2. src/QueryAccess/EntityQueryAlter.php - doAlter
Why is it necessary to query ALL plugins and add them to the cache tags?
| Comment | File | Size | Author |
|---|---|---|---|
| #14 | group-only_installed_plugins-3166252-14.patch | 2.88 KB | jonnyeom |
| #10 | cachetags_1.2_vs_1.2patch_vs_1.0-rc5.png | 238.55 KB | carolpettirossi |
| #8 | cachetags_1.2_vs1.0-rc5.png | 194.63 KB | carolpettirossi |
Issue fork group-3166252
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
carolpettirossi commentedComment #3
carolpettirossi commentedComment #4
kristiaanvandeneyndeYour permissions are calculated based on your memberships (and other factors). So if your membership changes, it might change your permissions, rendering the cached page stale. This is why we add all of your memberships' cache tags.
Furthermore, any plugin that defines entity access over nodes could be used to group the node the group content is for. When that happens, we invalidate the group_content_list:plugin:group_node:NODE_TYPE cache tag, which therefore must be added so that any new group content for the node is taken into account when calculating permissions.
This is currently not bundle-specific and could be optimized to only find those plugins that deal with the entire entity type (node) or specifically cares about a bundle (node type) as all group_node plugins do. The reason we add them all right now is because parts of this code are copied from the query access logic and there we can't know what bundle we're dealing with.
So the only actionable item I can see right now is to reduce the amount of list cache tags, but that would require some more refactoring on
$plugin_manager->getPluginIdsByEntityTypeAccess($entity->getEntityTypeId());so that we can only retrieve plugins for a given bundle.Closing as a support request, but feel free to open a feature request that only focuses on that part.
Comment #5
kristiaanvandeneyndeComment #6
kyuubi commentedHi @kristiaanvandeneynde,
Apologies for reopening, but it would be useful to conclude the discussion on this issue before opening others and loosing context.
This makes complete sense, however it doesn't explain why I need membership cache tags for all memberships in all groups. If I belong to 100 groups, I don't see what the justification to add 100 membership cache tags for a piece of content from Group 1. Changing memberships on Group 1 will have cache implications yes, but not the other 99.
Yes this is exactly what I was thinking when looking at that method, would you be willing to accept a patch here? The way it is now means any change on any given content will invalidate all lists across all groups and content types, which will have huge implications on caching ratio.
Let me know what you think!
Comment #7
kristiaanvandeneyndeThat's true, if we are adding totally unrelated memberships, that might be a problem.
But I think it's because we are adding the user.group_permissions cache context to something that might also have the user cache context. In that case, the entire set of calculated permissions is added as a dependency, adding all of your membership's cache tags because user.group_permissions is being optimized away.
See: https://www.drupal.org/docs/8/modules/group/turning-off-caching-when-it-...
As to your second point: I already agree to the idea of finetuning the method to filter plugins on bundles. I just don't have this at the top of my list right now as people are experiencing issues with permissions.
Comment #8
carolpettirossi commentedHi @kristiaanvandeneynde,
Thanks for taking the time to understand the issue and guide us on how could we potentially fix it. However, I couldn't get successful progress with your initial suggestions.
1. Turn off user.group_permissions cache
I've tried disabling user.group_permissions cache context as per your "how-to" on the link above, but it has not affected Drupal Cache Tags when I'm viewing a Group Content logged in as an authenticated user.
This is what I have on my services.yml:
I checked and development.services.yml is not overriding it.
2. getPluginIdsByEntityTypeAccess refactor
In regards to the refactoring on
getPluginIdsByEntityTypeAccessmethod:As I mentioned in this issue summary this method is being used in 2 places:
1. group.module - group_entity_access
2. src/QueryAccess/EntityQueryAlter.php - doAlter
After debugging a bit more in order to implement your suggestion to "retrieve plugins for a given bundle", I've noticed that this is not possible on EntityQueryAlter call (unless I'm missing something). We don't have the entity in EntityQueryAlter context. All we have there is the entity type and the query.
Can you give us further guidance on how could we fix the cache tags here?
3. Explaining the issue for others and summarizing the problem focusing on group cache tags
I'm adding below a a diff between the group module version. 1.2 on the left and 1.0-rc5 on the right just to make it easier for us to analyse the cache tags. For context, the user I'm logged in is member of Groups: 2 and 6
As you can see in the diff above these are the additional cache tags:
group_content:3779
group_content:378178
group_content_list:entity:158
group_content_list:group:2
group_content_list:plugin:group_membership
group_content_list:plugin:group_membership:entity:158
group_content_list:plugin:group_membership:group:2
group_content_list:plugin:group_membership:group:6
group_content_list:plugin:group_node:article
group_content_list:plugin:group_node:author
group_content_list:plugin:group_node:campus
group_content_list:plugin:group_node:career_opportunity
group_content_list:plugin:group_node:course
group_content_list:plugin:group_node:event
group_content_list:plugin:group_node:institution
group_content_list:plugin:group_node:landing_page
group_content_list:plugin:group_node:scholarship
group_content_list:plugin:group_node:story
group_content_list:plugin:group_node:third_party_page
group_content_list:plugin:group_node:video
group_content_list:plugin:group_node:virtual_experience
group_permissions
Cache tags explained:
- 3779 is the group content for carolpettirossi user on group 2
- 378178 is the group content for carolpettirossi user on group 6
- 158 is carolpettirossi uid
- group_node:* represents all installed and uninstalled group node plugins
Expected behavior:
- 3779 cache tags shouldn't be added cause I'm viewing this group content on group 6
- group 2 cache tags shouldn't be added
- Only group_content_list:plugin:group_node:employer makes sense in this context. The group content being viewed is an employer one.
user.group_permissionscaching, the group_permissions and group_content_list:plugin:group_membership continues on the list.Comment #9
kristiaanvandeneyndeThanks for the detailed report!
With regard to your findings as to the refactor, I already indicated that it is "copied from the query access logic and there we can't know what bundle we're dealing with".
But focusing on the problem at hand, I really am starting to think we are seeing the user.permissions cache context being optimized away in favor of the user cache context. When that happens, all of the cacheable metadata of your calculated permissions get added to the response.
The reason I'm pretty sure about this is because we are seeing the 'group_permissions' cache tag, which is only added in ChainGroupPermissionCalculator. And believe it or not, most of what you're seeing in the list above might be added because it indeed affects your permissions. A reason why group 2 might be added could be that you're using Subgroup (or ggroup).
Having said that, there is currently a bug (with a fix) where we are adding more group_content_list:x:y cache tags than necessary under certain circumstances. Please see #3110773: List cache tags in getCacheTagsToInvalidate() result in bad cacheability and report back after having tried out that patch.
Comment #10
carolpettirossi commented@kristiaanvandeneynde,
Just tested the patch and here is the result:
As you can see on the diff above,
group_content_list:entity:*have been removed from group content page view and this is probably correct.However, there are still some cache tags being added to the page that doesn't make sense to me:
- group_content_list:plugin:group_node:*
- group_content:3779
We are actually not using subgroup (ggroup). So, I'm not sure why
group_content:3779(3779 is the group content for carolpettirossi user on group 2) is being added to a group content that belongs to group 6.When I add a breakpoint to
return $calculated_permissions;indoCacheableCalculationI see this list (after applying the patch you mentioned):So, the calculator does add cache tag related to group 2 (group_content:3779) to the list. I think this is not correct, right? Since I'm viewing content on group 6.
Do you have any suggestion/path that we could move to solve this part (
group_content_list:plugin:group_node:*cache tags) ?Comment #11
kristiaanvandeneyndeThis is actually doing things correctly. Your permissions are only calculated once so they need to contain all of your group permissions. So if you are a member of Group A, B and C, it wouldn't matter which page you're on, you'll always get the permissions for all 3 groups from the calculated permissions cache.
The problem is that these cache tags are leaking into the headers, probably when the user.group_permissions cache context is optimized away in favor of the user cache context. If you turn off caching for user.group_permissions, the DynamicPageCacheSubscriber will no longer cache the page (because of auto_placeholder_conditions), so that might get rid of the extra cache tags.
In any case, the goal now is to figure out where user.group_permissions is being optimized away. Setting breakpoints in the CacheContextsManager::optimizeTokens() might help a great deal here.
The list cache tags we can solve for individual access checks, but not for query access checks. So it's quite low on my priority list as we'd still have to deal with these cache tags on pages that show entity lists. (We'd be spending time fixing something that cannot be fixed everywhere.)
Comment #12
carolpettirossi commentedThe cache tags I'm reporting in previous comments are being added to the headers even with the
user.group_permissionscache context disabled. I've added it to renderer.config > auto_placeholder_conditions > contexts after you suggested and haven't reverted this changed since then.I just want to confirm what we are investigating here is: Checking why a group 2 cache tags (group_content:3779) is being added to the header when I'm viewing group content that belongs to group 6. Correct?
I set a condition breakpoint on return $optimized_content_tokens; where $context_id = "user.group_permissions" and when viewing a group content page, it never hits this breakpoint.
I know that this is quite low priority for you, just wanted to check if you have any idea on how to fix and then I can put some effort providing a patch. Maybe you can show me the ropes without actually implementing it? :)
Comment #13
kristiaanvandeneyndeNot really, as I'm 99% sure it's being added because 'user.group_permissions' is somehow being optimized away into 'user'. The thing we want to find is where that optimization happens.
Either that, or something else is adding your calculated permissions as a dependency.
Hmm, that contradicts what I wrote above :/ Maybe you're hitting a cached response?
Not off the top of my head, sorry :( Fixing this for query access will be nearly impossible without adding all of the plugin tags.
Comment #14
jonnyeom commentedHere is a patch that simply filters out plugins that are not installed.
What this does
.
* Any plugins that are not enabled will not litter our cache-tag lists.
What this doesn't do
* This doesn't check the bundle type of the $entity in hook_entity_access. I think we could apply this logic for bundle filtering in hook_entity_access, after we retrieve the list of plugin ids.
@carolpettirossi, @kristiaanvandeneynde,
In terms of this issue itself, I agree that we should eventually check the bundle type for any entities we check create cache tags against.
As mentioned before, this should happen in 2 places,
group_entity_access, I think the logic can stay in this function.
EntityQueryAlter, Because of the complexity of checking the logic in Conditions, perhaps it would be better to do it not in a query alter but a in the
hook_views_post_render()?(We would still keep the access check logic in the EntityQueryAlter, Just pull the caching related concerns out of this Class).
thoughts?
Comment #15
kristiaanvandeneyndeIf you read this piece of code you will see why we can't only add enabled plugins:
If we do so, that large block of text becomes invalid. So we could try to only add installed plugins, but then we'd have to add the group_content_type list cache tag saying: "We only checked for installed plugins before, so when a new GCT is created, the cached results based on those checks might no loner be valid".
Although ideally we do not clear the cache when a GCT is created for an unrelated entity type. So we'd need to introduce a GCT list cache tag per target entity type and clear that one when a new one is created.
Comment #16
catchI think this might be a duplicate of #3538509: Try to remove group_relationship list cache tags in group_entity_access() at this point.
Comment #17
kristiaanvandeneyndeIndeed it would seem so.