Add option to disable Linkchecker (esp. entity hooks)
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3581562. -->
Reported by: [anybody](https://www.drupal.org/user/291091)
Related to !156
>>>
<h3 id="summary-problem-motivation">Problem/Motivation</h3>
<p>We're using lovely Linkchecker in a large project and just had to run a batch update on many entities. We were wondering why it became <em>extremely</em> slow and saw MANY Linkchecker queries causing this.</p>
<p>I tried <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/linkchecker/-/work_items/3313343" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/linkchecker/-/work_items/3313343</a></span>, but then had to find out that it's not a cron thing, but Linkchecker also uses <code>hook_entity_*</code> a lot:</p>
<div class="codeblock">
<pre><span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #FF8000">/**<br> * Implements hook_cron().<br> */<br></span><span style="color: #007700">function </span><span style="color: #0000BB">linkchecker_cron</span><span style="color: #007700">() {<br> if (\</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">config</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.settings'</span><span style="color: #007700">)-></span><span style="color: #0000BB">get</span><span style="color: #007700">(</span><span style="color: #DD0000">'check.disable_cron'</span><span style="color: #007700">)) {<br> return;<br> }<br> \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.extractor_batch'</span><span style="color: #007700">)-></span><span style="color: #0000BB">processEntities</span><span style="color: #007700">();<br> \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.checker'</span><span style="color: #007700">)-></span><span style="color: #0000BB">queueLinks</span><span style="color: #007700">();<br>}<br><br></span><span style="color: #FF8000">/**<br> * Implements hook_entity_insert().<br> */<br></span><span style="color: #007700">function </span><span style="color: #0000BB">linkchecker_entity_insert</span><span style="color: #007700">(</span><span style="color: #0000BB">EntityInterface $entity</span><span style="color: #007700">) {<br> if (</span><span style="color: #0000BB">$entity </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">LinkCheckerLinkInterface</span><span style="color: #007700">) {<br> return;<br> }<br><br> if (</span><span style="color: #0000BB">$entity </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">FieldableEntityInterface </span><span style="color: #007700">&& !</span><span style="color: #0000BB">$entity</span><span style="color: #007700">-></span><span style="color: #0000BB">isNew</span><span style="color: #007700">()) {<br> </span><span style="color: #FF8000">/** @var \Drupal\linkchecker\LinkExtractorService $extractor */<br> </span><span style="color: #0000BB">$extractor </span><span style="color: #007700">= \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.extractor'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$links </span><span style="color: #007700">= </span><span style="color: #0000BB">$extractor</span><span style="color: #007700">-></span><span style="color: #0000BB">extractFromEntity</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br><br> if (!empty(</span><span style="color: #0000BB">$links</span><span style="color: #007700">)) {<br> </span><span style="color: #0000BB">$extractor</span><span style="color: #007700">-></span><span style="color: #0000BB">saveLinkMultiple</span><span style="color: #007700">(</span><span style="color: #0000BB">$links</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$extractor</span><span style="color: #007700">-></span><span style="color: #0000BB">updateEntityExtractIndex</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br> }<br><br> \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.clean_up'</span><span style="color: #007700">)-></span><span style="color: #0000BB">cleanUpForEntity</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br> }<br>}<br><br></span><span style="color: #FF8000">/**<br> * Implements hook_entity_update().<br> */<br></span><span style="color: #007700">function </span><span style="color: #0000BB">linkchecker_entity_update</span><span style="color: #007700">(</span><span style="color: #0000BB">EntityInterface $entity</span><span style="color: #007700">) {<br> if (</span><span style="color: #0000BB">$entity </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">LinkCheckerLinkInterface</span><span style="color: #007700">) {<br> return;<br> }<br><br> if (</span><span style="color: #0000BB">$entity </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">FieldableEntityInterface</span><span style="color: #007700">) {<br> </span><span style="color: #FF8000">/** @var \Drupal\linkchecker\LinkExtractorService $extractor */<br> </span><span style="color: #0000BB">$extractor </span><span style="color: #007700">= \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.extractor'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$links </span><span style="color: #007700">= </span><span style="color: #0000BB">$extractor</span><span style="color: #007700">-></span><span style="color: #0000BB">extractFromEntity</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br><br> if (!empty(</span><span style="color: #0000BB">$links</span><span style="color: #007700">)) {<br> </span><span style="color: #0000BB">$extractor</span><span style="color: #007700">-></span><span style="color: #0000BB">saveLinkMultiple</span><span style="color: #007700">(</span><span style="color: #0000BB">$links</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">$extractor</span><span style="color: #007700">-></span><span style="color: #0000BB">updateEntityExtractIndex</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br> }<br><br> \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.clean_up'</span><span style="color: #007700">)-></span><span style="color: #0000BB">cleanUpForEntity</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br> }<br>}<br><br></span><span style="color: #FF8000">/**<br> * Implements hook_entity_delete().<br> */<br></span><span style="color: #007700">function </span><span style="color: #0000BB">linkchecker_entity_delete</span><span style="color: #007700">(</span><span style="color: #0000BB">EntityInterface $entity</span><span style="color: #007700">) {<br> if (</span><span style="color: #0000BB">$entity </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">LinkCheckerLinkInterface</span><span style="color: #007700">) {<br> \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.clean_up'</span><span style="color: #007700">)-></span><span style="color: #0000BB">cleanUpQueues</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">-></span><span style="color: #0000BB">id</span><span style="color: #007700">());<br> return;<br> }<br><br> if (</span><span style="color: #0000BB">$entity </span><span style="color: #007700">instanceof </span><span style="color: #0000BB">FieldableEntityInterface</span><span style="color: #007700">) {<br> \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">service</span><span style="color: #007700">(</span><span style="color: #DD0000">'linkchecker.clean_up'</span><span style="color: #007700">)-></span><span style="color: #0000BB">cleanUpForEntity</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br> }<br>}<br></span><span style="color: #0000BB">?></span></span></pre></div>
<p>As Drupal 8+ has no "disable module" functionality, there's no way to disable it. Uninstalling removes configuration and all LinkChecker entities.</p>
<p>So it would be great to have a simple setting to disable linkchecker (for a while)!</p>
<p>Disabling link checker on cron run should still be a separate setting (<span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/linkchecker/-/work_items/3313343" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/linkchecker/-/work_items/3313343</a></span>) as the use-cases for both are different.</p>
<h2>Workaround</h2>
<p>As a workaround I just commented out these hooks now, but that's not a nice user-facing solution. After doing that, the batch ran fast again!</p>
<h4 id="summary-steps-reproduce">Steps to reproduce</h4>
<h3 id="summary-proposed-resolution">Proposed resolution</h3>
<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<h3 id="summary-ui-changes">User interface changes</h3>
<h3 id="summary-api-changes">API changes</h3>
<h3 id="summary-data-model-changes">Data model changes</h3>
issue
GitLab AI Context
Project: project/linkchecker
Instance: https://git.drupalcode.org
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://git.drupalcode.org/project/linkchecker/-/raw/2.1.x/README.md — project overview and setup
Repository: https://git.drupalcode.org/project/linkchecker
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD