Problem/Motivation
There are three different situation when links are extracted from entities:
1. One way is when users are creating entities organically, one after the other. This is when the linkchecker_entity_insert and linkchecker_entity_update hooks are extracting the links from the new or updated entities.
2. The second one is, when some cleanup was done for an entity, and \Drupal\linkchecker\LinkCleanUp::cleanUpForEntity was invoked. This is when the entity was updated or deleted. (FYI, in case of an update, LinkExtractorService::extractFromEntity is invoked twice, with the same entity argument. I'd consider to cache the results somehow to reduce computing time.)
3. The third case is, when LinkExtractorBatch is processing all entities after install or a config update. (Can be the Drush command.)
There is a main difference between these three cases. In the cases I described in (1) and (2), the actually saved language version is what we can find in the $entity variable. So, it is unnecessary to check every other translations in LinkExtractorService::extractFromEntity. Other translations only need a re-check if some non-translatable fields were changed (hasTranslationChanges()), or if the translation is new.
Only in the third, last case, makes it any sense to check every translation.
Proposed resolution
Refactor LinkExtractorService to perform only the necessary check. When extraction is done after entity save (so, entity original is available), perform the check only if the corresponding language version AND the field value was changed.
Remove the ->isNew() check from the entity insert implementation. When entity_insert hooks are invoked, the new flag is already reset by the storage. So it always will be false.
Simplify the loops at LinkExtractorService::extractFromEntity. Check $skip_unpublished = $this->linkcheckerSetting->get('search_published_contents_only'); outside of the loops. Load the same entity translation only once.
Add a second argument to LinkExtractorService::extractFromEntity; "only_check_changed". Defaults to FALSE. Then, when checking fields and the translations of fields, only perform extraction when 1. the actual translation has affecting changes AND 2. the actually checked field has changes.
In the entity insert and update hooks, use this argument as TRUE.
Rethink how LinkCleanUp::cleanupForEntity works. When only a translation was deleted, clean up the index and links using the entity type, ID and the language code. When there was a change, delete the obsolete link records based on the url hash. Also, call LinkExtractorService::extractFromEntity by using the new second argument or leave a comment why we need a full scan.
Remaining tasks
User interface changes
Nothing.
API changes
New, optional method argument at LinkExtractorService::extractFromEntity.
Data model changes
Nothing.
Issue fork linkchecker-3616460
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
claudiu.cristeaComment #6
yaqbick commentedMR https://git.drupalcode.org/project/linkchecker/-/merge_requests/167 contains code from https://git.drupalcode.org/project/linkchecker/-/merge_requests/157