Follow-up to #2511516: Make local tasks and actions hooks provide cacheability metadata, to make the blocks showing them cacheable

Problem/Motivation

When we enable content translation for an item (node, menu, block etc from admin/config/regional/content-language), the local task tab isn't getting displayed until clear cache.

Proposed resolution

Clear the render cache when we submit the form that changes the translation settings for content so any user with permissions will see the updated tabs with or without the "translate" link.

Comments

vijaycs85 created an issue. See original summary.

vijaycs85’s picture

Title: content translation translation local tasks are not getting displayed » content translation local tasks are not getting displayed
berdir’s picture

I really think we should just clear the render cache when a new module is installed and remove block_install().

swentel’s picture

Status: Active » Needs review
StatusFileSize
new6.18 KB

Clearing at module install sounds logical to me as well.

Status: Needs review » Needs work

The last submitted patch, 4: 2572125-4.patch, failed testing.

berdir’s picture

You probably want the same logic and clear the tag.

I'm guessing what you're running into in that test fail is smart cache which afaik uses a separate bin.

vijaycs85’s picture

sorry, I wasn't very clear explaining the issue summary. The problem isn't when you enable content_translation module. It's when you try to enable translation for different components at admin/config/regional/content-language

The last submitted patch, 4: 2572125-4.patch, failed testing.

rodrigoaguilera’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests
StatusFileSize
new1.97 KB

I don't understand why we are deleting that hook_install.

Here is a patch with the same logic but for the form we are interested.

andypost’s picture

Is this still repeatable?

rodrigoaguilera’s picture

Yes, as of yesterday HEAD I was able to reproduce the bug.

I will try to write the test at the drupalcon sprint.

rodrigoaguilera’s picture

Issue summary: View changes
Issue tags: -Needs tests, -Needs issue summary update
StatusFileSize
new2.7 KB

Here is the patch with the failing test.

Status: Needs review » Needs work

The last submitted patch, 12: 2572125-9-test-only.patch, failed testing.

The last submitted patch, 12: 2572125-9-test-only.patch, failed testing.

rodrigoaguilera’s picture

StatusFileSize
new4.68 KB

The test only patch had a wrong file numbering.

Here is the complete patch.

rodrigoaguilera’s picture

Status: Needs work » Needs review
wim leers’s picture

Status: Needs review » Needs work
Issue tags: +DrupalCon Barcelona 2015

The latest patch has lost the hunk that removes block.install. See earlier patche.s

+++ b/core/modules/language/src/Form/ContentLanguageSettingsForm.php
@@ -41,7 +52,8 @@ public function __construct(EntityManagerInterface $entity_manager) {
+      $container->get('cache.render')

@@ -156,6 +168,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Invalidate the render cache because we need to show new actions in blocks
+    // with tabs. eg. "Translate".
+    $this->renderCache->deleteAll();

This is wrong. This only invalidates the default render cache bin. Any cache bin can be used for render caching.

You must instead invalidate the 'rendered' cache tag.

rodrigoaguilera’s picture

Status: Needs work » Needs review
StatusFileSize
new3.58 KB
new2.02 KB

I understood that the hook_install was out of scope for this issue. I don't know how it fits the proposed resolution because the cache stall doesn't happen on any module install but when changing translation settings.

Here is the patch that invalidates the tag.

wim leers’s picture

Status: Needs review » Needs work
+++ b/core/modules/language/src/Form/ContentLanguageSettingsForm.php
@@ -170,7 +159,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
+    Cache::invalidateTags(['rendered']);

Let's inject the cache_tags.invalidator service instead.

rodrigoaguilera’s picture

Status: Needs work » Needs review
StatusFileSize
new4.8 KB
new2.28 KB

Thank you for the reviews :)

I thought it didn't mattered but I guess I have to learn about the advantages of injecting.

New patch attached

rodrigoaguilera’s picture

StatusFileSize
new4.8 KB
new594 bytes

small mistake

The last submitted patch, 20: 2572125-20.patch, failed testing.

The last submitted patch, 20: 2572125-20.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 21: 2572125-21.patch, failed testing.

rodrigoaguilera queued 21: 2572125-21.patch for re-testing.

wim leers’s picture

  1. +++ b/core/modules/content_translation/src/Tests/ContentTranslationOperationsTest.php
    @@ -103,9 +106,38 @@ function testOperationTranslateLink() {
    +    // Ensure that when I disable translation for a content type I don't
    +    // see a translate link anymore in the actions block.
    

    We don't use "I" in comments. Prefer to use "we", or even better, neither.

    e.g. Ensure the 'Translate' local task does not show up anymore when disabling translations for a content type.

  2. +++ b/core/modules/content_translation/src/Tests/ContentTranslationOperationsTest.php
    @@ -103,9 +106,38 @@ function testOperationTranslateLink() {
    +    // Warm cache.
    +    $this->drupalGet('node/' . $node->id());
    +    // Check that we can see the translate link.
    +    $this->assertLinkByHref('node/' . $node->id() . '/translations');
    +    // Make article not translatable.
    +    $this->drupalPostForm('admin/config/regional/content-language', ['settings[node][article][translatable]' => FALSE], t('Save configuration'));
    +    // Go back to the node.
    +    $this->drupalGet('node/' . $node->id());
    +    // Check that there is no link to translate anymore.
    +    $this->assertNoLinkByHref('node/' . $node->id() . '/translations');
    

    One comment per statement is excessive. Please group them logically.

  3. +++ b/core/modules/content_translation/src/Tests/ContentTranslationOperationsTest.php
    @@ -103,9 +106,38 @@ function testOperationTranslateLink() {
    +   * Test the access to the overview page for translations.
    

    Method names must use 3rd singular person, so "Tests", not "Test".

  4. +++ b/core/modules/language/src/Form/ContentLanguageSettingsForm.php
    @@ -156,6 +168,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    +    // Invalidate the rendered tag cache because we need to show new actions in
    +    // blocks with tabs. eg. "Translate".
    

    s/rendered/'rendered'/

    "new actions in blocks with tabs" does not make sense.

    "eg." is not a valid abbreviation.

rodrigoaguilera’s picture

Status: Needs work » Needs review
StatusFileSize
new4.45 KB
new2.84 KB

Added corrections.

pwolanin’s picture

looks possibly related to: #2624594: Local action plugins do not have any way to provide cacheability metadata

Clearing the whole render cache here seems rather like overkill? we don't have a cache tag that generically covers all local tasks?

pwolanin’s picture

Status: Needs review » Needs work

Looks like the local task plugin should have a custom class and expose a cache tag according to what entity and bundle it relates to?

Unfortunately, the local task plugin is associated with a route that applies to the canonical entity view, but the config cache tags are like:
language.content_settings.node.page

so specific to a bundle (e.g. node type). So the local tasks should emit cache tags for all bundles of the entity type, which means they also probably need to include the entity_bundles cache tag? Or does adding or removing a bundle do a rather blanket wipe on caches?

gábor hojtsy’s picture

Title: content translation local tasks are not getting displayed » Content translation local tasks are not getting displayed due to caching
swentel’s picture

Or does adding or removing a bundle do a rather blanket wipe on caches?

I think adding or removing a bundle wipes caches. I know it happens for say changing the manage display or form display. Those aren't daily actions, so I think wiping the render cache here is fine since you won't be changing those content language settings often.

tstoeckler’s picture

Re #29: Do you want a dedicated cache tag because you think clearing the rendered clears too much that wouldn't actually have to be cleared? If that's the case I disagree because the language configuration is mostly a one-time operation for a site so I don't think we should optimize for that.

In general we should never invalidate caches in a form submission, because that means importing config or drush config-set doesn't get the same treatment. Instead we should override EntityInterface::getCacheTagsToInvalidate() in ContentLanguageSettings to return the rendered cache tag. The DateFormat config entity does this, which similarly is one of those things you set up once and then mostly never touch, so I think that's a pattern we can re-use. Looking at DateFormat::getCacheTagsToInvalidate() I see that it actually just returns ['rendered'] without delegating to the parent method for the generic config:date_format... cache tag. I'm not sure if that is intentional or an oversight (perhaps even a bug?). Looking further DateFormat also has list_cache_tags = { "rendered" } in its annotation. Not sure whether we need that for ContentLanguageSettings as well. But in general I think that would be a better approach.

berdir’s picture

That's by design for date formats, we want to avoid adding lots of date format cache tags when we don't need them for invalidation.

tstoeckler’s picture

Ahh thanks, that makes sense. So I guess since for content language settings that's not the case (since they generally will not be part of any output, at least I guess so) it would be best practice to call the parent in cacheTagsToInvalidate()?

berdir’s picture

I think so, yes.

What's a bit different for them is that we usually save a lot of them on the overview page, possibly dozens. So we'd invalidate the rendered cache tag dozens of times. But we have optimizations in place that will ignore any additional invalidation of the same cache tag in a single request, so that should be OK.

vijaycs85’s picture

Status: Needs work » Needs review
StatusFileSize
new2.94 KB
new2.65 KB

Thanks @tstoeckler and @Berdir. Updated as per #32to #35.

Status: Needs review » Needs work

The last submitted patch, 36: 2572125-36.patch, failed testing.

vijaycs85’s picture

Status: Needs work » Needs review
StatusFileSize
new2.88 KB
new798 bytes

Looks like we just need list_cache_tags. getCacheTagsToInvalidate has no effect, because postSave() => invalidateTagsOnSave() => getEntityType()->getListCacheTags() which just returns this->list_cache_tags;

Also, the patch is green on both 8.0.x and 8.1.x

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Ahh, I had looked at Entity::invalidateTagsOnSave() and had missed the override of ConfigEntityBase::invalidateTagsOnSave(). It does seem a little suspect that getCacheTagsToInvalidate() is ignored for config entities, but that's not introduced here, so I think this is good to go. Thanks!

swentel’s picture

Related issue with config translation, also probably misses a cache tag somewhere.

  • catch committed c5ce9ce on 8.2.x
    Issue #2572125 by rodrigoaguilera, vijaycs85, swentel, Berdir, Wim Leers...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to all three 8.x branches, thanks!

  • catch committed 0de4c96 on 8.0.x
    Issue #2572125 by rodrigoaguilera, vijaycs85, swentel, Berdir, Wim Leers...
gábor hojtsy’s picture

Issue tags: -sprint

Thanks, this was a tough one for users, good to see it fixed!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

quietone’s picture