Problem/Motivation
When I set a certain block to be cached for up to e.g. 15 minutes, then I expect that the containing page also emits a corresponding header. And I also expect Drupal's page cache to honor this.
Examples:
- A block with a weather forecast summary. The data constantly changes so e.g. cache for maximum 15 minutes.
- A View with a date filter relative to current time, such as "Upcoming Events", showing say 3 soonest future events. The block can be cached for maximum until the time/day of first event, then it must be refreshed to exclude the event now in the past.
Proposed resolution
TBD
Workaround
Install contrib module Cache Control Override. However this is not perfect, and if you use it you might hit the exact same problems that are making this issue be postponed (see #113).
- Other system blocks such as forms, language switcher that are in fact cachable currently may emit max-age = 0 so will prevent caching after CCO is installed.
- CCO disables dynamic cache as well as static cache.
- CCO only detects max-age = 0 so it won't work if your block has a small positive max-age.
Also note that in the case of the second example above, Views will not automatically emit the correct max-age based on the presence of a date filter - you need to write a hook to do that.
Remaining tasks
We need to fix these issues first:
- #2578855: Form tokens are now rendered lazily, allow forms to opt in to be cacheable
- #2232375: Support CacheOptionalInterface in BlockViewBuilder, use it in language switcher block to prevent max-age 0 bubbling
- #2483181: Make book navigation block cacheable
- #2351015: URL generation does not bubble cache contexts: this one is marked as closed but
RedirectFormBlockstill contains max-age 0.
User interface changes
None.
API changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #170 | bubble-2352009-170.patch | 2.51 KB | hmdnawaz |
| #131 | 2352009-max-age-bubbling-8.9.x.patch | 2.67 KB | jackson.cooper |
| #124 | bubbling-max-age-2352009-124.patch | 969 bytes | chi |
| #82 | bubbling_of_elements_-2352009-82.patch | 9.1 KB | yogeshmpawar |
| #78 | 2352009-78.patch | 9.01 KB | rgpublic |
Issue fork drupal-2352009
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 #1
catchIn principle I think supporting this use case is OK.
In practice I'm not sure about it.
The Drupal 7 model was that block caching was really about authenticated users, and page caching was for anonymous, and these were separate things. It opens up the potential for contrib or custom modules (especially ones that don't ignore/don't understand cache tags) to break page caching in fun and unexpected ways.
Comment #2
wim leersGood points.
This is indeed more about the principle than saying "we MUST do this for 8.0.0". It's a nice-to-have. It'd be consistent with the reasoning we want developers to apply when thinking about page rendering.
Comment #3
wim leersClarifying the main use case.
Comment #4
fabianx commentedSo a clear yes from me:
This is why #cache['ttl'] or max-age is independent from #cache expires.
I think we can easily combine this with the place holder approach, but bubbling should probably be configurable.
But having that information besides the cache context is important to know if we want to render a place holder.
Comment #5
wim leers#4: Yep. But making the bubbling configurable sounds problematic/tricky/confusing to me.
FYI:
CacheableInterfacealready works with a max-age (i.e. relative time), not with an expiration time (i.e. absolute time), so we've already paved the way partially :)Comment #6
catchSo I definitely think we can make this a developer decision - always bubble up max_age. Then it's OK to file a bug report if people are doing it wrong.
Where there's a UI for setting the cache time of a block, then that should probably also include whether it should affect parent/page caching ttl too - since that's where people will get into trouble (thinking of the Views UI specifically here).
Comment #7
Anonymous (not verified) commentedwhen there are multiple blocks on a page with different max-age values, i'm assuming we want the smallest one to win? perhaps we should put that in the summary.
Comment #8
fabianx commented#7 The smallest one that we want to bubble up - yes.
Comment #9
wim leers#2443073: Add #cache[max-age] to disable caching and bubble the max-age already did this :)
Comment #10
fabianx commentedNot to the page cache though what this was originally about ;).
Comment #11
fabianx commentedComment #12
wim leersWell, it actually is bubbled to the response, but is then overwritten by FinishResponseSubscriber.
But sure, let's use this issue to fix that overwriting problem :)
Comment #13
fabianx commentedComment #14
catchAre we sure we want to override the internal page cache? I'm not completely opposed but that feels tricky to explain how it works with the max_age setting. For example render cached content is assumed to be permanent if there's no max age set, so the setting would represent a cap on that - but then if someone sets max_age to a week in #cache and the config is set to 10 minutes, which wins?
Comment #15
wim leers#14: I think that's super easy to explain actually: that setting represents the default max-age. Therefore it applies to pages that are marked as permanently cacheable. In other words:
min(bubbled max-age, configured default max-age).Comment #16
wim leersThis will be able to absorb code from #2458349-8: Route's access result's cacheability not applied to the response's cacheability and #2458349-11: Route's access result's cacheability not applied to the response's cacheability's interdiffs, once we start tackling this (now is not the right time).
Comment #17
mr.baileysComment #18
fabianx commentedCurrently the max-age is already set on the Response object via setMaxAge(), the page cache can use that setting.
Comment #19
mr.baileysComment #20
mr.baileysIf I understand correctly, what is requested in this issue is actually what we are removing in #2467041: max-age on HTML responses wrongly set to `max-age=0, private` instead of `max-age=N, public` (breaks reverse proxies and client-side caching). Until that issue lands, max-age is actually bubbled up the stack and the smallest element max-age value makes it into the cache-control header. However, since some elements are setting max-age=0, this ends up marking each page request as "max-age=0, private".
So, is this issue about:
a) re-enabling what we are removing in #2467041: max-age on HTML responses wrongly set to `max-age=0, private` instead of `max-age=N, public` (breaks reverse proxies and client-side caching) after all elements correctly set max-age <> 0
b) changing the way we handle previously set cache headers in FinishResponseSubscriber::onRespond()
In any case, it seems that this issue is postponed until we have fixed max-age being set to 0 on some elements that are currently causing the entire page to have max-age=0?
Comment #21
mr.baileysComment #22
wim leersYes, you're right. Sorry about that. We can do this as soon as #2469431: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts lands, or when all blocks that actually are cacheable, but aren't yet due to unfixed issues. Whichever is done first.
Comment #23
wim leersComment #24
yched commentedComing from #2429617-270: Make D8 2x as fast: Dynamic Page Cache: context-dependent page caching (for *all* users!).
SmartCache puts debug info in its own X-Drupal-SmartCache header, with values HIT/MISS, and UNCACHEABLE when max-age = 0.
This issue here is where we should, similarly, output UNCACHEABLE in X-Drupal-Cache when applicable ?
Comment #25
wim leersNow that we have #2476407: Use CacheableResponseInterface to determine which responses should be cached (since yesterday), this has become more possible than ever. And I don't think this is actually blocked on #2469431: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts — I suspect that was either the wrong issue ID, or that originally had a different intent.
But, I think this will have to wait until 8.1.
Comment #26
duaelfrI opened #2592555: Time based cache does not work as expected for anonymous users today that seems to be related to this issue (thanks @catch to have pointed this one to me).
What I understand here is that if I want to implement the very common use case of a piece of generated content with a time based cache (i.e. a RSS aggregation or a randomized view), anonymous users are going to always see the same thing unless I manually clear the cache.
Am I right?
Comment #27
wim leersYou're right, because the
PageCachemiddleware does this:i.e. it only respects
Expiresin HEAD, notCache-Control: max-age.Which is silly.
Comment #28
wim leersMarked #2592555: Time based cache does not work as expected for anonymous users as a duplicate of this one.
Comment #29
berdirIt is kind of silly, but you sold that to me as a feature a while ago :)
Having a separate external and internal max age can definitely be a useful feature, especially if you can't invalidate the external cache. You can set it to a short time, so that it frequently re-validates using the internal page cache.
Comment #30
catchI think we can do that with an X-Internal-Page-Cache-Max-Age header specially for that purpose. Akamai has similar with Edge-control, and Wim pointed out in irc there is s-max-age.
Comment #31
fabianx commented#30: Yes, Symfony and the FosHttpCache bundle uses s-max-age for that purpose, e.g. $response->setSharedMaxAge(600);
Comment #32
wim leers#29: I'm sure that was only jokingly then :P
The problem with
s-maxageis that it will be respected by ALL proxies. i.e. also those we cannot invalidate using cache tags.For that purpose, CDNs like Akamai and Fastly have the
Surrogate-Controlheader, which is likeCache-Controlbut specifically targeted at the infrastructure you control. That infrastructure then strips that header. But since that's already used by such external services, I think something likePage-Cache-Controlwould make more sense: it'd target Page Cache specifically.Comment #33
berdirI'm a bit confused why we talk about headers. If you are able to set a header, then you can already set the Expires header and page cache will respect that? The problem IMHO is when you can't but want it to respect whatever max-age was set by a block, statistics.module, a views or whatever.
Isn't what we want here exactly what you implemented in #2527126-143: Only send X-Drupal-Cache-Tags and -Contexts headers when developer explicitly enables them? To be clear, I'm not against doing that at all, just against doing it in that issue. That would finally allow to properly use statistics.module also for anonymous users, something that in 7.x only worked by setting the page cache expiration manually to 1h or whatever frequency you wanted to have it updated. And right now doesn't work at all.
But we need to make sure that we no longer send out max-age 0 in common cases, as that would avoid those pages from being cached. Maybe we should only respect a value != 0. It's a bit weird but would be a minimal behavior change compared to HEAD...
Comment #34
catchYou can but then Expires gets passed to everything after page cache too. Same with max-age once we make page caching respect max-age.
Comment #35
wim leers#33 I was replying to #29+#30+#31, which were talking about "different max-age for proxies".
Yes, it is!
Comment #36
fabianx commented#28: Per marking #2592555: Time based cache does not work as expected for anonymous users as a duplicate of this, that makes this essentially a 8.0.x bug.
If we don't want this, we need to split this out again.
Comment #37
aNickPlx commentedMarking #2578627: Drupal Cache for Anonymous users does not respect render array's #cache['max-age'] as a duplicate of this.
Comment #39
wim leersComment #40
berdirIMHO, before considering this, I think we need to introduce a concept to say that e.g. a block is not worth caching. Right now, you can achieve that by returning max-age 0, but it's not the same. As the result would be that page cache would be disabled.
For example something like \Drupal\system\Plugin\Block\SystemPoweredByBlock, it's totally pointless that cache a hardcoded #markup string , just calling it will always be faster since we have to instantiate the block anyway. But you still want it to be cached in the page cache.
Also, at this point this would IMHO be an API change?
Comment #41
fabianx commented#40: I think you can now do this within system module via https://api.drupal.org/api/drupal/core%21modules%21block%21block.api.php....
Just unsetting the #cache 'keys' is enough.
I still would love to have a $block->alterBuild() call to do the same as the hook, but Wim was against it so maybe in 8.9.x ;).
So not worth caching is taken care of and yes probably the new behavior would be opt-in via a $conf flag or such, e.g. maybe even enabled for new installations ...
Comment #42
davidwbarratt commentedI created #2732129: FinishResponseSubscriber::setResponseCacheable() does not respect Cacheable Metadata for Cache-Control header but it looks like it might be a duplicate of this issue?
Comment #43
deviantintegral commentedI just ran into this as a part of #2772847: Add support for private uploads / presigned URLs. I'm a bit lost in all of the discussion, but is there any need for this to be more complex then this patch? At the least, it's working for me in the basic case.
Comment #45
berdirThe test fails show why this is a problem. There are max-age 0 elements on many pages, doing this results in them no longer being cacheable. That's a behavior change that I don't think we can just introdue like that.
I've commented about this multiple times before :)
As a start, we need to at least figure out what max-age 0 elements we have and figure out what to do about them. Then respecting this could be an option, I don't know..
Comment #46
andypostThat's all depends on "strategy" used - all places are replaced with placeholder to make whole page cachable if I remember right
Comment #47
wim leers#46: not everything can be placeholdered. Only things that are rendered using a
#lazy_builder.Comment #48
berdirAnd more importantly, we are talking about the internal/anon page cache, that doesn't support that anyway, only the dynamic page cache does :)
Comment #49
fabianx commented#48 I think the only way to fix this is to be honest about it and remove the max-age=0 e.g. for forms when used by the anonymous user, etc.
And all other places, too, where we currently allow caching in page cache and external proxies.
Because if we allow that caching, then we should not be declaring max-age=0 in the first place (for anon user).
Comment #51
berdirSo the patch above didn't actually do anything useful yet, because max-age 0 was skipped and ignored (which should result in it not being cacheable) while a -1 actually resulted in expire being lower than request time and was then not cached.
The 4xx caching expire also changed this quite a bit, so didn't apply anymore.
New patch that should work correctly with 0 and -1. Also checking expires first, that's more backwards compatible I think.
Lets see how many test fails we have now. The aggregator test is now actually passing.
Comment #53
berdirInteresting, was wondering if that was going to happen. That means we're actually doing a much better job at setting max-age than I thought. Good. The question is how many pages we are not testing.
The only fail we have is in GlossaryTest, because that view is marked as uncacheable and that now bubbles up. And the test actually doesn't care about testing that the page is cached, it just uses the standard method from the trait to assert this. And the cache tags/contexts are actually there on the response, the problem is just that there is no cache entry now anymore for this view.
Also weird is that \Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait::assertPageCacheContextsAndTags() does a negative MISS check on the first request, but it does *not* check for a HIT on the second. But it would implicitly fail as shown in this example.
The reason why the glossary view is not cacheable is interesting, though. And that's \Drupal\views\Plugin\views\style\Table::getCacheMaxAge(). That returns 0 and I can't really say why? Best I can tell is that this is actually a bug that we introduced in #2464427: Replace CacheablePluginInterface with CacheableDependencyInterface. We went from isCacheable() TRUE to max-age 0.
That means we have two ways of fixing this test. I first converted the test to not use that trait, but by fixing the table style plugin, the test actually passes unchanged.
Comment #54
wim leersUgh, that looks like a big, sad mistake :( Did some archeology: #2381217: Views should set cache tags on its render arrays, and bubble the output's cache tags to the cache items written to the Views output cache introduced that code, and there, we did check the first request was a MISS and the second a HIT. Then came along #2477157: rest_export Views display plugin does not set necessary cache metadata and it refactored things, and it seems that's how it was lost. I failed to notice that.
+1 for being an accidental bug. I re-read that patch, and indeed, the
Tablerow style is the exception: everything went fromTRUEtoCache::PERMANENTand fromFALSEto0, except this one.Fixing the table style plugin totally makes sense to me. If people feel uncomfortable doing that here, it could easily be spun off into its own issue.
I'm very wary of using
> Cache::PERMANENTor< Cache::PERMANENT. I'm only comfortable with=== Cache::PERMANENT. I don't see why we can't make this$expire = $max_age !== Cache::PERMANENT ? $request_time + $max_age : Cache::PERMANENT;I think this patch looks splendid. I'd love to RTBC, but … I think we must be overlooking something? I remember this issue feeling unsolveable without breaking BC. Do we need additional tests to ensure we're not breaking BC?
Comment #55
pfrenssenThis looks like it will cause all pages in my current project to become uncacheable in a very fun way.
I have a couple of facet blocks in my search page, and these have
max-age = 0. I have noticed that this currently causes all my pages to be marked withmax-age = 0, not just the search page. At the moment this is not a huge problem since, hey, the page cache still caches pages withmax-age = 0at the moment! :DI could not find an issue about it so I created #2828136: A single uncacheable block can make the entire website uncacheable. We might have a look at this since this will mean that merging this issue will affect every site that uses uncacheable blocks, for example because they use the Facets module.
Comment #56
berdirCommented there, but yes, I think that is the main thing that we have to worry about. Core is either doing pretty fine or we simply don't have the explicit assertions to detect unexpected page cache misses. But who knows that will happen to actual sites.
What we could do is declare it an (experimental?) opt-in behavior that you have to enable with a setting or so. And declare that we'll make it the default in 9.x. Kind of the opposite of a deprecation. And speaking of that, if this would just work, it would mean that we could replace most if not all use cases of the kill switch service with setting max-age on the response/some render array and eventually deprecate that?
Comment #57
pfrenssenI agree that we cannot predict all cases that will happen in live sites, but even though I did find #2828136: A single uncacheable block can make the entire website uncacheable which would turn my entire site uncacheable, I actually think it's fine to fix this for real. I don't think we should default to the buggy behaviour and opt-in to the fixed behaviour. I fear that this will just complicate things in the long run. The current behaviour is just wrong.
I do think we need enough lead time to discover other potential caching problems that are now hidden because of this issue. 8.3.0 is scheduled for April next year, if we can get this in before the end of December this will probably give enough time. After that, we should maybe consider a kill switch or postpone it to 8.4.x.
Comment #58
bkosborneDoesn't the current patch fail to account for
FinishResponseSubscriber.php, which currently has this code:The max age is always taken from the core config setting.
EDIT: The issue I described is documented in #2732129: FinishResponseSubscriber::setResponseCacheable() does not respect Cacheable Metadata for Cache-Control header, but as it stands it's not clear if this issue is intended to fix that as well as the Page Cache module, or just the Page Cache module.
Comment #59
catchWe should split this into two issues:
1. For deciding on and implementing support for a new #cache property which can be used to set s-max-age or Surrogate-Control or similar.
2. For bubbling max-age itself (except I don't think we should do that because it could very easily break existing sites).
Comment #60
duaelfrAbout Max-age bubbling we could create a checkbox in the performance settings to enable this option then have an upgrade path that would disable it for existing sites to avoid breaking BC. That setting could be marked as deprecated and removed in D9 (or not).
Comment #61
pfrenssen+1 for @DuaelFr's idea of providing this as a configuration option which is disabled for existing sites and enabled for new installations. I don't think it is necessary to make this option visible in the UI, but if we do this is then the performance settings are indeed ideal for it.
Comment #62
vijaycs85we are trying to solve two different problems here (at least in code level):
1. Adding expire to page cache - gets saved in a storage to serve when Drupal receives a request
Current: This has been done by the value of response's 'expires' header. If there is no 'expires' header, cached permanently.
Solution: patch #53 allows to fallback to bubbled-up max age, which is set by setCacheMaxAge(),
2. Adding max-age header - Allows other systems (browser, cdn, reverse proxy) in front of drupal/server to deal with the response caching
Current: If a page is cacheable, use the config value system.performance:cache.page.max_age to set max-age in cache-control header
Solution: We have two parts mentioned in #59 here. We can use #2732129: FinishResponseSubscriber::setResponseCacheable() does not respect Cacheable Metadata for Cache-Control header for 59.1 and this issue for 59.2 AND page_cache expire.
As per #60 and #61, we can provide an option, so that user can select to use setCacheMaxAge() instead of fixed system.performance:cache.page.max_age
Below patch adds option in performance page and use it as max-age in response header. Now it's possible to fallback on getCacheableMetadata()->getCacheMaxAge() for both page_cache and max-age on Cache-Control header.
Comment #64
vijaycs85Quick schema update...
Comment #66
vijaycs85Fixing last test fail.
Comment #67
wim leersThis is looking good, but it's not ready yet! An update path is missing for example.
===
Why 30 days?
This seems to be an unrelated fix.
Not every response has cacheability metadata. So you need to wrap this in
else if ($response instanceof CacheableResponseInterface) {…}and then keep the originalelse.I'm not sure these are the appropriate labels.
"fixed" is really "override": override whatever the bubbled max age is, and use this fixed value anyway.
"dynamic" is really "computed", "calculated" or "bubbled": it matches the rendered content's max-age.
The
?: 'fixed'should not be necessary if there is a working upgrade path.Nice!
Comment #68
vijaycs85Thanks @Wim Leers, for the review.
#67.1 - FIXED
#67.2 - NOT FIXED: Not sure how to set permanent. probably we shouldn't set max-age derivative at all?
#67.3 - NOT FIXED: Yeah, its for page cache. @Berdir mentioned, we might split the views related changes to different issue. probably leave it for now.
#67.4 - FIXED.
#67.5 - NOT FIXED: hmm, I know I wasn't totally happy about the labels either. Should we do ['override' => 'Fixed period', 'bubbled' => 'Dynamic']
#67.6 - NOT FIXED: we have hook_update_N. we might need to change the number, but it would work right?
#67.7 - :)
Comment #70
vijaycs85Comment #72
wim leersThis still needs an update path.
#67.6: well if it works, hen you should be able to remove that
?: 'fixed'portion!Comment #73
vijaycs85Thanks @Wim Leers. Updated for #67.6 and re-rolled.
Comment #74
wim leersCan you provide an interdiff?
Comment #75
vijaycs85@Wim Leers It was a reroll as the patch in #70 was against 8.2.x. However here is the diff-ui (after all we have UX initiative in core :D)
Comment #76
rgpublicWhat I don't understand (and I literally mean "don't understand" - it's not a rhetoric question or rant or anything): How could Drupal 8 have ever been released without that feature? Why is there some talk above in this issue about it being "nice-to-have"? Why do I diligently set my max-age-keys within my preprocess functions of paragraphs, views all the time only to find out they have no effect whatsoever, because the page cache is overriding all of this anyway? It's totally unexpected. Or was the full-page-cache never intended to be used at the same time with the dynamic page cache caching individual parts? Was it my mistake to enable both or expect both of them to work at the same time? Would be great if someone could enlighten me :-)
Comment #77
wim leershttps://www.drupal.org/project/cache_control_override implements this in contrib, see http://cgit.drupalcode.org/cache_control_override/tree/src/EventSubscrib....
Comment #78
rgpublicWell, I liked the solution in this patch much better, because you can set the Max-Age explicitly to "Dynamic". The module uses the setting "Permanent" to disable the dynamic Max-Age. So you have to explicitly set a max-age for it to work. The patch, however, didnt work with the current Drupal version 8.3.4 (mostly Array syntax changes), I've modified the patch and posted it here. Perhaps it's also useful for someone else.
Comment #80
sic commentedtotally agree with 76. wasnt the "new" cache system such a great new deal of d8? So many things dont work out of the box, or just not well, it still surprises me today how d8 could be released :(
so, this is still a bug and the only workaround is that contrib module?
Comment #81
yogeshmpawarComment #82
yogeshmpawarUpdated patch, because #78 failed to apply on 8.4.x branch.
Comment #84
berdir#2232375: Support CacheOptionalInterface in BlockViewBuilder, use it in language switcher block to prevent max-age 0 bubbling is one of possibly many issues why this change/issue is not as trivial as it may look. Any multilingual site with a language switcher would then be completely uncacheable. So we likely need to fix that issue first and identify what else would be impacted but is not explicitly tested.
We also need still need to move the Views Table style fix to a separate issue and get it in.
Comment #86
andrewbelcher commentedI just got caught out by this. It seems very un-intuitive to me that I would go to the effort of setting a max age for it to be ignored by page cache... Not sure I'm in a position to deal with the various complications of resolving this issue (e.g. translation stuff), but when I get time I'll at least try to get the documentation updated so it's clear...
Comment #87
berdirAnother problematic issue for this is #2521956: Missing contexts prevent caching of block access. Combined with this, that would disable page caching on all non-node pages as soon as you use a node type visibility condition.
Given that we do not have test fails for either that or the issue mentioned in #84 shows how problematic this is, it is quite likely that we are missing test coverage for a whole bunch of cases that would result in partially or completely disabled page cache for a lot of sites. Even a configuration option IMHO isn't an OK solution as users have no way of seeing/knowing about those issues.
See also #2888838: Optimize render caching for some ideas on what kind of features would also be quite important, for example a way of saying "this block isn't worth caching", so that we can e.g. stop render-caching the "Powered By" block without that bubbling up and resulting in not caching the whole site.
Comment #88
effulgentsia commentedI haven't read through all the comments in this issue, but my hunch is that there's really important information in some of them that isn't reflected in the issue summary, so if anyone is inspired to update the issue summary with where this issue is at, that would be helpful. Thanks!
Comment #89
dries commentedI was messing around last weekend, and I ran into this problem as well. I was expecting "max-age=0" to bubble up and not cause the page to be cached. I was surprised it still got cached until I found this issue; it looks like it gets other people confused as well. I worked around it with a kill switch but it feels like a hack; see screenshot.
Comment #91
izus commentedin my case, a workaround for this:
uninstall "Internal Page Cache" and let only "Internal Dynamic Page Cache" enabled
Comment #92
gagarine commentedRelated https://www.drupal.org/project/drupal/issues/2968485 .
I will keep this issue as a personal reminder that drupal is in slow death. If @dries #89 don't understand how drupal cache system (don't) works their is a serious problem.
Comment #93
berdirAnother example of why this is not easy: #2578855: Form tokens are now rendered lazily, allow forms to opt in to be cacheable. Any page with a form, including any page with a lazy builder that is then uncacheable would be uncacheable by the internal page cache module.
Comment #94
rgpublic@Berdir: Absolutely, it might not be easy and I don't want to defend @gagarine and I see Drupal thriving more than ever and not in slow death, but I have to admit I do share somewhat of his harsh criticism, because: Drupal 8.6 is now approaching - the caching system was introduced with 8.0 and advertised as the next cool thing. And then I read about what features are planned for 8.6 (https://www.drupal.org/core/roadmap) I read about a lot of work (or so it seems) going into migration of old legacy sites, Media support etc. Certainly all great features. But shouldn't something basic like the caching system work reliably? And isn't it a least a tad bit disturbing that even Dries is surprised by this bug? I really shared this part of @gargarine's sentiment. But don't get me wrong: Drupal is a cool piece of software and D8 is really a huge step forward. And I do appreciate all the work. But I sometimes think those problems should really be given a bit more priority. These things IMHO are also part of the "Out-of-the-box" experience, because you need to do a lot of researching and reading to make seemingly simple things work properly as you just don't expect them to be buggy in an 8.5/8.6 release. We're out of alpha state, I suppose.
Comment #95
catchThe render caching system, considering it was almost entirely new in 8.x (at least in terms of its application and several new concepts like cache tags and contexts) does work reliably. This issue is a lot more about expectations for a specific use case. Also the render cache API and the caching system are two different things, the former is based on the latter. Most projects do not have an equivalent of the render caching API which handles variation and invalidation in the way we do.
If someone wants to help get the issue closed, then updating the issue summary would be a good start. Documenting issues that block this one like #2578855: Form tokens are now rendered lazily, allow forms to opt in to be cacheable would help as part of that.
Comment #96
mpp commentedComment #97
borisson_We should probably postpone this issue on the 4 issues that @mpp linked in #96.
Comment #98
dawehnerComment #99
Hungerburg commentedMy site has an events page - Only future events are in the view by filter. The internal page cache does not respect the filter criterion in the view, anonymous users always get outdated views. Do I have to disable the internal page cache for all of my site, just to have the events page show current affairs?
Comment #100
johnpitcairn commented@Hungerbug: That's what I had to do in that situation. You may find that the combination of the dynamic page cache and bigpipe makes the site almost as fast as it was with the anonymous page cache anyway.
Comment #101
berdirWhat we implemented in our project is a response subscriber that converts a non-zero, non-permanent max age to the Expires header, which allows time-based expiration without disabling page cache entirely for pages that set max-age 0.
Comment #102
wim leersLovely to hear that! ❤️❤️❤️ 🙏
Comment #103
malcolm_p commented@Berdir we are doing the same; Wim mentioned https://www.drupal.org/project/cache_control_override earlier in this thread. Adding that workaround to the issue summary may help some people who are encountering this without having to dive that deep into core until there is a permanent fix.
Comment #105
Hungerburg commented@malcom_p The cache_control_override module makes filters on the view active, even for anonymous users. Performance does not suffer noticeably. Deservers more prominence indeed, I probably never learned of it, if it was not for your post!
Comment #106
yakoub commentedi think you won't be able to solve such problems without client side javascript code that updates part of the page outside the context of the whole page caching .
Comment #107
rodrigoaguileraChanged issue link for a better overview
Comment #108
duaelfrI'm not sure to understand why this has to be postponed.
Some pages can have no forms, no book navigation block, no language switcher and still need to have a proper bubbling of their max-age.
Fixing this issue just won't change anything for people that do have one of the above on their pages but could really help all other websites.
Am I right or am I missing something?
Comment #109
gagarine commented"I'm not sure to understand why this has to be postponed."
Neither I do, certainly because this issue is simply to hard. Seriously this should be a blocker: a cache system with a broken cache invalidation is useless.
And don't make me wrong, this is a complicated issue. But it's why you use a system like drupal at the first place, so you don't have to take care of super complicated things like cache.
This major bug in the cache system is now 4 years old. It's unbelievable.
If someone know how to fix it, but need money to work on that, please give us a price.
Comment #110
borisson_This is true, but when they have one of those elements the page should behave in an a correct way. Because the current behavior, without those issues fixed is not correct, this will lead to far less hitrate in the page cache/reverse proxy. If I understand it correctly, that is the reason why this issue is postponed by those issues.
This might be overstating it, the cache system as it is implemented right now is not useless.
Comment #111
mpp commentedAtm #2888838: Optimize render caching is marked as a blocker for #2232375: Support CacheOptionalInterface in BlockViewBuilder, use it in language switcher block to prevent max-age 0 bubbling which in turn is a blocker for this issue.
I updated the summary to reflect this.
Comment #112
mpp commentedComment #113
rgpublicHmmmm. I've been experimenting again with the above mentioned CCO module recently to see if it can be of any help with this long standing issue:
https://www.drupal.org/project/cache_control_override
The situation is: I have a block somewhere on the page which is showing dynamic data from an external source (weather). Now, I want it to be updated regularly. First attempt: Set #maxage of this block to 0. Without the CCO module, this is ignored by the static page cache. I get X-Drupal-Cache: HIT and the weather never updates. Now I install the CCO module. It detects that the maxage 0 has bubbled up and then switches off all caching (static and dynamic). Now, the weather info is updating. Nice. But: I wonder whether this is really correct. IMHO only the static page cache should be disabled, because other blocks on the remaining page could still of course benefit from the dynamic cache.
Now, in addition, it would be even better to be able to set #maxage to 120 so the weather only updates every 2 minutes. The CCO module doesnt support that. It only detects whether the value is 0. Which is understandable as the static page cache itself AFAIK doesnt yet support a max-age. It would of course be cool if the static page cache would update after 2 minutes.
IMHO, It should simply work like this: I have a website. There are several blocks/paragraphs. They define their separate max-age: 50, 120, 900, whatever. The minimum of all max-ages bubbles to the top and is used for the static page cache and cache-control header.
Comment #114
mikeryanWell, looks like we're 25% of the way to unpostponing...
Comment #116
sic commentedthis is so frustrating. this should really work for a cms before releasing it :(
Comment #117
dpiLets try to be more constructive.
Comment #118
Chris CharltonPatches welcome!
Comment #119
jansete commentedHi all,
Are the patches very olds? Is a good point to start? Or is better start from scratch.
Greetings!
Comment #120
adamps commentedI have just found this issue and after spending some time reading through I tried to do the IS update to help future readers. Most of the people who have commented on this issue already know the facts better than me, so please correct me if I misunderstood.
I have raised #3075116: Document limitations of Cache Control Override asking CCO to document the limitations on the project page.
Comment #121
wim leersThanks, @AdamPS!
Comment #123
marcoscanoAdding #2985253: content_moderation_entity_access() wrongly adds uncacheable dependencies as related issue, since if it doesn't get fixed before this one, that's another place where a
max-age=0ends up being generated and bubbled up.Comment #124
chi commentedAttached a patch for 9.x that checks max-age from cache metadata when storing response. It could be used as a workaround when a site does have issues with block caching (#2828136: A single uncacheable block can make the entire website uncacheable).
Comment #126
bmathieuhFew years ago, I wanted my pages to be cached the whole day, because one block changed at midnight every day... and I didn't manage to do that before I noticed that it seems that the age is lost when we get the cached_element in RenderCache.php So I substracted the request time when I get the cached element in RendereCache :
And I wrote a sample module, a kind of proof of concept : https://github.com/mathieu71/render_cache which allowed me to do what I wanted.
Comment #128
maskedjellybeanI just want to make sure I'm reading this correctly. The page_cache module is enabled by default and so is dynamic_page_cache. If page_cache module is enabled, dynamic_page_cache (and max-age) essentially doesn't work for anonymous users?
If that's correct, then shouldn't there be an explanation of this at /admin/config/development/performance? There isn't even an indication on that page (the only one that I know of where I can configure Drupal core caching) that two caching modules are involved. I really think that if this is by design (as Berdir says here), then there ought to be an explanation on this page that explains the differences between the two modules and when it might be best to simply disable page_cache entirely (such as when most of your visitors are anonymous but you have a lot of dynamic content). I think we should even be able to enable or disable the two modules from this page.
If this is truly by design, then this seems like mostly a communication issue doesn't it?
Another example: In the "Cache API" documentation there is no explanation that two modules are involved. It doesn't explain that most of the documentation is actually about functionality provided by the dynamic_page_cache module and completely unrelated to (and in fact negated by) the page_cache module (as far as i can tell). In fact you don't learn this until you read to the bottom of "Cache max-age". There is a page about Internal Page Cache and another about Dynamic Page Cache but they're not linked to the the main "Cache API" documentation and don't even link to each other. It's chaos.
I'm frustrated I wasted time wondering why max-age wasn't working because of bad documentation. If I knew enough to fix this documentation I would, but I can't even say with certainty that what I'm saying now is 100% correct.
Comment #129
maskedjellybeanThat said, if someone in the know confirms what I said is correct, I will hack at this documentation and link these pages together.
Comment #130
rgpublicAfter fighting for a very long time with this bug, I've come to the conclusion to switch off page_cache module. IMHO it just shouldn't be enabled by default. It would be much better the other way round. If you really have only static pages and a massively visited site - THEN it might make sense to enable this and shortcut many requests while being fully aware that you need to be extra careful when you do changes. Currently, it's automatically installed and you might think that it might work for most cases when in fact it causes huge problems. My mistake was to actually try to make this work. And it gives the superb dynamic_cache a bad rep, because you thin that it doesn't work when in fact only the page_cache causes all the trouble. Just uninstall page_cache is the recommendation I can give.
Comment #131
jackson.cooper commentedHere's patches that include updates to the `Cache-Control` header, as well as the cache storage expiry time.
Comment #133
Bram Linssen commentedFor people that stumble upon this issue and look for a solution to cache blocks for a specific amount of time there is a fairly simpel solution.
Just add your own cache tag to the block and create a cron job that invalidates this tag. This will bubble up to the page cache.
In the cron job you will be able to specify how often this job has to run, eg daily in my case.
See the following post that I found regarding the issue: https://ashday.com/blogs/custom-cache-tags-max-age-drupal
Comment #134
mxr576I strongly agree with #130
Comment #135
heni_deepak commentedI also agree with #130 and #134. But it's not a solution. If I disable page cache then server response time is increased and the page took time to load in less network. Is this a definite solution?
Comment #136
rgpublic@heni_deepak: As this is not really moving forward for a long time, I guess the Cache Control Override module (see issue description) is your best bet if you want to keep page_cache. Another, perhaps a bit controversial/unconvential tip that nevertheless worked for us: Don't necessarily search for solution to your performance problems in the cache. Disable page_cache. And if your website now gets slower, check if there is sth. else you can do. Perhaps do some profiling (XDebug, Tideways, Blaxckfire) if you have other bottlenecks in your code. And, often overlooked, hardware isnt really that expensive these days IMHO. Why not use a second server? Or faster CPU? Or faster disk (SSD). All these things can make a huge difference. Is your APCu/OPcache/Memcache configured correctly? They are very important even if you only use dynamic_cache!
Comment #137
yakoub commentedi'm sorry to repeat but what about using angular ?
https://www.drupal.org/project/drupal/issues/2352009#comment-12814040
Comment #138
johnpitcairn commentedIsn't that what BigPipe is for?
Comment #139
leon kessler commentedJust ran into this issue, and want to get page_cache working correctly on a site that has a max-age set from render cache.
There appears to be two currently possible alternative solutions:
Both of these solutions are working OK for me.
But I'm not sure which of these is the best option. The core patch from this issue is smaller, but looks like it's unlikely to be merged in anytime soon.
Patch for CCO module also does the trick, but that module seems to be more intended to solve the issue for external cache's/CDN's (so again it may not get merged in).
Possibly I'm using page_cache in a way it was unintended, i.e. a poor-mans Varnish. But I can't really see how else it is supposed to work? (Perhaps we are scared of breaking the internet by messing with page_cache)?
Comment #141
plachI was also bit by this and came up with a patch before finding this issue. It's available at https://www.drupal.org/project/drupal/issues/2938524#comment-14457583 in case anyone is interested.
Comment #142
gaëlgComment #144
moshe weitzman commentedmass.gov needs this bubbling as well.
@plach - #141 patch looks good to me. is there a reason you chose to use Expires header instead of Cache-Control? Looks like Cache-Control is the modern way. Or call $response->setMaxAge().
Comment #145
plach@moshe weitzman
No good reason, let's switch to
Cache-Control:)Comment #146
berdirThere is a reason, expires is what the internal page cache module uses.\Drupal\page_cache\StackMiddleware\PageCache::storeResponse().
To make it clear again. There are several issues that are blocking this, without having those resolved, this will absolutely go nowhere.
Per issue summary, https://www.drupal.org/project/cache_control_override is an option, but the same blocks apply to that. Language switcher block? no caching for you. Forms? no caching for you. certain views? no caching for you.
We worked around that by ignoring max-age 0 and only explicitly supporting specific age expiration definitions, essentially this, in a KernelEvents::RESPONSE event:
If you want to hep move this forward, we need help to review and complete the documented child issues.
Comment #147
rgpublicThanks for the clarification/update. One thing I don't understand, though: Currently those mentioned page elements emit a max-age:0 but then we are almost "happy" about the fact that this has no effect ;-) ? If the language switcher (for example) is currently not cachable, then, well it's not cachable right? Why would we want the page cache to serve a page with a language switcher on it from the cache anyway? I thought this issue here is only about the idea that when a page contains elements that are not cachable then the whole page simply should not be served from the cache.
Comment #148
moshe weitzman commentedThis patch is based on #141 but it uses Cache-Control. This is better for sites that dont use Drupal Page Cache module (i.e. every site I've worked on). Before you use this patch, read @Berdir comment in #146 to make sure you site doesn't use the known problematic bits of Drupal, and do your response header testing.
Comment #150
sonfdUpdate the issue summary to link to the referenced comment.
Comment #152
casey commentedThis patch is based upon #131, but ignores cacheable metadata's max-age if it is 0. This should work around the issues like suggested by @Berdir in #146
Comment #153
casey commentedTurns out, skipping max-age of 0 (that is, handling max-age=0 as permanent in page cache) is not a good solution; if you use max-age to ensure a cached page is rebuild after a certain timestamp, and if a request happens on exactly the expiration time, the response must not be cached (as the max-age is 0). At least in such cases, page cache must not handle max-age=0 as cache permanently.
And since we (currently) cannot determine why max-age was set to 0, I think page cache should never cache responses with a max-age>=0. Even if this means that certain (or all) pages will no longer be cached by the page cache. Note this means that for example multilingual sites using the language switcher block (#2232375: Support CacheOptionalInterface in BlockViewBuilder, use it in language switcher block to prevent max-age 0 bubbling) no longer have their pages cached by the page cache module, but at least the page cache won't be incorrectly caching pages permanently that have a max-age>=0. Also, those pages will probably still be cached by the dynamic page cache.
I think we should deprecate using the Expires header as the expire time for the page cache and just use the max-age of the cacheable metadata of the response. There are several places (like access checks) that don't have access to the final reponse and can only pass cacheable metadata.
I also think this issue is actually about two different issues:
For now, if your site depends on time-based cache invalidations you at least need the patch from #3447821: Stacked caches result in max-age drift in caches, even if your site is not using the page_cache module.
If you are using the page_cache module, you also need the patch from #131 as a complete solution.
If you are not using the page_cache module (but are using another public/managed caching solution, like a reverse proxy like Varnish), the patch from #148 might work.
The Cache Control Override module can be used additionally if you want to enforce a minimum and/or maximum on the max-age of the Cache-Control response header.
Comment #154
casey commentedThis patch is a combination of the patch in #131 and #3447821: Stacked caches result in max-age drift in caches for usage in sites <=D10.2
Comment #155
casey commentedPatch #154 incorrectly did request_time - expire instead of expire - request_time to recalculate mag-age
Comment #156
davidwbarratt commentedI'm still subscribed to this issue 8 years later and I re-read the duplicate I created and I'm still thinking about this:
#2732129-4: FinishResponseSubscriber::setResponseCacheable() does not respect Cacheable Metadata for Cache-Control header
And now I'm wondering.... so what? Like if a bunch of things become uncachable in Drupal 11.0 and we go and fix those things (individually) in 11.1+ that seems... fine? Am I missing something here?
Comment #157
yakoub commentedmaybe consider my comment once again ?
https://www.drupal.org/project/drupal/issues/2352009#comment-12814040
Comment #160
bbralaCommitted #155 to an issuefork so tests actually run on that code.
Comment #161
davidwbarratt commentedThat seems fine?
In my mind, undercaching is always prefered to overcaching. In an ideal world, it would be perfectly cached, but if the form (or whatever) declares that the whole page is uncachable because of it's inclusion, then.... it is what it is. The only thing you can do is refactor that element to be cacheable (i.e. generate the dynamic bits with JS and/or WebAssembly)
Comment #162
davidwbarratt commentedOr we figure out that we don't actually need to declare these things as completely uncachable anymore.
For instance, what if we decide to rely on SameSite=Lax rather than using our custom CSRF protection? This is what other projects like Next.js do and maybe that would be fine for us as well?
Comment #163
catchCSRF protection doesn't kick in unless you have a session, in which case page caching doesn't happen anyway. However if forms are uncacheable purely because they might have CSRF protection, then that would be breaking page caching for no reason then.
I have personally seen multiple sites that only stay online because of Drupal's internal page cache or sometimes because they cache pages in a CDN. If we suddenly make those sites uncacheable, they will go down. Something to test would be this patch + a site with anonymous comments enabled (and the form displayed on node pages) to see if those pages still go into the page cache. It would be OK if some poorly behaving contrib modules break the page cache after this lands, although we still might want to make the behaviour configurable as a first step, but not if lots of pages output by core do.
#2578855: Form tokens are now rendered lazily, allow forms to opt in to be cacheable would make forms render cacheable.
Comment #164
mxr576Made outdated patches hidden, MR#8155 contains the latest changes _always_.
Comment #165
tstoecklerRead through chunks of this issue again and as far as I can tell two separate things are being discussed here and while both of them are related to the cache max-age and its (lack of) bubbling, only one of them seems to be the cause for the seeming "intractability" of this issue:
While I would absolutely applaud also getting the second use-case somehow properly integrated with page cache it seems getting the first use-case to work is technically just as simple but does not have any of the repercussions in terms of page cache integration that the second one does.
So I would like to propose to solve that issue first, i.e. to bubble nonzero max-ages into the page cache and explicitly leave the "max-age = 0"-case as a todo for the future.
The only place in core we ever set a nonzero, finite max-age, as far as I know, is the time-based caching plugin for views, so the only affect this would have in core is those views actually being properly expired by page cache. But the benefit would be that any contrib or custom modules that either (knowingly or unknowingly) do not work with Page Cache or somehow have to employ hacks to set the response expiry manually will now work with Page Cache automatically (and without any hacks).
Would love to hear if people agree with the splitting up of this problem and if so, whether or not the first (nonzero) part should happen in this issue and the zero part be handled in a follow-up or if we should keep the zero part here and split out the nonzero part.
Comment #166
gaëlgI do not have all the specific elements in mind, but generally, yes, I'm in favor of splitting this kind of big old issue in several smaller ones, so that they can be reviewed more easily. That's the kind of practice encouraged by core reviewers: https://www.youtube.com/watch?v=_uCfmFH4rUs
Comment #167
bbralaI think #2232375: Support CacheOptionalInterface in BlockViewBuilder, use it in language switcher block to prevent max-age 0 bubbling could be handled without evaluating the render cache i think. Hoepfully we can get some activity there.
For this issue, we should try and keep the blockers as small as possible, seems it is a pretty short list.
Comment #168
timohuismanThis patch contains a snapshot of the current state of the MR so it can be safely used with composer-patches.
Comment #169
berdirNote: cache_control_override, at least in it's current from, does _not_ deal with page_cache, see #2916705: Page cache isn't invalidated
There was lot of activity on #2888838: Optimize render caching and related issues in the last few weeks and months. That said, I don't think that is actually blocking this at all, as it's just optimizing (the scope of that meta changed a few times).
So removing it and reducing pp, although i have no idea what the correct number for that is. The most obvious one is still the language block, but I'm working on that.
> Things setting their max-age to 0 to declare themselves as uncacheable
> Would love to hear if people agree with the splitting up of this problem and if so, whether or not the first (nonzero) part should happen in this issue and the zero part be handled in a follow-up or if we should keep the zero part here and split out the nonzero part.
In fact, our custom response subscriber does exactly that, it ignores a 0 max-age. But we've also been using the language block patch for years. The problem is that in this scenario, anything setting a max age 0 will remove the ability for anything else to set a finite specific cache max age. Most multilingual sites have a language switcher (or multiple) on every page, so this won't do anything for them.
I recommend rebasing this so we can see the current effect this has on the performance tests.
Comment #170
hmdnawaz commentedThe patch in # 148 for Drupal 11
Comment #171
berdirThe language block blocker is resolved. Started a look at the remaining test failures, but it gets "interesting" quickly.
PageCacheTest fails on the fact that with this, 404 pages are no longer cacheable in page cache. That is because \Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest throws a non-cacheable exception (as symfony doesn't know about cacheable exceptions), and then in \Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber::makeSubrequest(), we run this code:
So this was kind of always the intention, but at the same time, we also want those responses to be cacheable and have a built a way to deal with invalidations with the 4xx-response cache tag. On HEAD, we set Cache-Control to must-revalidate, no-cache, private, set the debug header to Uncacheable... but we actually still cache it. I'm not sure what the right change here is. Should we change DefaultExceptionHtmlSubscriber::makeSubrequest() to not break caching? That will mean it will also be cached externally and in dynamic page cache. Maybe only ignore it for a client error to keep it in sync with \Drupal\Core\EventSubscriber\ClientErrorResponseSubscriber::onRespond? At a glance, the dynamic page cache test coverage does not seem to have explicit test coverage for a 404, so it might not cause test failures, but it will be a behavior change.
Comment #172
rgpublic"That will mean it will also be cached externally and in dynamic page cache" => If we approach that question from a mere functional POV, I'd spontaneously say that's even desirable, right? 404 pages are exactly those pages that IMHO would profit the most from an external cache like Varnish etc. considering how many invalid page calls are raining down on a usual Drupal website per day. Stuff like /wpconfig.php (WP login attempts etc). The only exception would be if the 404 page is reacting individually on the specific request. But that's rarely the case and if someone is implementing this, they should probably send a bubbling max-age 0 with that content. Just my few thoughts on this...
Comment #173
berdirI agree, but it's a behavior change and to be clear, it does not reflect the current state of this MR. Right now, 404 pages are completely uncacheable, internally and externally. We'll need to open another issue and align this, which means this is again/still blocked.
Comment #174
catchA couple of related issues for 404s #3516169: Adding url.path to breadcrumb cache context results in every 404 page getting a different dynamic page cache entry #3516173: Block status code visibility condition should use a status code cache context - we do have some bad behaviour including in core on 404s but it's also fixable.
Comment #176
tstoecklerRe #171:
OK, couldn't/shouldn't we work around that, then? So that instead of the non-cacheable exception a cacheable is thrown directly by an overridden/alternative listener as we know the 404 just depends on the
urlcache context (or similar, didn't check in detail, but it definitely seems "knowable") or alternatively we check for the exception thrown fromRouterListenerover inDefaultExceptionHtmlSubscriber(which also seems doable albeit somewhat hacky) and convert it into a cacheable one.So I think we could still leave the general applying of the exception cacheability (including uncacheable ones) and just fix the one(s) that are actually problematic. That would still make it a behavior change as random contrib/custom controllers that throw
NotFoundHttpExceptions are now now longer cached were they were before, but with a change record explaining to convert to the cacheable exceptions that might be acceptable?Also not 100% what specific issue you want to open in #173, i.e. which part you think cannot/should not be fixed here directly. Would love to push this forward, so would appreciate your input.
Comment #177
berdirYes, I think we should address it, just not quite what the best option is, there are going to be some behavior changes either way if we cache it consistently (dynamic page cache + external caches). I think a separate issue makes sense because I'm sure there are going to be other issues with other failing tests and then we can discuss and decide on this specifically with a dedicated change record.
another note: This includes code from #3447821: Stacked caches result in max-age drift in caches, or an earlier version of that. That might not be a hard blocker as it's already an issue with existing stacked caches, but this does make a larger issue. But I think that one is close.
Comment #178
tstoecklerOK, that's fair enough. Does the fact that this is then again blocked on at least one other issue which is anticipated to be at least somewhat contentious change your opinion on #165, i.e. could we maybe fix this for all cases where the max age is strictly larger than 0 with an explicit @todo to resolve the === 0 case once the other issue is solved?
Comment #179
berdir> which is anticipated to be at least somewhat contentious change your opinion on #165
It might not be, I didn't get around to doing it, but I think if we find a good technical solution then we might be able to get this done reasonably quick. If you give it a try then I can help with reviews.
> could we maybe fix this for all cases where the max age is strictly larger than 0 with an explicit @todo to resolve the === 0 case once the other issue is solved?
I'm not against it if the 404 issues turns out to be complicated. This is a big enough change that the whole thing maybe should be behind a feature flag anyway for BC (off by default for now, only non-zero, all). And then we either drop the flag completely or switch the default later.
I'm a bit surprised that there is only one test fail left now, two previous ones were contact, which is removed from core now and one was a weird performance change, so this might be closer than I thought.
Comment #180
tstoecklerOK, taking that as a mild "no" ;-)
Took stab at the cacheable 404 thing, though, now, so would love some thoughts over in #3580545: Make empty route lookup cacheable.
Comment #181
berdir#3580545: Make empty route lookup cacheable is in, this seems to have a surprising amount of test failures again though. Some are expected, the generic rest tests are repeating many times.
What's concerning is that the random fails are back that I've seen before:
Drupal\Tests\jsonapi\Functional\EntityTestComputedFieldTest::testGetIndividual in https://git.drupalcode.org/project/drupal/-/pipelines/806973/test_report...
This is actually due to the changes of #3447821: Stacked caches result in max-age drift in caches, which I think we'll need to resolve first separately in that issue. The stacked max-age fixes now sometimes cause off-by-one assertions on the actual max-age on those requests. I'll also add a comment on that other issue on that.