Problem/Motivation
I was profiling the decoupled router module and the biggest overhead in there was calculating the list of resources. The same overhead will be triggered on the next request to fetch the node by ID that the router will provide. On top of that this data seems mostly static but we are generating it on every request and preserve in a static cache that gets lost.
Proposed resolution
Task 1:
Introduce a persistent cache on top of the static one, so we follow the flow static cache -> persistent cache -> generation
.
Task 2:
After research (can be found in the discussion below) implementation of the ::all() method was found to be non-optimal and even the cold cache scenario can be optimized.
Both tasks are already done in the latest patch #69.
Final benchmarks can be found in #73
History / Others
During development it was found that there are cases that need to clear the static cache mid-request. This were initially raised in #44 and #49 and this is already included in the patch from #69.
RESOLVED There was a blocker that was preventing commit in jsonapi_extras - #3019729: Invalidate JSON:API Extras' cache tags when needed
Remaining tasks
JSON:API team review and commit.
User interface changes
None - this are cache internals.
API changes
None - change of implementation detail.
Data model changes
None.
Release notes snippet
Maybe note that there is a persistent cache around the data...
Comment | File | Size | Author |
---|---|---|---|
#111 | interdiff-110-111.txt | 540 bytes | ndobromirov |
#111 | 3018287-111--reroll-of-89-for-8.7.11--do-not-test.patch | 9.38 KB | ndobromirov |
#89 | 3018287-89.patch | 9.4 KB | Wim Leers |
#89 | interdiff.txt | 2.37 KB | Wim Leers |
#88 | 3018287-87.patch | 9.06 KB | Wim Leers |
Comments
Comment #2
ndobromirov CreditAttribution: ndobromirov at FFW commentedComment #3
ndobromirov CreditAttribution: ndobromirov at FFW commentedComment #4
e0ipso160ms to calculate the relatable resource types only once? That seems excessive.
Can you share the insight of
calculateRelatableResourceTypes
? Maybe even a blackfire trace we can drill down on?Comment #5
ndobromirov CreditAttribution: ndobromirov at FFW commentedI do not have blackfire running, but here's 2 levels deeper:
As well as the bigger offenders underneath...
Comment #6
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere are more contextual results...
Xhprof summary + AB results (no xdebug, no xhprof) before:
Xhprof summary + AB results after:
Comment #7
e0ipsoUgh. Even though I don't like the extra complexity this brings in the numbers make a good point. NOTE: I do not think this will be a problem in non-paragraph based data models.
I'm on the fence on this. This goes against the simplification we hoped to introduce in #3016733: Simplify ResourceTypeRepository; use a protected property in place of an in-memory cache bin..
Comment #8
e0ipsoComment #9
e0ipsoComment #10
ndobromirov CreditAttribution: ndobromirov at FFW commentedThe module degrades proportionally to relationships and total entity types amount. Whether this are paragraphs, custom blocks, nodes, terms, custom entities, webforms, it will degrade with the site's complexity.
The cache hierarchy I am introducing can be a moved outside from the class in 2 ways:
During further checks i found that
Drupal\jsonapi\ResourceType\ResourceTypeRepository::isReferenceFieldDefinition
is executed SO many times (1800+), but it's executed over 133 fields (amount taken from fields report UI). So adding a static cache there should speed things quite a bit for cold cache scenario as well.Research is ongoing...
Comment #11
ndobromirov CreditAttribution: ndobromirov at FFW commentedSo the static cache helped the cold scenario dropping the
Drupal\jsonapi\ResourceType\ResourceTypeRepository::isReferenceFieldDefinition
resulting in this speed-up without any caches:Here is the AB as well (no xhprof):
PоC patch is coming shortly.
Comment #12
ndobromirov CreditAttribution: ndobromirov at FFW commentedThis dropped some good 40-50k function calls based on the xhprof top level summary.
Code here can be moved to a decorator without any issues.
Note: The persistent cache is still an open question.
Comment #13
ndobromirov CreditAttribution: ndobromirov at FFW commentedI've managed to speed-up the other slow internal underneath all from ~35ms to ~15ms on cold cache. Total function calls are down to around 35k from around 170k.
The high level idea is that the list of resources instead of numerically indexed is now keyed by
entity_type:bundle
so we will not need to loop the list over and over again, but just look-up the key for presence in the map.This changes are small improvements to the internals of the service, but the persistent cache in there will still help, as it will spare all that computation on every request.
Comment #14
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere are the benchmarks that the last patch can achieve (without a persistent cache).
Top level summary:
Changes in
Drupal\jsonapi\ResourceType\ResourceTypeRepository::getRelatableResourceTypesFromFieldDefinition
:AB (no xdebug, no xhprof):
Comment #15
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere is the final patch that will add the persistent cache with no further changes to code - just tweaks on the services that gets injected.
Cached performance is comparable to the one in the description.
Comment #16
ndobromirov CreditAttribution: ndobromirov at FFW commentedComment #17
ndobromirov CreditAttribution: ndobromirov at FFW commented@eoipso any idea why the builds are not triggered?
Comment #18
ndobromirov CreditAttribution: ndobromirov at FFW commentedMaybe change the flat list from
"entity:bundle" => resource
to"entity" => ["bundle" => resource]
. This way we will do a micro-optimization, sparing a key preparation on every call and have direct array access on all places I've done changes in here. Nice to have...Comment #19
ndobromirov CreditAttribution: ndobromirov at FFW commentedThe 2 micro-optimizations can be moved to a separate issue, as they are not directly related to the persistent cache, but optimizing the old scenario that the cache will resolve.
Comment #20
ndobromirov CreditAttribution: ndobromirov at FFW commentedI need this patch in a way usable in composer. The one in #15 is conflicting for me with #2992833: Add a version negotiation to revisionable resource types (#153), so here is a much simpler one with just the persistent cache changes in.
Note: The cold cache scenarios are not included in here, but they are both still valid and the changes in 15 are resolving them.
PS: Use the patch from the next comment...
Comment #21
ndobromirov CreditAttribution: ndobromirov at FFW commentedWell - I need to upload a patch :D
Comment #23
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere is a rebased patch on top of the latest dev.
It is including only the persistent cache tweaks in jsonapi.services.yml file.
The performance tweaks will be moved / implemented in their own issues, unless anyone objects to that.
Comment #25
Wim LeersThat's not failing in HEAD, so it seems like a legitimate failure. OTOH, it's pretty strange that a small abstract change like this causes such a specific failure. I'll try to reproduce.
Comment #26
Wim LeersReproduced. The failure is due to the persistent cache not being invalidated after the
jsonapi_test_collection_count
is installed.Which brings us to another point: JSON:API Extras configuration changes need to invalidate this cache.
\Drupal\jsonapi_extras\EventSubscriber\ConfigSubscriber::onResponse()
is adding the'config:jsonapi_extras.settings'
and'config:jsonapi_resource_config_list'
cache tags, but they'll also need to be associated when theResourceType
objects are cached that are generated by\Drupal\jsonapi_extras\ResourceType\ConfigurableResourceTypeRepository
.IOW: this needs more test coverage, and needs manual testing with JSON:API Extras. It potentially also needs changes in JSON:API Extras. We don't want to break JSON:API Extras during the 2.0 RCs.
I'm very glad to see that the work we did on making things more isolated, including making
ResourceType
value objects actual value objects (and no longer call any services), are paying off. This sort of optimization is exactly what I had in mind while we were working on that 😀Comment #27
ndobromirov CreditAttribution: ndobromirov at FFW commentedWill something like this help in an install file on the test module:
I can extend the patch to have it, so we can spare the manual testing.
On top of that maybe expose the tags we are setting on the cached list as a protected method or something, so extras can overwrite it and add the additional tags in there.
Comment #28
Wim LeersYes it would. At least for
jsonapi
. Butjsonapi_extras
3.x doesn't use those cache tags, so you'd still need manual testing. Or … better yet, expand the automated tests of JSON:API Extras :)Comment #29
ndobromirov CreditAttribution: ndobromirov at FFW commentedAs per #28 added #3019729: Invalidate JSON:API Extras' cache tags when needed.
Comment #30
Wim LeersComment #31
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere are the proposed changes
- Tweak on the install of the test module.
- New protected property to be used by jsonapi_extras to modify cache tags used.
Comment #33
gabesulliceThis LGTM. Just needs to fix the CS violation:
Comment #34
Wim LeersAgreed that this is looking close!
Cache tags used for caching the repository.
Nit:
string[]
s/$defaultCacheTags/$cacheTags/
Alternatively, we could remove this hook and instead do
Cache::mergeTags($this->cacheTags, ['config:core.extension'])
, then it'd be invalidated whenever a module is installed, which seems the more prudent approach anyway :)Comment #35
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere are Wim's comments. They should resolve the on from #33 as well, as we do not need the install file now.
1-3: Fixed.
4. Nice idea :)! This will be way more durable and as any module might change resources configuration so jsonapi will react on that.
Comment #36
ndobromirov CreditAttribution: ndobromirov at FFW commentedComment #37
e0ipsoPlease do!
Comment #38
Wim LeersThis is looking much better now! 😀
This is looking quite complex. In fact, this is the first time I've ever seen this. I understand how this works, but how do we know we absolutely need this? Are we concerned about repeated unserialization? Needs profiling to prove we really need both layers.
Comment #39
e0ipsoWe are. In fact this proved to be a problem already with the previous
cache.static
backend.Comment #40
ndobromirov CreditAttribution: ndobromirov at FFW commentedThis exact case was the root cause for #3014232: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources, in my case around 60% of the response time (~7 seconds out of 12).
Comment #41
ndobromirov CreditAttribution: ndobromirov at FFW commentedIf I recall correctly we need to tune the backwards compatibility layer as well.
Comment #42
Wim LeersThanks for confirming.
I think we can mark this
private
too.And rather than repeating this, I wanted to inherit it using
parent: cache.default
, but that unfortunately fails; it somehow insists on inheriting the class too then.This doesn't quite make sense, this is not the
default
bin, but thejsonapi_resource_types
bin. Due to chaining, it just happens to reuse some of it.This comment can now go away, because this is now clearly a cache bin, whereas before, when this was just using the
MemoryCache
class, that was not obvious.Addressed all that.
Comment #43
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere is the backwards compatible change for 8.5 - dropping the 2 levels of cache.
Comment #44
Wim LeersWe should also be invalidating whenever the set of fields is modified through for example adding a new configurable field (the
entity_field_info
cache tag) and/or when the set of entity types changes (theentity_types
cache tag).At least the former can easily be tested by adding a new required field with a default value; this should immediately show up.
Right now, this patch's more aggressive caching is breaking that. That needs test coverage!
Comment #45
Wim Leers#43 looks good!
Comment #46
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere is the changes from #42 incorporated onto #43.
Added the 2 new tags mentioned in #44.
The only things are the tests. I will not be proficient in writing them so anyone can take over.
Comment #47
ndobromirov CreditAttribution: ndobromirov at FFW commentedComment #48
ndobromirov CreditAttribution: ndobromirov at FFW commentedAny examples I can steal test code for this :) ?
Comment #49
gabesulliceThis test should do it.
It will fail because there's still a class property used as a cache that's not cleared with the
->all()
cache is cleared.Comment #51
ndobromirov CreditAttribution: ndobromirov at FFW commentedWe can likely drop the
$cache
property if we introduce the indexing from #13's interdiff.Will have to profile it again at that point...
Comment #52
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere is the proposed change...
Comment #54
ndobromirov CreditAttribution: ndobromirov at FFW commentedFixed CS issue...
Comment #56
ndobromirov CreditAttribution: ndobromirov at FFW commentedHopefully the last CS fix...
Comment #57
ndobromirov CreditAttribution: ndobromirov at FFW commentedThis will likely need a follow-up in jsonapi_extras as well to trigger the cache clear whenever resource configs are changed.
Comment #58
e0ipsoI'd like to see a patch for #3019729: Invalidate JSON:API Extras' cache tags when needed before merging this. Otherwise JSON:API Extras will break, people will get impatient and I'll have the pressure to fix it quick-quick-quick.
Comment #59
gabesulliceThanks @ndobromirov!
Since we're changing ResourceTypeRepository, it's a good opportunity to clean up a little. So, don't mind me. Your patch looked good and I haven't fundamentally changed any of it.
I won't commit this till #3019729: Invalidate JSON:API Extras' cache tags when needed lands, per @e0ipso.
Comment #60
ndobromirov CreditAttribution: ndobromirov at FFW commentedRe-adding the changes from #12 (speeds-up the cold cache scenario a bit).
Comment #61
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere are benchmarks on the patch from #56. There is a small performance regression in the
ResourceTypeRepository::get($entity_type, $bundle)
. But I think it's a good enough compromise. Compared to the original speed couple of weeks back - 109ms.More details about the original issue: #3017239: Optimize ResourceTypeRepository::get(): don't loop over all possible resource types in every call
Here is the before (6ms):
And after the patch (18ms).
Comment #62
Wim LeersLet's not change this.
I asked for test coverage in #44.
This tests that. So removing the tag.
These don't have any effect; it needs to be
public: false
. Yes, this is incredibly bad DX on Symfony's behalf…#3019729: Invalidate JSON:API Extras' cache tags when needed was blocked on #3020237: [Regression] Broken with latest jsonapi 2.0-rc3. #3020237 landed yesterday. I just re-RTBC'd #3019729. Soon this will be RTBC too.
Comment #63
Wim Leers#3019729: Invalidate JSON:API Extras' cache tags when needed landed.
It conflicted with #2992833: Add a version negotiation to revisionable resource types, which added a hunk that this patch was also adding. So this patch became slightly smaller & simpler :)
Comment #65
ndobromirov CreditAttribution: ndobromirov at FFW commentedI am not getting why the system thinks the service is not there as it's clearly defined in the services.yml file. So strange...
Comment #66
ndobromirov CreditAttribution: ndobromirov at FFW commentedCame back here as we are in the process of upgrading RC3 to 2.1 :)
Rebased my local version and fixed the comments from #63 -
public: false & README
.This should be the same as the patch in 64 but I am hoping to be somewhat different and tests to pass :D.
If not, we start from there...
Comment #67
ndobromirov CreditAttribution: ndobromirov at FFW commentedAnd the patch...
Comment #69
ndobromirov CreditAttribution: ndobromirov at FFW commentedFound it... Cache API is using the tagged services in container get calls, so it can not be private... Dropped one private flag on the service definition.
Here is the new patch with it.
Comment #70
Wim LeersI'm very sorry for the silence here, @ndobromirov! 😞
Do we still need this with #3014232-62: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources committed? The issue summary is no longer accurate.
#38 + #39 + #40 say we need the chained cache (with first static caching and then persistent caching) to avoid the cost of repeated unserialization. Is that still true in HEAD? (Since December, we refactored away a lot of places where the resource type repository is accessed repeatedly.)
#61 shows this makes things slower?
Comment #71
ndobromirov CreditAttribution: ndobromirov at FFW commentedPutting in needs work for the profiling and IS update. Details below...
Yes it is still relevant and we are still running with patch from #69.
Yes but compared to #49.
Note that the 2 xhprof-s on the screen are comparing pre-#49 performance of the patch to the one that actually works correctly. And both are way faster than the no-patch scenario of 100+ ms.
The overhead likely comes from 2 places:
1. The fact we've moved from cached data in a private property (non cleanable mid-request) to an external service that can invalidate based on cache tags mid-request. Fixing #49.
2. The XHprof tool function calls overhead.
I think we can have another iteration to adopt the old inline property cache and get VERY similar performance to the original 6ms but we will likely loose the mid-request cache clear functionality or introduce some indirect complexity as discussed before (somewhere in the issues). And that was not liked by eoipso at the time...
Re-profiling with and without the patch should be ok to do.
I am pretty sure it is, as an overall idea :D...
Once I have the profiles will update it as well.
Comment #72
ndobromirov CreditAttribution: ndobromirov at FFW commentedUpdated IS...
Comment #73
ndobromirov CreditAttribution: ndobromirov at FFW commentedAll comments from previous reviews are resolved.
Here are the profiles for the patch with all the related changes:
All tests are done with disabled:
- Page cache. (disabled module)
- Dynamic page cache:
cache.null
- Render cache:
cache.null
No patch:
Patch (#69) cold cache:
Patch (#69) warm cache:
Comment #74
ndobromirov CreditAttribution: ndobromirov at FFW commentedTweaks in IS.
Comment #75
ndobromirov CreditAttribution: ndobromirov at FFW commentedTags clean-up...
This should still work and I am still using it in production for over 10 sites now :).
The supoprt code for jsonapi_extras is already committed.
I consider this very stable and a blocker for further improvements in #3039730: Include paths are resolved for every resource in a resource collection, instead of once per unique resource type.
Comment #76
e0ipsoWhy not a regular
cache.backend.chainedfast
instead?Why only these here? You add some additional below. Any reason to have some, but not all here?
If a module does that it should automatically clear the necessary tags by calling the save action on field / entity definitions. Right?
I'm not 100% sure we are getting the cache tags rights here. It's my own limited knowledge of the Entity API system's fault. Maybe we can get someone to confirm this looks right?
Let's keep the
$cid
as it was before. This way we only change the relevant bits.This seems like an unrelated change. However I'm +1. One thing, did you find this to be a performance bottleneck?
Comment #77
ndobromirov CreditAttribution: ndobromirov at FFW commentedThanks for the reviews and questions.
1. The regular chained fast is APCu + DB. Overwriting the DB storage with something else is let's say tricky. Having the APCu means that we have state in the L1 cache layer. I am OK for testing that. We will spare the initial load from the L2 on every request and that should be a good thing in general :).
2. No particular reason, the ones below are just too generic and seemed OK to hard-code. The ones above are the ones that are kind of exposed through extension (already done by Extras).
3. Yes, as far as I've tested this.
4. Waiting for Wim or someone else on this one.
5. Ok :).
6. Yes. It turned out in the profiling output from XHProf. I do not recall the details now, but this was helping the cold cache scenario to compute the list faster.
Comment #78
ndobromirov CreditAttribution: ndobromirov at FFW commentedAbout 1 - if I recall correctly we needed memory cache as level one, because this is a VERY HOT piece of code. And the
unserialize
that APCu will do, will cause a regression already solved by using the memory cache used here. This one... #3014232: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources, As we can not use APCu, this is why we are not using the core's chained fast...Comment #79
ndobromirov CreditAttribution: ndobromirov at FFW commentedAfter checking the APCu code in D8 core - there is no serialize / unserialize in there. This is a viable option to test.
Comment #80
e0ipsoThanks for looping back in #79! I'm looking forward to get this in. I'd love to hear about what you find. Thanks for collaborating SO much on these issues. Your skills and expertise are deeply appreciated. Please try to not get discouraged by lack of progress.
Comment #81
gabesulliceAmen! Thank you @ndobromirov! I suspect this will get some new life in the next few weeks. The 8.7 release and commit freeze + DrupalCon + a maintainer (me) being on paternity leave meant that the pace of the JSON:API issue queue was slowed down. I hope it will pick up again soon.
Comment #82
ndobromirov CreditAttribution: ndobromirov at FFW commented@e0ipso & @gabesullice Thanks for the kind words!
There is some lack of time on my end due to pressing deadlines at the moment. Once I am a bit more free, I will get on this for sure (as it's saving servers).
Comment #83
ndobromirov CreditAttribution: ndobromirov at FFW commentedSo, I've tested the APCu and it is counter-intuitive :(...
No patch as I do not see a meaningful reason to make one.
Using the chained fast with APCu was killing the performance by a HUGE margin (with XHProf running).
The main difference that I see is that the resource type repository was getting the ::wakeup call every time, where with the memory cache it was not... There were hidden overheads that were just part of the apcu_fetch (taking over 5 seconds for some 231 calls).
On top of that I had to increase the APCU_SCHM_SIZE to a lot more than it used to be (96MB -> 256MB) to make it actually do any cache hit... Before that it was falling back to the persistent cache all the time and it was even slower.
This will cause issues on the scalability side for multisites, as this APCU should be scaled based on the amount / count of multi-sites being installed.
Other thing - the memory usage went up like 6x+ times, further killing the scalability of the system. I have not tracked from where as I was just switching between the 2 cache implementations.
I've changed one of the existing service definitions with this
(Sample code taken from core's bootstrap cache bin definition and tweaked a little):
Tests were executed against a particular article node with good amount of included relationships.
No page cache, no dynamic cache, no render cache. Tests were done on warm Drupal caches (no containers were being rebuild).
Chained fast with APCu (L1) (with the above definition)
Chained fast with Memory (L1) (as in the last patch)
From what I see, we should keep using the memory backed chained fast service.
I have no idea why, but fetching from APCu is WAY slower than the memory cache used in the patch, even if we need to reach to the persistent layer once.
Comment #84
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere is a re-based patch for core. It's functionally the same as the last patch from #69.
The backwards compatibility layer is removed, as it is not needed with core 8.6+. No intrediff for that - just no changes in JsonapiServiceProvider.php
Comments from reviewers are resolved.
Comment #85
Wim Leers#2819335: Resource (entity) normalization should use partial caching landed, let's move this forward again! This is the next big performance win.
(I also think it makes sense to have this live at the class level rather than in code dealing with implementation details, because it ensures cache tags are not dynamically computed, which is something this further communicates.)
I know I suggested this in #34, but this was at a time where
entity_field_info
andentity_types
were still absent. It's safe to remove these now.It's easy to convince yourself of this: look at the DB row for these cache tags in the
cachetags
table, then install for example the Actions module, and observe both cache tags incrementing.Removed. ✅
These are used by
\Drupal\Core\Entity\EntityDisplayRepository::getAllDisplayModesByEntityType()
and\Drupal\Core\Entity\EntityFieldManager::getFieldDefinitions()
, so 👍To be safe, we should add
entity_bundles
too though, which is used by\Drupal\Core\Entity\EntityTypeBundleInfo::getAllBundleInfo()
. Done. ✅ Also expanded the test coverage for this.This was introduced by #3017239: Optimize ResourceTypeRepository::get(): don't loop over all possible resource types in every call but is now no longer necessary, thanks to
::all()
:A) returning a result faster (thanks to using caching itself)
B) that result now has keys that allow a faster checking for a match
Continuing on this observation:
This is now the only place where
$this->cache
is used. And this is one of the exceedingly rare cases where an unclearable static cache is actually okay. Because the result depends on a lot of loading of PHP objects and then checking if the final object in the chain is an instance of a particular class.So: it's fine to remove
$this->cache
. Done. ✅This change means it's switching from its own static cache to not having any caching and instead relying on
::all()
itself being much faster (thanks to caching). 👍Continuing on this observation, let's look at the lines next to it:
In both of these cases, this is not a CID but a key as returned by
ResourceTypeRepository::all()
. Because in both cases, making the call to::all()
return faster (thanks to caching) is just one aspect of the performance boost; the other aspect is removing the need to iterate over all returned resource types to find a match. Finding a match efficiently wasn't possible before, but it is now, thanks to this key.So renamed
$cid
to$key
. ✅Nit: If we're changing this, then we can also simplify
$modules
.Done. ✅
👍 I didn't understand this at first, but it makes sense: this
field_relationship
field can have relationships to both anyNode
and since two bundles exist, that results in two relatable resource types:node--article
andnode--page
.This is now no longer a simple static cache. When #2984886: Trigger route rebuild when new bundles/fields are added/removed introduced it, it was. But then #3014232: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources changed it to use
MemoryCache
(which is a pretty new thing in D8, see https://www.drupal.org/node/2973262). And this is then taking that further and changing it into something else entirely. Will do that in the next patch iteration to keep interdiffs easy to understand.Comment #86
Wim LeersThis addresses #85.9.
Comment #87
Wim LeersThis is caused by the recent #3061117: Make optional and deprecate the BackendChain constructor parameter, easily fixed.
Comment #88
Wim LeersOops, rebased.
Comment #89
Wim Leers\Drupal\jsonapi\ParamConverter\ResourceTypeConverter::convert()
is used on pretty much every JSON:API route, and that calls\Drupal\jsonapi\ResourceType\ResourceTypeRepository::getByTypeName()
. Which happens to be the only method on the repository to not yet have been converted from iterating over all returned values until a match is found. Like #85.6 described, this is one aspect of the performance improvement.So let's make that also use a direct lookup rather than iterating.
s/cid/key/
was an improvement … but it made me realize something more:This key is awfully reminiscent of something else … the resource type name. It uses
--
as a separator instead of:
.Furthermore, we already know for a fact that each
ResourceType
must have a unique name, per the JSON:API spec.So … let's use resource type names as the keys for
::all()
?I think this makes overwhelmingly much sense 😂
Comment #90
Wim LeersMy initial thought:
… but then I realized that actually, this is completely independent of #2819335!
That issue dealt with normalizations of resources (entities). This issue deals with bootstrapping the
ResourceTypeRepository
.The profiling in #73 is therefore still accurate. Which means I think this is ready! Thanks for your patience and excellent work here, @ndobromirov! 🙏
Since all I did was renaming things, adding a few comments, removing some whitespace, adding a single test assertion — in short, nothing but trivial bits that did not materially change the patch but merely made it easier to understand, I think it's okay for me to RTBC.
Comment #93
ndobromirov CreditAttribution: ndobromirov at FFW commented@Wim thanks for the thorough clean-up! :)
I also think it's ready to commit, but by moving the cache tags from the bottom to the top in the property. Now we need to change things in JSON:API Extras. #3019729: Invalidate JSON:API Extras' cache tags when needed
We should do any of the following ones:
- Undo the cache tags move to the property, introduced in #85.
- Redo the commit in #3019729: Invalidate JSON:API Extras' cache tags when needed to include the newly added tags as they are now in json:api.
- Tweak how the value is initialized in the descendant class in JSON:API Extras, so it will add only new unique ones to the list (merge them).
If we commit it like this it will conflict with Eoipso's comment in #58 in the thread here.
Personally I think it's best to have the last option implemented, as that is the best behavior in general and it will not degrade in strange ways in future.
Comment #94
Wim LeersWe shouldn't block core commits for mere convenience (because requiring trivial changes) of contrib modules that are not using official APIs. We're careful (and I was too), but it should never result in an un-RTBC unless it's making things completely impossible.
#3019729: Invalidate JSON:API Extras' cache tags when needed will continue to work fine as-is, it'll just need have more cache tags added to it. Besides, #3019729 was a premature commit anyway, because it assumed that a particular implementation would land in JSON:API.
Comment #95
ndobromirov CreditAttribution: ndobromirov at FFW commentedOk, noted :).
No issues then for a commit: RTBC +1
Comment #96
e0ipsoI'm wondering if the running assumption is that the in-memory cache entries can be invalidated mid-request. If there is no use case for that, I think that a property cache may be a more acceptable solution for the in-memory caching. Can you provide more clarification to that end?
Comment #97
Wim LeersSuch static caches make unit tests nearly impossible. They also bake in the assumption all over the code base that it's okay to have global state because it's going to disappear in the next request anyway. Baking in this assumption all over the place is what Drupal core used to have, and it hugely complicated tests.
So it's IMHO less about a concrete use case than it is about the risks and consequences of baking in certain assumptions.
Comment #98
e0ipsoWouldn't that be solved with a setter?
I think that is indeed OK. I would go further and guess that the in-memory cache back end leverages that fact.
Would it be an acceptable approach if a setter or a
::clearMemoryCache()
method made those tests simple again?Comment #99
ndobromirov CreditAttribution: ndobromirov at FFW commentedTo add to the discussion about the internal properties - once you start using something like PHP:PM the static cache will become persistent one. So having the capability to invalidate it will be huge advantage.
Comment #100
Wim Leers@e0ipso I was answering in the abstract.
Comment #102
e0ipso@ndobromirov that's super interesting! Are you using PHP:PM with a modern version of Drupal?
Comment #103
ndobromirov CreditAttribution: ndobromirov at FFW commentedFailure in #101 seems unrelated...
@e0ipso - this is just theoretical :(. No time to make Drupal run on that, even though I would like to see / make it happen :)
Comment #104
ndobromirov CreditAttribution: ndobromirov at FFW commentedComment #105
ndobromirov CreditAttribution: ndobromirov at FFW commentedPush the bot to retest... RTBC as it was...
Comment #106
catchVery nice.
Committed 22a2503 and pushed to 8.8.x. Thanks!
Comment #109
gabesulliceComment #110
ndobromirov CreditAttribution: ndobromirov at FFW commentedHere is a re-roll of #89 against Drupal 8.7.11 for anyone that might need it.
Comment #111
ndobromirov CreditAttribution: ndobromirov at FFW commentedAnd a functioning patch now...
There seems to be a small API change in the back-end chain that was missing from the 8.8 diff thus the new patch.