Problem/Motivation
On a current project this module shows in NewRelic as the most time-consumig.
It's related to the fallback behaviors, apparently abusing the entity cache system and other internals (on initial high level review).
More information will be provided once I have it.
More context on the issue:
We have many languages (6-8) and somewhat long fallback chains on some of them (4-5).
I suspect that due to the chain things are loaded one by one and thus causing delays. Maybe we could change that to load translations
multiple (maybe).
On a cron there are some node loading and translations loading related to Search API indexing and 3rd party data synchronizations.
It is clearly seen that the slow-downs are during the heavy processes doing multitude of entity load operations, and the biggest overhead in that calls comes from the cache gets (as a % portion of the requests).
We need to find what exactly is causing it.
Any ideas on your side will be REALLY apreciated :).
Proposed resolution
Benchmark, profile and see where the cache abuse is coming from.
Mitigate the issue by either a new cache layer or code change (details are TBD).
Remaining tasks
Find the root cause, discussion, patch.
User interface changes
None expected.
API changes
TBD.
Data model changes
TBD.
Release notes snippet
TBD.
| Comment | File | Size | Author |
|---|---|---|---|
| #7 | interdiff-3058394-4-7.txt | 1.07 KB | ndobromirov |
| #7 | 3058394-7.patch | 6.03 KB | ndobromirov |
Comments
Comment #2
ndobromirov commentedComment #3
ndobromirov commentedBiggest overhead I've found (from NewRelic) is the
hook_language_fallback_candidates_alter.The only module that implements it is this one, so I've checked more what it is doing...
There are some issues that I was able to find.
This alter hook is triggered and it is generating the list every time when an entity needs to be translated. The thing is the set of fallbacks are generated from configuration every time (per entity).
In reality all of that is changing per entity bundle (not entity instance). In reality all of that can be cached, so in heavy translation scenarios we get, this list can be preserved in the long run. Maybe a persistent second level cache could help as well.
I think I've found the issue in my scenario - we have around 100 000 iterations related to translations of and to have heavy processing over them and prevent out of memory errors the static cache for the bundle is disabled during that list processing.
I suspect this code path is somewhat protected from entity static cache, but once that is disabled, the overhead is exposed (after some 100k iterations)..
I have a PoC patch that will be posted in near future for review and further discussions.
Comment #4
ndobromirov commentedHere is the promised patch...
There is a new utility method on the controller that moves the alter code there.
There is a in-property cache for the values that will cache all of the things there per bundle, so we skip executions per entity.
Change in the interface, as there is a new method added.
Comment #5
ndobromirov commentedI will try to have benchmarks available at near future as well, so we can see the performance difference this will make in 10k+ iterations.
Comment #6
ndobromirov commentedI've managed to force the hook to be executed on a node with this snippet.
Time was aggregated with
microtime()in the alter hook's code.I am seeing a 40-50% speed-up on my simple local setup with 3 languages only.
Comment #7
ndobromirov commentedHere is a patch with the discussed changes.
Comment #9
valthebaldCommitted to HEAD. Thank you!
Comment #11
adamschan commentedI believe this patch created the regression I mentioned at https://www.drupal.org/project/entity_language_fallback/issues/3085077
Should not commit.
Comment #12
valthebald@adamschan: thanks for the detailed report, looking into it