Updated: Comment #N

Problem/Motivation

Resaving an image style triggers a full rebuilding of block plugins.
Because this includes derivatives, it means loading every View and initializing all of its displays.

The equivalent call was in D7, but there we were using cache_block for the rendered output of each block.
Now in D8, we're using render caching (see _block_get_renderable_region), so we don't need this call.

Proposed resolution

Remaining tasks

User interface changes

API changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett’s picture

Status: Active » Needs review
Issue tags: +Quick fix
FileSize
588 bytes

Eh, may have exaggerated on the major, but this is just so simple and silly.

longwave’s picture

Status: Needs review » Reviewed & tested by the community

Makes sense.

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -Quick fix

Was trying to think why we'd ever need this in the first place because it wasn't immediately obvious. And why it wasn't just using cache_clear_all() too, then we'd have seen the problem sooner.

The only situation that cache clear is valid I can think of is the following:

1. The final dimensions of the image derivatives change as a result of the update.

2. Since we specify width/height in markup, those would be wrong in cached HTML.

If that's correct, then rather than trying to clear the cache directly, we should be adding cache tags when an image derivative gets rendered, so that affected HTML gets invalidated. That will then work for both the page and block caches as well as anything else.

tim.plunkett’s picture

The block cache bin has nothing to do with content or rendering or anything like that.

It is *only* the cache of block plugins. It probably doesn't need its own bin...

But blocks were switched from caching their own output to using render cache a while back.

catch’s picture

I understand that, but the underlying bug here is that image styles have been trying to clear the block cache because of the content, not the plugins, and never got updated to clearing the render cache.

This would have been caught if it'd been a cache_clear_all() call, or if there was test coverage for outdated image style dimensions in cached HTML from blocks, but yeah neither of those...

To fix the plugin performance issue, we could fix this by changing the code here to clear the 'content' cache tag, then open a critical follow-up as a sub-issue of #2124957: Replace 'content' cache tag with 'rendered' and use it sparingly to add image style cache tags instead of using that.

And agreed this should be in cache_bootstrap or cache, not cache_block.

longwave’s picture

Status: Needs work » Needs review
FileSize
862 bytes

#1 was already accidentally committed in http://drupalcode.org/project/drupal.git/commitdiff/af0f562

The attached patch clears the 'content' cache tag as suggested in #5.

catch’s picture

Status: Needs review » Reviewed & tested by the community

OK that works, I left a note on the other issue. Also whoopsie on the commit.

Wim Leers’s picture

Status: Reviewed & tested by the community » Needs work
Related issues: +#2124957: Replace 'content' cache tag with 'rendered' and use it sparingly

Why would we add a new content cache tag? Our goal is to remove it! See #2124957: Replace 'content' cache tag with 'rendered' and use it sparingly.

Why not just add proper cache tags to Image Styles and add the necessary test coverage?

catch’s picture

Title: Saving an ImageStyle clears all block plugin caches » Image styles do not add correct cache tags to content
Priority: Major » Critical

Yeah we could also just do that here.

I wanted to let a quick fix in to stop the block plugin rebuilding, but inadvertently did that already.

Wim Leers’s picture

Status: Needs work » Postponed
Issue tags: +D8 cacheability, +Performance

Great! :)

This should wait for #2188565: Test coverage for Comment, Custom Block, Node, Taxonomy Term and User entity cache tags, at least in terms of test coverage, because that issue provides a handy base class.

Wim Leers’s picture

Wim Leers’s picture

Title: Image styles do not add correct cache tags to content » Image styles do not add correct cache tags, nor do they invalidate cache tags upon flushing
Assigned: Unassigned » Wim Leers
Issue tags: +Spark, +sprint
Related issues: +#2217749: Entity base class should provide standardized cache tags and built-in invalidation
FileSize
6.18 KB

To solve the general problem, I've opened #2217749: Entity base class should provide standardized cache tags and built-in invalidation.

There still is a part of the solution that is specific to this issue though, so I've kept this. This is now blocked on #2217749: Entity base class should provide standardized cache tags and built-in invalidation.

Wim Leers’s picture

Title: Image styles do not add correct cache tags, nor do they invalidate cache tags upon flushing » (Responsive) Image styles do not add correct cache tags, nor do they invalidate cache tags upon flushing
Status: Postponed » Needs review
FileSize
11.54 KB
7.58 KB

#2217749: Entity base class should provide standardized cache tags and built-in invalidation landed, now we can continue here!

Here is a (cleaner) reroll of #12. I've expanded the scope to also handle the Responsive Image Formatter, since it's so similar (and because otherwise ImageStyle::flush() still wouldn't have the desired effect, so it's actually within scope on second thought).

Berdir’s picture

As commented in the views bubble up issue, this is one of the cases where I'm wondering if it's really a good idea to add 3,4, 10+ cache tags to pages just because you change them twice a year? And when you do, then it will be as part of a config sync on any serious sites, which does a full cache clear anyway.

It really might not be a stupid idea to retain the content cache tag and use it for extremely rare cases like this because it's going to be much more efficient in the end to do a huge cache deletion once a month or possibly even once every few days instead of having to check 10+ additional cache tags on every single page request...

Wim Leers’s picture

#14: I thought the idea was to first add cache tags to everything and then see which ones make sense to remove and/or combine for performance reasons. "Premature optimization is the root of all evil" and all that.

Berdir’s picture

Ok fine (although one could argue how premature this is :)), but then I think we should have a critical task or something to profile cache tags usage, possibly tag with revisit before release or something if we don't have that already anyway?

catch’s picture

Should config sync do a full cache clear if we have cache tags working properly? Not sure it has to except for module enable/disable.

Agreed on a critical task for rationalising the tags once we have them. I think it's better to use them consistently then optimise them away afterwards. In the back of my mind I'm thinking about implementations that can do direct deletion and then won't have a runtime hit fetching the tags at all, but since both memcache and db backend will have the performance hit we can't rely on that.

Wim Leers’s picture

#16: we already have that, see #2241377: [meta] Profile/rationalise cache tags :)

So, with that aspect out of the way, can I has RTBC? :) (The patch still applies cleanly.)

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

I reviewed the code and it looks like a solid improvement.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.x, we might rip this out again when we do the cache tag audit but this unblocks other stuff.

  • Commit 677b9fc on 8.x by catch:
    Issue #2204159 by Wim Leers, longwave, tim.plunkett: (Responsive) Image...
tstoeckler’s picture

+++ b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldFormatter/ImageFormatter.php
@@ -113,6 +113,14 @@ public function viewElements(FieldItemListInterface $items) {
+    // Collect cache tags to be added for each item in the field.
+    $cache_tags = array();
...
+      $cache_tags = $image_style->getCacheTag();

It seems this should be

$cache_tags[] = $image_style->getCacheTag();

right? That would also be consistent with how it is used below.

Wim Leers’s picture

Issue tags: -sprint

#22: No, because Entity::getCacheTag() looks like this:

  public function getCacheTag() {
    return array($this->entityTypeId => array($this->id()));
  }

i.e. it already returns an array.

In other words, the name is a bit misleading. But I think that's a fine cost to pay, because it allows us to things like:

$cache_tags = NestedArray::mergeDeep(
  $entity->getCacheTag(),
  $entity->getListCacheTags(),
  $other_entity->getCacheTag(),
  array('and_something_custom' => TRUE)
);

i.e. it makes it super trivial to gather cache tags from all sorts of places and merge them together in a single cache tags array.

If you disagree with this and would like to change it, please open a new issue :)

tstoeckler’s picture

+++ b/core/modules/responsive_image/lib/Drupal/responsive_image/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php
@@ -168,6 +169,23 @@ public function viewElements(FieldItemListInterface $items) {
+    $all_cache_tags = array();
...
+      $all_cache_tags[] = $responsive_image_mapping->getCacheTag();
...
+          $all_cache_tags[] = $image_style->getCacheTag();
...
+    $cache_tags = NestedArray::mergeDeepArray($all_cache_tags);

Ahh... thanks very much for the explanation. The [] part of the code above threw me off and lead me to believe it was just a single tag. I missed the NestedArray::mergeDeepArray().

I do think the method name should be changed but that is definitely not part of this issue.

Thanks very much and sorry for the noise!

Wim Leers’s picture

You're very welcome and no worries! :)

Status: Fixed » Closed (fixed)

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