Problem/Motivation
There are some problems here that can be handled here or tracked in their designated issues.
If requested like so, I will move them there ant make this a tracking one...
This is happening when the list of includes that needs to be validates is (in my case) 84 items and average tree depth is 4. The request URL is essentially:
http://d8.test/jsonapi/node/article?include=field_image,field_image.field_media_image,field_content,field_content.field_custom_content,field_content.field_custom_content.field_image,field_content.field_custom_content.field_image.field_media_image,field_content.field_custom_content.field_image.field_media_provider_api_image,field_content.field_news,field_content.field_news.field_image,field_content.field_news.field_image.field_media_image,field_content.field_news.field_image.field_media_provider_api_image,field_content.field_gallery,field_content.field_person,field_content.field_person.field_image,field_content.field_person.field_image.field_media_image,field_content.field_person.field_image.field_media_provider_api_image,field_content.field_person.field_phone_number,field_content.field_files,field_content.field_files.field_media_file,field_content.field_products,field_content.field_products.field_product,field_content.field_left_video,field_content.field_left_image,field_content.field_left_image.field_media_image,field_content.field_left_image.field_media_provider_api_image,field_content.field_right_video,field_content.field_right_image,field_content.field_right_image.field_media_image,field_content.field_right_image.field_media_provider_api_image,field_content.field_list_topics,field_content.field_list_topics.field_topics,field_content.field_list_topics.field_topics.field_image,field_content.field_list_topics.field_topics.field_image.field_media_image,field_content.field_list_topics.field_topics.field_image.field_media_provider_api_image,field_content.field_list_topics.field_topics.field_image_icon,field_content.field_list_topics.field_topics.field_image_icon.field_media_image,field_content.field_list_topics.field_topics.field_image_icon.field_media_provider_api_image,field_content.field_questions,field_content.field_questions.field_image,field_content.field_questions.field_image.field_media_image,field_content.field_questions.field_image.field_media_provider_api_image,field_content.field_media_image,field_content.field_media_image.field_media_image,field_content.field_media_image.field_media_provider_api_image,field_content.field_media_video,field_content.field_form,field_content.field_articles,field_content.field_articles.field_image,field_content.field_articles.field_image.field_media_image,field_content.field_articles.field_image.field_media_provider_api_image,field_content.field_articles.field_media_image,field_content.field_articles.field_media_image.field_media_image,field_content.field_articles.field_media_image.field_media_provider_api_image,field_content.field_articles.field_article,field_content.field_articles.field_article.field_image,field_content.field_articles.field_article.field_image.field_media_image,field_content.field_articles.field_article.field_image.field_media_provider_api_image,field_content.field_articles.field_product,field_content.field_topics,field_content.field_topics.field_image,field_content.field_topics.field_image.field_media_image,field_content.field_topics.field_image.field_media_provider_api_image,field_content.field_topics.field_image_icon,field_content.field_topics.field_image_icon.field_media_image,field_content.field_topics.field_image_icon.field_media_provider_api_image,field_content.field_left_column,field_content.field_left_column.field_questions,field_content.field_left_column.field_media_image,field_content.field_left_column.field_media_image.field_media_image,field_content.field_left_column.field_media_image.field_media_provider_api_image,field_content.field_left_column.field_media_video,field_content.field_left_column.field_form,field_content.field_center_column,field_content.field_center_column.field_media_image,field_content.field_center_column.field_media_image.field_media_image,field_content.field_center_column.field_media_image.field_media_provider_api_image,field_content.field_center_column.field_media_video,field_content.field_right_column,field_content.field_right_column.field_questions,field_content.field_right_column.field_media_image,field_content.field_right_column.field_media_image.field_media_image,field_content.field_right_column.field_media_image.field_media_provider_api_image,field_content.field_right_column.field_media_video,field_content.field_right_column.field_form
Issue 1:
Relationships includes validations are not scaling well with reused / many relationships.
This is caused from the following structure of includes that are causing the explosion of the include tree size.
The thing is that a simple static cache can speed-up this algorithm a lot (suspecting).
Step 1: EntityType-1 -> Paragraph-1 -> Media-1 -> File
Step 2: EntityType-1 -> Paragraph-2 -> Media-1 -> File
Step 3: EntityType-1 -> Paragraph-3 -> Media-1 -> File
The overhead comes form the recursive calls in steps 2 and 3 that the processing for media 1 and all of it's relationships was completed.
This is problematic on entity level as well, as it's very common to have
EntityType-1 -> Paragraph-1 -> Media-1 -> File
EntityType-1 -> Paragraph-2 -> Media-1 -> File
EntityType-1 -> Paragraph-3 -> Media-1 -> File
... Imagine 40 more paragraphs that could nest each other and have many types of media in many places...
EntityType-2 -> Paragraph-1 -> Media-1 -> File
EntityType-2 -> Paragraph-2 -> Media-1 -> File
EntityType-2 -> Paragraph-3 -> Media-1 -> File
Here the overhead is for all paragraph types.
... Imagine 5-10 different entity types that all share the above list of paragraphs.
All of this is causing repeated computation withing the include tree.
It turned out that the overhead here was caused by 2 things
- DFS on the includes tree, executed based on the public includes count.
- Excessive use of exceptions for code flow and complex errors messages generation.
Issue 2:
Includes meta-data is re-calculated on every request :(, even though it is resource config bound and will never change unless there is a change to entity field or resource configuration changed at some point...
In my use-case this is exhausting 75%+ of the time for fetching a single term through the API.

Issue 3:
On listing responses this overhead is multiplied (per entity count) and will likely benefit from static cache. Or at least it seems that way.
On a particular terms list it is causing 90%+ overhead (28 terms).

Points 2 and 3 are the main reasons why this is marked as critical - 13 out of 14 seconds are spend in here (point 3) and it's a-lot-more than the core's guidelines. It is degrading based on configuration / data
Repeated code paths without added functional value.
Resolved in #14.
Proposed resolution
Point 1:
A smarter static cache that will allow for run-time trimming the include tree where possible will speed things in cold cache scenarios.
Located somewhere in FieldResolver...
Resolved in #14
Point 2:
I suspect that persistent cache here will help alleviate the pain a lot.
This should be around IncludeResolver::resolveInternalIncludePaths
Implemented in the PoC patch through a 2 level cache service in #3.
Point 3:
Static cache should resolve this one.
This should be around IncludeResolver::resolveInternalIncludePaths
Implemented in the PoC patch through a 2 level cache service in #3.
Loop implemented due to the internal iterator, caused repeated calculation for the same entity type because of different entity instances provided, that all share the same type. Resolved in #14
Remaining tasks
- PoC - done proven a valid issue.
- Review(s)
- Decide how to handle the cache tags setting in a jsonapi + jsonapi_extras compliant way if we go with the cache solution.
- Guidelines for final patch implementation.
- RTBC.
- Commit.
User interface changes
None expected.
API changes
None are expected, as we should aim to improve internals and add a cache layer(s).
Maybe add new internal methods to improve efficiency...
Data model changes
None.
Release notes snippet
TBD...
| Comment | File | Size | Author |
|---|---|---|---|
| #50 | interdiff-3039730-48-50.txt | 1.24 KB | ndobromirov |
| #50 | 3039730-50.patch | 1.33 KB | ndobromirov |
| #28 | 3039730-28.patch | 1.25 KB | wim leers |
| #27 | 3039730-27.patch | 5.55 KB | wim leers |
| #27 | interdiff.txt | 1.29 KB | wim leers |
Comments
Comment #2
ndobromirov commentedComment #3
ndobromirov commentedThe PoC patch is somewhat related with #3018287: ResourceTypeRepository computes ResourceType value objects on *every request*.
There are two patches that:
- One that is standalone with the new service added to manage the static + persistent cache.
- One that depends on #3018287: ResourceTypeRepository computes ResourceType value objects on *every request* and is not adding the service ( I will be using this ).
The PoC follows the idea to maintain an always expanding list of valid include paths in cache for a resource type. Have the requested includes cross-checked against the cached ones and compute only the missing ones. The solution should cover point 2 and 3 from the issue summary.
XHProf runs for the change:
Single entity before:

Single entity after:

List 27 entities before:

List 27 entities after:

I will try to see what is possible to be done in point 1 at some point in near future (if anything).
Comment #4
ndobromirov commentedComment #6
ndobromirov commentedComment #7
ndobromirov commentedComment #8
ndobromirov commentedI've tried to speed the issue in point 1 a bit I am hitting a wall, as it seems it can not be made faster for the cold cache scenarios and this is purely on the computational complexity of the problem at hand.
I have the following includes added through jsonapi_defailts.
The problem comes from the fact that field_content is a paragraph reference field and it is referencing about 40 different bundles in there and the loop that goes and checks for connection in each of them, in
FieldResolver::resolveInternalIncludePathThe only way I can see to currently optimize the cold cache scenario, that's currently running for half a second, is to reduce the list size of includes you can see above.
I see 2 options:
1. Renaming and tuning, so including 1 thing can actually generate 2 iternal includes. For example:
Through jsonapi_extras - rename the field in the end to a common public name:
field_media_provider_api_image -> field_media_image
and then include only one line for that:
This way defaults will pass on half the items for inclusion and jsonapi will internally load 2 things on one iteration instead of looping 2 includes to load 1 thing each. Could have been nicer to know this sooner :D. In my case this re-configuration should speed things around 25-30% (hooray)
Is it possible at the moment in jsonapi to have...
... interpreted as
Comment #9
ndobromirov commentedComment #10
ndobromirov commentedFrom researching this, i see the overhead is mostly coming from the fact we are checking the include paths 1 by 1 and in a DFS manner. As a result any fields that have many possible related entities are processed multitude of times, returning a success only 1-2 out of 40 iterations.
In my case this is paragraph reference field that points to ~40 different types and I have ~80 includes that point to something through it. As a result there are ~40 * ~80 = ~2400 edges for traversal and it is going to get slower with more elements added to the system (in theory and practice). This can be mapped to the function calls count I see in the profiler for
FIeldResolver::resolveInternalIncludePath()We already have a utility to build a tree in
IncludeResolver::buildTree(), so lets build the tree first from the requested include paths and have a BFS implemented on it to validate it and build the internal tree / list of paths from it in bulk.The positives here are that we will be able to iterate over the nodes and traverse the edges in bulk. This way it will handle all paragraphs in bulk only 1 time, resulting in let's say 20 successes out of 40. Ideally the complexity for traversal should be based on the nodes in the relationships graph (about 50 in my case). Performance degradation should scale based on nodes in the graph linearly, compared to multiplication of edges and includes count (that increases with entities in the system).
I propose a new method
FIeldResolver::resolveInternalIncludeTree()orFIeldResolver::resolveInternalIncludePaths()to be implemented based on the above description and see the change...I suspect this should be able to drop some 3-4x+ the execution time for cold cache scenarios on point 1 for cases as complex as mine.
Points 2 and 3 are already alleviating the pain A LOT, so I am keeping the issue in needs review. The amount of work needed on point 1 will likely make it deserving it's own issue... Will see...
Comment #11
ndobromirov commentedAfter some more experimentation and tweaks...
The biggest overhead in that method is the generation of the error messages :(.
Just removing that it, by returning empty lists and not throwing exceptions accelerated it like 10-11x times. (450ms -> 40ms) for the whole validation tree.
I have a PoC implementation that behaves 99% the same based on the BFS solution described above that runs (with no exceptions for 5-6ms).
Here is the PoC code for it. Note that there is no error handling in there...
... and now using that in
IncludeResolverlike so:The only functional difference is that I am adding intermediary includes. If you have includes list passed as:
The BFS solution will generate the following things if all is correct:
This for me has the hidden benefit that actually configuring the includes on the resources in jsonapi_extras or clients gets easier to maintain in the long run, as you need to add only paths to leaf resources and not all of the sub-paths in there (like the example some comments above).
I am currently working on the errors handling in the method, but as I see it, it needs to be made in a scalable way. Current implementation that is causing 90%+ overhead is not acceptable.
Comment #12
ndobromirov commentedHere is a new patch that is implementing point 1.
Currently it is handling the cases for invalid includes passed in, but this is without the fancy messages like before.
I am expecting tests to fail because of exactly that :( if at all.
No time to tweak test at the moment. If the direction is OK, I will update them as well.
I will provide cold cache benchmarks as well shortly.
Teaser - currently this is running in 5-10 ms consistently in cold caches for a single term for the same list of includes.3
Comment #13
ndobromirov commentedFunctionally everything is the same except:
- The exceptions... They are now handled in the
IncludeResolver::resolveInternalIncludePaths- Valid sub-paths are always added to the result.
Here is the old cold cache scenario for a single term entity (repost from #3).

Here is the promised benchmarks for cold cache scenario 8ms :D!

At this point it's debatable whether we will need the persistent cache on top of this, but in my case it shows a measurable speed-up of at least 5-6 ms per call.
On top of all that there is still a new method that will likely need tests...
Leaving as needs review. (PARTY)
Comment #14
ndobromirov commentedAfter sleeping on it for a couple of days - the only reason for the need of static cache in issue 3 is that the code was executed multiple times. Essentially in a simple loop. If we move the code to work on the first iteration only, we drop the need for static cache.
On that train of thought, this is only triggered on a per root entity cases.
If we want to have a persistent cache in here, we can move it's storage on the resource configuration objects directly. Have it lazy initialized as it currently is and stored on the value objects we already have. This way no additional cache management overhead will be introduced
If we consider that this is now fast-enough - 8ms on each request (that has that many includes defined, this can go without the need for persistent cache either.
Some tuning internally on the method can shed some of the internal overhead as well - the de-duplication logic. It is not that needed as in the end we will be creating a tree out of the result set either way. So it will be duplicated at that point in a much more efficient manner.
I will be implementing a new patch that aims at dropping the need for a cache layer fully at the cost of around 5-10 ms per request in my case.
If we then decide the cache is needed. It can be added as a follow-up. This will resolve one of the open TODOs I currently have in there - the cache tags management that are currently hard-coded and taken from jsonapi and jsonapi_extras.
Comment #15
ndobromirov commentedHere is the patch from solution proposed in #14.
- Generate and validate the includes list only one time during a request.
- Optimized algorithm - currently running for 8 ms in my case. (same as #12)
- No cache layers.
I consider this commitable.
Comment #16
wim leersImpressive research, thank you so much, @ndobromirov!
However, this issue is definitely not "critical". Everything is working correctly. It's just working slowly for one particular entity type that has made design decisions (these decisions cause *many* performance problems, also outside of JSON:API). I'm fine with optimizing this, but marking this critical is not fair. See https://www.drupal.org/core/issue-priority#critical-bug. I understand it is critical *to you* though!
I will look into this in the coming week!
Comment #17
ndobromirov commentedCritical task based on the link you've provided:
This is not a bug - it works correctly (but slow).
I really do not care for the severity, as long as it's fixed :)
Comment #18
ndobromirov commentedI have some other MISC questions, that cropped up while debugging this.
All the methods I've had to change were static.
If they were on a service this could have been possible to decorate and be tuned from outside.
Are the static methods intentional to make it hard to customize as possible?
How about the errors?
Is it OK that we are now just listing all the invalid include paths in bulk?
- I think it is, as now only 1 exception is generated and only when there are invalid includes passed in. This saves a huge overhead.
- All invalid includes are shown in absolute manner so it should be fairly easy to reason the connections. The only regression compared to the solution present in the module now is that we are first not giving guidelines to what piece of the path is incorrect, but that it as a whole is such.
Comment #19
xjmThis might be considered a performance gate issue for core if JSON:API were in the critical path for normal site operation. However, since JSON:API is a new module that's off by default, let's call it a blocker for using JSON:API in a core install profile. :)
Comment #20
ndobromirov commentedComment #21
ndobromirov commentedComment #22
e0ipsoI value a lot these kind of issues @ndobromirov! Content modelling using paragraphs has many benefits in UX / editorial side, however it can lead to these performance issues you are flagging. This happens in decoupled and monolithic sites.
Thanks to these examples we are improving JSON:API's performance even over the monolith. This is fantastic! Thanks for this.
On the down side you'll need to be patient because this is considered an uncommon configuration, and we're onto more pressing matters at the moment.
Comment #23
ndobromirov commentedNo problem, I have a patch that I can live with :). I can wait.
Comment #24
wim leersQuestions:
metaas in https://jsonapi.org/format/#document-meta, or some other metadata?includeexpressions, and hence has a simple DFS strategy and throws HTTP exceptions. It makes sense to optimize for this, because JSON:API optimizes for getting just the data you need. But in your scenario, with not only a highly complex content model but also an enormous number of default includes, this causes performance issues, because you're trying to fetch All The Data in a single request. That this is slow is then not very surprising :) I agree we should optimize JSON:API's implementation further to allow for very complex use cases like your own. But I feel compelled to point out that you're using JSON:API in an atypical way.Based on what I read here, I think we may want to persistently cache the result for an
includeexpression for a given resource type, if that expression contains >=2 levels. (Since for a single level, there is no overhead: all necessary information already is in-memory.) Perhaps we even want to only do the persistent caching for >=3 levels. This is essentially what you're saying in point 2, and what you've implemented in #3.This seems to be the root cause of the performance problems you're encountering. Let us please keep this issue focused on that, and not address other things here.
Comment #25
wim leers#8: Wow, that is a lot of default includes you're adding… this quite clearly shows the consequences of using Paragraphs. That being said, there are a lot of includes you can omit. As https://jsonapi.org/format/#fetching-includes documents:
So in your example,
can be simplified to:
You can easily check whether this is indeed working as expected, by doing this in the standard install profile:
/jsonapi/node/article?include=field_tags.vid— this will result in all articles, all their tags and all their vocabularies being fetched, with a singleincludeexpression.EDIT: hah, ironically, this is what you're also talking about in #11, but it already works this way :)
#12: this is where it becomes very confusing. Let's keep this issue about the patch in #3 only, if we're going to be talking about multiple patches and multiple problems in a single issue, it's going to be extremely confusing.
#18:
staticbecause they don't rely on$this: they're static to minimize dependencies and avoid unwittingly introducing new dependencies.Will look into this in my review.
Now reviewing #15 in detail. Thanks again for all this work, it's SUPER useful, and very much appreciated! 👏👏👏👏🙏🙏🙏
While I'm doing that, here is already a reroll that fixes the CS violation and should result in a green patch :)
Comment #26
ndobromirov commented#24
Paragraphs have 2 logical sub-types: layout ones and data ones.
Note that this are not Drupal bundles, it's just how we treat them.
- Data ones are only showing content.
- Layout ones are having a multi-valued reference field to data paragraphs. They can reference most / all of the data paragraphs. We run a default like a single column and the layout paragraphs allow us to have 2 and 3 column grids on a per paragraph entry.
Overall the content follows this structure:
Node > data paragraph -> Media -> File.
Node > Layout paragraph > data paragraph -> Media -> File.
Node > Layout paragraph > data paragraph -> Node -> media -> file
List of valid include paths for the particular entity resource. If we have that available before-hand, this can be reduced to a simplearray_diff. This is why I was focused on cache and cache merging initially in the patches before #15.#25
Already simplified includes list today. As I am running with the last patch (#15 now), it's not getting much faster, if at all, from the reduced includes in the validation step. I've managed to shed some of the deepest includes and this helps to an extent on the loading step after that. A separate issue that I will maybe bother you in near future... :]
Thanks for the positive feedback and I am waiting any further comments / questions :).
Comment #27
wim leersAttached is a reroll that fixes the last few CS failures (which
phpcsdoesn't show locally …)I don't see why this would be faster. It's just different.
BFS can be better (faster) than DFS when we're trying to actually … search for a particular value, and we know that the needle is less likely to be located deeply.
But in this case, there is no searching, there is only processing. We need to process all of the data (all of the include paths).
If I'm missing something: I'm looking forward to your explanation 🙂
This will not work for mixed-bundle collections. Yes, #2956414: Support mixed-bundle collections (e.g. `/jsonapi/node`) is not yet done, but mixed-bundle collections already occur today: when looking at the
relatedroute for a relationship on a resource, because an entity reference often allows linking to multiple bundles of a given entity type.For example:
field_related_contentmay link both toarticlenodes andtalknodes. With this change, that'd result?include=…only being evaluated for whichever resource comes first: anode--talkor anode--articleresource.I think the correct way to do this is to not run this just once, but once per resource type in
$data.I'm not opposed to this in principle, but … it does result in a vastly worse DX, because now the much more helpful errors that
\Drupal\jsonapi\Context\FieldResolver::resolveInternalIncludePath()throws that #2973681: Regression introduced by #2953207: Deep nested include on multi target entity type field fail introduced are no longer being created:I don't think this is an acceptable regression.
Comment #28
wim leersI think this simpler subset of this patch (which also fixes the bug I pointed out in #27.2) achieves the same performance improvement. Could you please test this? 🙏
Comment #29
wim leersAnd I think this is a more accurate description of the performance problems @ndobromirov reported here.
Comment #30
wim leersOh, and having written that clearer title, I think I suddenly understand why @ndobromirov was talking about BFS: if >1 independent include path resolutions end up inspecting the same subtree, we're doing duplicate work.
#28 only ensures we're not evaluating the same "tree root evaluations" multiple times. It doesn't yet ensure we're not evaluating the same "subtree evaluations" multiple times.
Still, I think the tiny patch in #28 will actually yield a very significant performance improvement already :) Let's first see if #28 brings the majority of the speed boost: if it does, that may be sufficient. If it doesn't, I think I see a simpler way to achieve the same performance benefit as @ndobromirov's patch with less complexity.
Comment #32
wim leersOne clarification in the IS: the concrete request URL that is triggering the reported problem. That makes it much clearer how complex the test scenario is 🙂
Comment #33
ndobromirov commentedThis is essentially same as the
break;approach.I will test this today...
We've already reduced the include lines to the bare minimum to keep same functionality, so I suspect the overhead to be minimal now. We dropped from 80+ default include lines to something like ~40 on all 5 content types so I suspect there will be a lowered overhead with this.
Comment #34
wim leers👍
👍 (Probably using the explanation I gave in #25, of not being able to simplify
?include=foo,foo.barto just?include=foo.bar?)Comment #35
ndobromirov commentedI can say it is resolving the overhead for the multiple items case to a big extent.
With my current includes setup that has drastically improved compared to earlier benchmarks the recursive overhead has dropped from 500ms to ~200ms. Note all the XHProf things you see above are with xdebug and xhprof running. Though on the dev servers we were having 11-12 seconds of response times even without the 2 extensions...
The changes in the includes are that the following 2 have merged into only
*.field_media_image, after the lattest commit in JSON:API.And the rest is just skipping the intermediary paths that were duplication like:
Current list of includes (functionally equivalent):
This is why I've made a black box test between the 2 approaches with xdebug and xhprof both disabled.
As both are equally solving the multiple entities handling in terms of call it only once (#28 is functionally correct...).
This is why I am testing only a single entity as essentially that's the same for this patch and reduce the normalization overhead.
All tests in this issue are done with no page dynamic or render cache.
Here are the benchmarks against the 2 (#15 & #28) patches on my local environment.
This is full request tests (AB) against a single entity. I am comparting Wim's patch from #28 and mine from #15.
There is a consistent 25-30% speed-up in the average / mean case and about 80-100% change in the worst case.
Wim's patch in #28 (ab -n 100 -c 1 http://HOSTNAME:81/api/taxonomy_term/sections/77d82ef5-3e45-4eb3-8490-85...)
Patch in #15 (ab -n 100 -c 1 http://HOSTNAME:81/api/taxonomy_term/sections/77d82ef5-3e45-4eb3-8490-85...)
Comment #36
ndobromirov commentedHere is the patch from 15 incorporating all the feedback from @Wim. (#28 + some code styles mentioned earlier).
This is the point where we need to decide are we introducing persistent cache on this or not. If we do - algorithm to implement the validation is not that important, as we will be validating only the non-cached paths and the DFS will be efficient to do that as it is. In that case, #3018287: ResourceTypeRepository computes ResourceType value objects on *every request* becomes a blocker.
If we steer away from the persistent cache, this might hit back in multi-resource type collections, as the overhead will be multiplied per resource type and we will be back here :).
Comment #37
wim leers#35: can you do a similar
abtest run without any patch applied? So that we can compare the numbers more clearly?Comment #38
ndobromirov commentedThis test with the AB was against a single entity, so patch in #28 should not add any meaningful overhead.
I will provide the same AB for a list of entities some time today.
Comment #39
ndobromirov commentedHere are the promised benchmarks.
No page, dynamic or render caches.
No xhprof or xdebug running.
No patch against list with 27 entities.
Wim's patch from #28. Same endpoint. Same data.
My patch from #36 (BFS + Wim's changes in #28).
Some thoughts...
Comment #40
wim leersGreat, thanks!
I personally don't think your patch's additional complexity justifies the small extra performance gain. We always need to balance maintainability and features/performance. This is even more true now that JSON:API has landed in Drupal core (this issue will soon become a Drupal core issue).
Would you be okay with #28 getting committed? Or do you have a very strong reason to say that you don't want #28 to go in, but #36 instead?
I'm curious to read @e0ipso and @gabesullice's thoughts :)
Comment #41
ndobromirov commented#40 I am +1 for 28 getting in, as it's a step in the right direction.
My only concern was that because I lacked knowledge on the jsonapi module and how includes worked I had a non-optimal configuration and I suspect this might be the case for many more people (not just me).
The main difference (ignore the 3% that may as well be 0) in there is that #36 does not degrade with non-optimal configs passed in for validation. If that is OK as a compromise on the module I can live with it.
At that point I will think of a follow-up to add persistent cache on the resource types for the valid list of includes and eliminate this overhead fully, once resource type definitions are loaded from cache. Intersect against the valid list and compute only the new ones. I think this can be a follow-up.
Comment #42
ndobromirov commentedPing.
Let's get this in, so we can start on caching it (eventually).
Comment #43
webchick@ndobromirov If you've tested the patch and it looks good to you (and it appears you have!), feel free to mark this issue's status "Reviewed & tested by the community." That will escalate it to the core maintainers' attention.
Comment #44
e0ipsoComment #45
ndobromirov commentedHere is a re-roll of #28.
I've been using it for the last month on over 60 sites without issues, and it helps a ton.
Any further speed-ups (based on cache) can be done in follow-ups.
If the tests pass, I'll be marking as RTBC as per recommendation from #43.
Comment #46
ndobromirov commentedShould I Re-Roll this against core?
Comment #47
ndobromirov commentedMoving to core's issue queue.
Rebased patch coming shortly.
Comment #48
ndobromirov commentedHere is the new patch against core.
It's the same as #45 / #28.
Comment #49
l0keStoring
$resource_object->getResourceType()in the variable will help to avoidgetResourceType()to be called twice.For the rest, it significantly improves performance.
Comment #50
ndobromirov commentedHere is a re-rolled version of the patch, implementing the proposal.
Note that there are not many gains expected in there, as there is already the internally cached property for the value.
Comment #51
l0keComment #52
alexpott@ndobromirov I'm confused in #50 you say that
Bt as far as I can work out in core we don't have any caching and the gains are still to had. Or are you referring to #3016733: Simplify ResourceTypeRepository; use a protected property in place of an in-memory cache bin.
Is there any chance we can repeat the tests in #39 so show that we have an improvement here? Thanks
Comment #53
ndobromirov commentedThis is only for return from #49 - the fact that we are sparing a single php function call with the latest patch to a function that has a lazy initialized property in it, and storing the result in a local variable is not that big of a performance win (if any).
I do not expect to have any visible change compared to #48. No time to do benchmarks on this now.
Back to RTBC, as you can pick #48 or #50, as for me they are essentially the same. Sorry for the confusion my comment triggered. The overhead that is spared by both of them are worth the commit.
Comment #54
alexpottCommitted and pushed c1b9f95417 to 8.8.x and df5f3e75f2 to 8.7.x. Thanks!
@ndobromirov thanks for #53
I've chosen to backport this to 8.7.x because it only changes internals and is a significant performance boost. On review I consider alternate strategies for reducing the array and only processing unique resource types but nothing seemed better than #50.
Comment #57
ndobromirov commentedAwesome! :D
Comment #58
rosinegrean commentedComment #59
wim leersRight, in #50 @ndobromirov was only saying that the changes in his interdiff for #50 should not result in significant gains, not that the entire patch would not result in significant gains :)
It's awesome to have this committed! 🥳🚀
And I think backporting this to 8.7 was the right call — disruption is guaranteed to be zero.