Problem/Motivation
@yched :
CacheableMetadata trips me up each time as implying that it is "metadata that you can cache". Rather, it's the metadata describing the "cacheability of something" --> CacheabilityMetadata would be more accurate ?
@Wim Leers :
Dear god thank you yes!!!!!!!
I discussed this with catch almost 2 months ago and he agreed we should rename CacheableMetadata to Cacheability. I fear it's too late now though :(
@yched :
Yeah :-/ Given the importance render cache has in D8 (currently and even more after SmartCache), and the fact that it's ... complex enough ;-) (see the discussions in #2556889: [policy, no patch] Decide if SmartCache is still in scope for 8.0 and whether remaining risks require additional mitigation), I'd think not having misleading names add to the complexity is worth the disruption. I'll create the issue and ask for release manager feedback.
In short :
The render cache and "things providing the right cacheability information" is shaping up to be one of the critical concepts to grasp in D8. Yet our central class for that, "metadata about the cacheability of something", has a name that gives you the wrong idea about what it is. That doesn't really help building and keeping a good mental model, since you have to mentally translate whenever you read / write code dealing with those :-)
Proposed resolution
Rename CacheableMetadata to
- CacheabilityMetadata
- just Cacheability ?
That is late for a BC breaking API change, obviously. Not sure whether how large the potentially impacted code might be though.
Alternatively, we could keep a legacy/deprecated "CacheableMetadata extends [NewBetterName]" around for BC, and move our onternal code to the more accurate [NewBetterName] ?
Remaining tasks
Release manager feedback needed
Beta phase evaluation
Issue category | Bug : class is a central piece of the API, but its name describes it for what it's not |
---|---|
Issue priority | Major : major DX bug in a critical API |
Disruption | Breaks code, obviously, but not sure what would be the exact extent |
Comment | File | Size | Author |
---|---|---|---|
#72 | cacheablemetadata_is-2561773-72.patch | 257.13 KB | vacho |
#39 | cacheablemetadata_is-2561773-39.patch | 8.5 KB | Wim Leers |
Comments
Comment #2
yched CreditAttribution: yched commentedFiled as major for now, I'd argue that, as a "major/critical DX issue within a critical API", it could qualify as critical.
Personal thoughts for the name :
I guess Cacheability works, it kind of feels incomplete somehow :-)
I find CacheabilityMetadata clearer and easier to use in sentences / code comments / discussions.
No biggie - and, yes, guilty : https://twitter.com/yched/status/638735993219235840
Comment #3
yched CreditAttribution: yched commentedwithout messing the beta evaluation table
Comment #4
yched CreditAttribution: yched commentedComment #5
Wim Leers@yched++
Quoting a new comment I just posted: #2551907-26: Follow-up for #2483183: make the Breadcrumb value object use composition instead of inheritance:
Comment #6
Wim Leerscatch and I prefer
Cacheability
.My reasoning: the "metadata" suffix is unnecessary, because "cacheability" already implies metadata: a value object capturing "cacheability" cannot have any other meaning, purpose or interpretation than "metadata". So, including the "Metadata" suffix would make it a pleonasm IMO.
Attached is the minimally viable patch.
Comment #7
yched CreditAttribution: yched commentedThanks for the explanation Wim. That makes sense and helps understanding that area, although it is indeed not too clear to me by looking at the code / API.
But then :
- if "you should generally not ever interact with CacheableMetadata", then it means the BC break impact of a rename is likely to be small ?
- If CacheableMetdata is "later, internal step in the pipeline", then for the "public API" side I guess it's more CacheableDependencyInterface I struggle with :-) I have a hard time mentally understanding "I am a 'cacheable dependency' " as "I have cacheability metadata that you want to take into account".
I guess it's the "dependency" bit, I don't see how "being cacheable / having cacheability info" makes me a "dependency" (of what ?), can't really map that to, for example, what config dependencies are.
[crosspost : fine with Cacheability :-) ]
Comment #8
yched CreditAttribution: yched commentedAnd
class Cacheability implements RefinableCacheableDependencyInterface
,i.e : "a Cacheability is a (refinable) cacheable dependency" confuses me the same way :-)
Would s/CacheableDependencyInterface/HasCacheabilityMetadataInterface be accurate ?
Or even CacheabilityInterface ?
(leaving aside "feasibility and impact of such a rename at this point" - just trying to see if I get the concepts and model right)
Comment #9
Wim LeersThe name
CacheableDependencyInterface
was selected after extensive debate in #2444231: Fix CacheableInterface so it makes sense (was: "Make Config objects & Entities implement CacheableInterface + add BubbleableMetadata::createFromCacheableObject()").The reasoning is:
.(First it was called
CacheableInterface
, but we decided that made no sense in #2444231-22: Fix CacheableInterface so it makes sense (was: "Make Config objects & Entities implement CacheableInterface + add BubbleableMetadata::createFromCacheableObject()"), -23, -26 and -27.RefinableCacheableDependencyInterface
is a more recent addition (also selected after extensive debate, in #2512718: EntityManager::getTranslationFromContext() should add the content language cache context to the entity), and is layered on top of the above reasoning. Its reasoning: . It is then refinable so you can add cache contexts/tags, so the loaded object can itself contain the reason why that specific variant (e.g. entity translation) was loaded (e.g. the "negotiated content language" cache context).Also, this is all documented over at https://www.drupal.org/developing/api/8/cache/cacheable-dependency-inter... :)
Theoretically that could work, but it takes a slightly different world view.
With
CacheableDependencyInterface
, we say that an object that implements it, and when that object is used as a dependency in some computation, that it allows that computation's result to be cached. This is the entire point of cache tags, contexts and max-age. That cacheability metadata is inherent to the object in question (think aNode
entity'snode:5
cache tag).With
HasCacheabilityMetadataInterface
, we say that an object has a value object that contains cacheability metadata. The same things are possible. But it's not as strong a connection: it does not communicate the inherent nature, nor does it communicate the point of this metadata. It almost seems like a "oh, by the way, I have this pile of metadata too".Comment #11
yched CreditAttribution: yched commentedThanks @Wim. I still find it a bit backwards to couple the name after the use intent ("I can be passed to the addCacheableDependency() method of some other object") rather than by what the standalone object is ("I have cacheability info, deal with it"). Also, just like CacheableMetadata, CacheableDependency seems to qualify those objects as "cacheable", while this is not about caching *them*.
But well, looks like that ship has sailed after intensive debate that I didn't bother take part in :-). Sorry for reopening that, never mind.
Regarding just the CacheableMetadata rename then :
Actually, the real understandability win would be to rename the various getCacheableMetdata() methods accordingly...
It's code like
$response->getCacheableMetdata()
that is actually the most misleading to read - as if this was about caching something else than the $response.AFAICT that affects CacheableResponseInterface and CacheContextInterface - the latter of which has many implementations :-/, but maybe not that much in contrib yet ?
Comment #12
borisson_Patch in #6 did not apply, attached a reroll. I'm also +1 to renaming to
Cacheability
.Comment #13
Wim LeersHah! That's actually exactly why it's called
, and not ! Because it's not about caching them, but they may be a dependency of something that we end up caching! That's what I tried to explain earlier but clearly did not succeed.Does that make sense now?
Can you think of a better way to explain it?
Great discussion :)
Just to make sure I understood that correctly… you mean renaming them to
getCacheability()
, right?Correct, I don't think there's any yet. It's only necessary for advanced use cases anyway (for every custom thing to vary by). So disruption would be very minimal.
Comment #14
yched CreditAttribution: yched commentedSure, I got the explanation, but I'm really skeptical that we can force people's brains to understand "CacheableDependencyInterface",
- not as : "a dependency that is cacheable" (which it is not, but is how the name does read IMO)
- but as : "a dependency of something that is cacheable" (which it is) instead
Which is why qualifying those objects as "I have cacheability info" rather than "something cacheable can use me as a dependency" would IMO be easier. Shaping the interface name to qualify the object, rather than the other objects using that object, means you don't need to find ways to convey that some parts of the name qualify the object ("dependency") and some other parts qualify the "other objects using that object" ("cacheable"), which seems quite a grammatical challenge :-)
Yep, I meant s/getCacheableMetadata()/getCacheability() :-)
+ Last attempt at defending "CacheabilityMetadata" : consistency with BubbleableMetdata ? Again, no biggie...
Comment #15
Crell CreditAttribution: Crell as a volunteer commentedThis. That sentence alone summed up all of my confusion about CacheableDependency*. An interface should describe the object, not the relationship of an object to some undefined and arbitrary other object. That some other object happens to use it a certain way is incidental, by design, because different objects can use it in potentially different ways.
Comment #17
Wim LeersNo, it is
.You indeed don't know how an object is going to be used. The point is that any object that does implement
CacheableDependencyInterface
is a cacheable dependency (a dependency that is cacheable). So any object depending on it, if it wants to do caching, then it is possible if and only if it depends if all objects it depends on are cacheable dependencies.Comment #18
yched CreditAttribution: yched commented#13 (emphasis mine)
#17
Hm - what ? I'm just more confused then :-)
The two statements can only coexist if "cacheable" doesn't mean "something that will possibly be cached - written into a cache store and read back later so that we don't recompute it from scratch" - which I'd think is a reasonable/common understanding of the word ?
It's that same common definition of "cacheable" that led us to conclude that what is currently called CacheableMetdata is not something that is "cacheable", hence the "CacheableMetadata is misnamed" issue title.
So, paraphrasing your sentences about CachebleDependencyInterface objects, I'd think :
- "it's not about caching them" = they are not the "cacheable" things,
- "but they may be a dependency of something that we end up caching" = they are "a dependency of something cacheable", not "a dependency that is cacheable"
- "any object depending on it, if it wants to do caching ..." = "if it wants to be cacheable, it can have dependencies that provide associated cacheability info (but are not themselves being cached/cacheable)"
In "Entity instanceof CacheableDependencyInterface", it's not the Entity that is cached/cacheable, it's some render output, of which the Entity is a dependency.
Same in "FieldStorgeDefinitionInterface instanceof CacheableDependencyInterface", this is never about caching the field definition.
(those two happen to have their own caches, but that's irrelevant here, and possibly even adds to the confusion...)
Really sorry for the pedantic (and remote :-/) hair splitting at this point. As the discussion shows, names do go a long way clarifying or obscuring the (fairly critical) underlying mental model (what is it that gets cached / what is it that provides cacheability info about that cached thing)
Comment #19
yched CreditAttribution: yched commentedAlso, #2476407: Use CacheableResponseInterface to determine which responses should be cached, where cacheable does mean "will be cached / written into a cache store".
CacheableResponseInterface, CacheableMetadata and CacheableDependency cannot use the same meaning of "cacheable"
Comment #20
Wim LeersNo, it is "a dependency that is cacheable".
100% correct.
I disagree.
CacheableMetadata
is a misnomer: it's not the metadata that is cacheable, it's (a value object containing) metadata describing cacheability. So there, you are right. But that's also why we have this issue to rename it so it makes sense.CacheableResponseInterface
andCacheableDependencyInterface
are consistent and have the same meaning. The first says "a responses that can be cached", the second says "a dependency that can be cached".I think that this issue needs @effulgentsia, who is a native speaker, was instrumental in arriving at the name
CacheableDependencyInterface
, and is able to explain these things much more clearly than most of us most of the time :)Comment #21
yched CreditAttribution: yched commentedYeah, looks like there's something I'm really not getting, or we're talking past each other somehow (my bad for not being in Barcelona :-/)
Yet you agree with :
"Entity instanceof CacheableDependencyInterface is not about caching the entity, FieldStorgeDefinitionInterface instanceof CacheableDependencyInterface is not about caching the field definition, but about caching something else that states it has a dependency on it.
I'm struggling to see how that does not contradict :-) It can be cached, yet it's not what we're interested in caching ?
Is it :
"it's something with cacheability info so we *could* cache it if we wanted, but it so happens that we're not interested in *actually* caching it, we only use the cacheability info to cache something else that uses it as a dependency"
?
Comment #22
Wim LeersBasically, yes.
Comment #23
yched CreditAttribution: yched commented@Wim : Thanks :-) Still feels a bit weird to me, being actually cacheable is more than just providing cacheability info, there are implementation concerns if an object is *really* going to be serialized to a persistent storage. But well, I'll shut up now :-)
As for CacheableMetadata, do you think a rename is still doable ?
and for the getCacheableMetadata() methods ?
Comment #24
Wim LeersI think a rename is still doable, as long as we keep supporting the old name, and just mark it deprecated.
It is too late (and already was when we opened this issue) to rename the methods I think.
Using this approach, we have zero disruption.
Comment #25
yched CreditAttribution: yched commentedRTBC then ?
(although : couldn't we similarly add new getCacheability() methods and deprecate the old ones ?)
Comment #26
Wim Leers#25: no, that would still change the interface.
Comment #27
catchWe could add a new interface extending the old interface for that. But probably in a followup?
Comment #29
Wim LeersI think it's indeed better to do that in a follow-up. This issue covers the most visible part.
Comment #30
Wim LeersThis is a catch issue.
Comment #31
dawehnerThere is the thing of renaming, but can we please also get sane documentation?
This documentation doesn't tell you much. It should be explained in which causes it should be used for example.
Comment #32
Wim LeersTotally agreed, doing that now.
Also, repeating what I quoted in #5, and with emphasis added:
Thoughts?
Comment #33
yched CreditAttribution: yched commented+1 on bringing that into the phpdoc for Cacheability :-)
Comment #34
Wim LeersI rewrote and refined this at least five times. I hope you like it.
Comment #35
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedAre we really sure we want to keep an API, which is sometimes immutable and sometimes not.
I (and many others) found this really confusing.
Or even better:
...
Just some food for thought ...
Comment #36
dawehnerThanks a lot
Comment #37
Wim Leers#35: I share your concerns, but that's out of scope here. This is just about renaming. Very few things call this, so *maybe* and *hopefully* we'll be able to still fix that during RC. But we must assume it is too late to change that. We'll see. The reason this rename is possible still is that it doesn't break anything: we keep full BC.
Comment #38
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedWell, I prefer addDependency() anyway over merge, which is just an addition and way less overloaded in terms of mutability.
And no it is the addition of a new Interface, so we could do changes here - that is why I am asking if we do not want to not copy merge() over and leave merge() just on the old class.
merge() btw. takes only a CacheableMetadata anyway right now, so should that not be Cacheability?
Comment #39
Wim LeersThat's a great point!
This still keeps full BC.
In doing that, I also noticed that
Cacheability
/CacheableMetadata
still hasgetCache(Tags|Contexts|MaxAge)()
methods, even though those are already provided by theRefinableCacheableDependencyTrait
that it uses. So, simply removed those.Comment #40
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedLittle nits, can be fixed on commit:
Cacheability
Comment #41
effulgentsia CreditAttribution: effulgentsia at Acquia commentedHEAD still has 300 occurrences of "CacheableMetadata". I don't think we necessarily need to convert all of them in this patch, but I think updating the issue summary with a plan of what we think the eventual end state will be will help us evaluate whether this is a good idea. Is it possible to organize all of HEAD's usages into buckets (e.g., CacheContextInterface::getCacheableMetadata(), usages in \Drupal\Core\Menu, etc.) and figure out how this name change will eventually fit in to those mental models?
Comment #42
Wim LeersWe could easily convert the vast majority in this patch, but the idea was to keep it as simple as possible here.
Regarding the several
getCacheableMetadata()
methods: my understanding is that those are frozen, and cannot be changed anymore, see #24 + #25 + #26.I'm not quite sure what you want me to do here now?
Comment #43
effulgentsia CreditAttribution: effulgentsia at Acquia commentedIn reading some of this issue's comments of people struggling with the concept of "cacheable dependency", I looked at the docs of CacheableDependencyInterface and found:
I think this adds to the confusion, because the use of the word "bubbled" here I think means that "parent" refers to parent elements in the render array tree. In which case, these docs belong on CacheableMetadata (or with this patch, Cacheability). And the docs on this interface should not be about bubbling, but about how the information benefits / transfers to dependents (e.g., via ->addCacheableDependency()).
Comment #44
effulgentsia CreditAttribution: effulgentsia at Acquia commentedRe #42, sure, let's keep getCacheableMetadata() frozen, but change its @return to Cacheability.
And for everywhere where the CacheableMetadata class is still used, let's either convert all of those usages, or if it would simplify this patch, at least ones used in type-hints and docs of non-test code.
Comment #45
effulgentsia CreditAttribution: effulgentsia at Acquia commentedAlso, something we should figure out here (perhaps via a comment from @catch) is whether we're expecting all of contrib to change their type-hints from CacheableMetadata to Cacheability. Because if not, then does that mean that core will need to keep getCacheableMetadata()'s and other @return types as CacheableMetadata? If so, I'm not sure if this patch really improves the mental model. Instead, it makes people have to remember when to use which name.
Comment #46
Wim LeersSounds like this needs a decision or at least input from catch to move forward.
Comment #47
catchIf Cacheability extended CacheableMetadata then doesn't #45 become a non-issue?
Also where are those contrib type hints? Why can't they type hint on RefineableCacheableDependencyInterface?
Comment #48
joelpittetWe've passed RC deadline on this one. Is this "rc target" eligible @catch?
Comment #49
effulgentsia CreditAttribution: effulgentsia at Acquia commentedAdding the new "rc target triage" tag per https://www.drupal.org/core/d8-allowed-changes#rc so that we triage it on our next triage call.
Comment #50
catch#47 still needs an answer to determine how much of a bc break this might or might not end up being.
Comment #51
effulgentsia CreditAttribution: effulgentsia at Acquia commentedAnswering #47:
The problem with that would be that the return value from something that returns CacheableMetadata (e.g., all contrib implementations of CacheContextInterface::getCacheableMetadata()) couldn't be passed to something that improved its typehint to Cacheability.
I don't know what contrib functions typehint params to CacheableMetadata, but 2 core examples are MenuParentFormSelectorInterface::getParentSelectOptions() and CachePluginBase::alterCacheMetadata(). I don't think either of those examples would make much sense with RefineableCacheableDependencyInterface. There's not a dependency that you're refining (e.g., an entity being refined by a language); there's cache metadata that you're collecting/aggregating. So I'm guessing that there's likely to be some contrib/custom module in a similar situation of having a CacheableMetadata typehint, but I don't know.
Personally, per #45, I think the addition of Cacheability and deprecation of CacheableMetadata should be punted to 8.1 at this point.
Comment #52
effulgentsia CreditAttribution: effulgentsia at Acquia commentedI suppose one way to make this all BC would be to make
Cacheability extends CacheableMetadata
andCacheableMetadata implements CacheabilityInterface
. Then @param typehints could be narrowed from CacheableMetadata to CacheabilityInterface while Cacheability return values could still be passed to all places that still typehint on CacheableMetadata.I still think that's a better thing to do for 8.1 than 8.0, but I'm open to counterarguments or @catch deciding otherwise.
Comment #53
Wim LeersPinged @catch about this, so he can decide.
Comment #54
xjmThis definitely needs to be 8.1.x now, and we should also use an approach like #52 so that there is not a BC break. Thanks!
Comment #55
xjmComment #57
cilefen CreditAttribution: cilefen commentedThe deprecation version needs an update in the next patch.
Comment #58
catch#52 is the sort of thing I was suggesting for this - so assuming it actually works out as non-breaking that sounds good. Un-assigning.
Comment #59
catchComment #60
Wim LeersThe problem with #52 is that we still can't get rid of the
merge()
method that is defined onCacheableMetadata
:Cacheability extends CacheableMetadata
means it inherits it!Comment #61
dawehnerCould we turn it around and let Cacheability extend CacheableMetadata, and keep the code in the later for now, while still marking CacheableMetadata as deprecated?
Comment #67
Wim LeersLet's do this before Drupal 9 ships.
Comment #68
catchSorry I'm nuking the Drupal 9 tag - this could happen in any minor release whether Drupal 8 or Drupal 9. Doesn't do any harm to do it sooner than later of course.
Comment #70
Wim LeersI think this is a great novice issue not to push across the finish line, but to at least get going again. I'd be happy to assist/help!
Comment #71
vacho CreditAttribution: vacho at Skilld commentedComment #72
vacho CreditAttribution: vacho at Skilld commentedI contribute with this patch to this issue.
Comment #73
Wim LeersThanks @vacho! Unfortunately it's not passing tests yet.
This seems to have gone under the radar again. I'd still like to see this happen, so assigning to myself. Already created one issue that makes this slightly simpler: #3071572: ContextCacheKeys should implement CacheableDependencyInterface rather than extend CacheableMetadata.
Comment #76
mradcliffeI am removing the Novice tag from this issue because there isn't anything to do if @Wim Leers is (still?) on the case. Feel free to unassign and promote the issue back though. :-)
Opinions: The patch, at this point, should probably just implement the new class and deprecate the old one for 10.0.0.
Comment #80
mxr576@Wim Leers can you please share your thoughts on #76? What should be the attack plan here?
Comment #81
Wim LeersIt's been years since I looked at this. I think I agree with the plan @mradcliffe proposes in #76. Except that deprecating in 9.4 for 10 might be too little notice for something like this — but I can't decide that, only release managers can. Perhaps @catch has thoughts?
Unassigning after the stupid self-assigning I did 3 years ago in #73 🙈
Comment #82
catchThere are about 40 pages of results on http://grep.xnddx.ru/search?text=CacheableMetadata&filename=&page=42, even with some false positives that's quite a lot.
Having said that, it'd be an easy rector rule.
I would probably go for deprecating in Drupal 9.4 for removal in Drupal 11 at this point - but no reason it can't happen in 9.4 with the later removal version.
Comment #84
catchThis is a naming improvement rather than a bug as such, since it'll be a new deprecation, moving to 10.1.x
Comment #86
donquixote CreditAttribution: donquixote commentedQuick note: We can use
class_alias()
to reduce the BC impact of renaming a class.https://3v4l.org/80D6A
It does _not_ help us with renaming methods, but we can find other solutions for that.
Comment #87
andypost