Problem/Motivation

Child issue of #3087218: Help searches fail if site is not fully indexed, and users do not know why

Many modules have cron jobs. For example, Search uses cron to index content, which sometimes takes multiple cron runs. When modules/themes are installed, there could be additional content to index, or other reasons to run cron.

If you're using the Automatic Cron module, you would expect it should run soon after installing a module, or you might expect your new content to get indexed (in the case of help topics being added), but it may not happen for several hours and it might take multiple cron runs to finish indexing.

Steps to reproduce

Install the Help Topics and Search modules. Try to search topics from admin/help. You will not see any results until cron has run, and you won't get complete search results until after several cron runs.

Proposed resolution

Make the Automated Cron module run cron not only after a certain interval has passed, but also if it detects that it needs to be run. Set the flag for this when any module/theme is installed.

To complete search indexing, it would be helpful if Search reset this flag if the index is not complete after an indexing run. This part has been moved to a separate issue to discuss. #3197264: Node searches fail if index is not complete, and users do not know why

Remaining tasks

Make a patch and test it.

User interface changes

Things depending on cron will have a better chance of working correctly sooner, after module/theme installation.

API changes

Not really.

Data model changes

No.

Release notes snippet

Probably not necessary.

Comments

jhodgdon created an issue. See original summary.

cilefen’s picture

I may be overreacting, but my gut reaction is as follows. I don't have an expectation that new cron jobs should jump the queue, although I understand that others may feel differently. However, I object to something that resets the last cron execution time to accomplish it on the grounds that altering the last execution time could have weird side effects, for example, on jobs that a site owner could reasonably expect to occur at certain times.

jhodgdon’s picture

Issue summary: View changes

This only affects the Automatic Cron module, which doesn't have definite times for running cron, but rather checks to see (on page loads) whether enough time has passed that cron should be run again.

Jobs that need to be run at a certain time or at a certain interval should not be relying on Automatic Cron to accomplish that. Most likely cron should be set up from an outside server rather than relying on the Automatic Cron module to do its thing at any particular time. This patch would not affect outside cron schedulers in any way.

jhodgdon’s picture

Component: extension system » cron system
Status: Active » Needs review
StatusFileSize
new674 bytes
new1.31 KB
new1.04 KB

@andypost made a patch early on, on the parent issue. It takes care of the module part. Uploading that here.

We also need to do themes, so here's a patch that does both. The interdiff is almost as big as the (small) patch, but uploaded it anyway.

andypost’s picture

Looks it needs basic test to rtbc

jhodgdon’s picture

Status: Needs review » Needs work

Probably a good idea.

jhodgdon’s picture

Status: Needs work » Needs review
StatusFileSize
new3.2 KB
new2.16 KB

Here's a new patch that adds a test (passes locally, and is similar to the other parts of the existing test in how it determines cron is triggered).

andypost’s picture

Great, just not clear why sleep(1); is used for...
As a bug it could use test-only patch

jhodgdon’s picture

That is used throughout this test file. What it does is ensure that the cron timestamp increases by at least 1 second when cron is run.

So let's say the time of last cron run is 10:00:00. Then you sleep for 1 second in the test and run cron. You are guaranteed the cron run time is at least 10:00:01. If you don't sleep for 1 second, cron might run again at 10:00:00 (if execution time is less than 1 second), or it might be 10:00:01 or later. That would make the test have random fails, because the way it is checking for a cron run is to see if the timestamp of last cron run has increased over what it was before.

There are probably better ways to do this, but that is what the existing test was already doing, and the new lines follow the same philosophy.

andypost’s picture

StatusFileSize
new3.52 KB
new2.4 KB
new3.45 KB

Here's patch with removal of sleep() and exact testing of that hooks are fired

I thought to put it around \Drupal\Tests\system\Kernel\System\InfoAlterTest but that just increase code fragmentation (automated cron tests living in system module)

andypost’s picture

StatusFileSize
new1.28 KB
new2.17 KB

I still think it should be part of system module, so here's
- hooks moved to system module where the state is maintained
- test converted to kernel test

no interdiff as it bigger then patch

andypost’s picture

+++ b/core/modules/system/system.module
@@ -1304,3 +1304,21 @@ function system_theme_registry_alter(array &$theme_registry) {
+function system_modules_installed() {
+  // Reset last cron run to allow new modules to execute scheduled tasks.
+  \Drupal::state()->delete('system.cron_last');
+}
...
+function system_themes_installed() {
...
+  system_modules_installed();

probably here better to not call install hook implementation

andypost’s picture

StatusFileSize
new543 bytes
new2.19 KB

Fix #12

The last submitted patch, 10: 3194120-test-only-10.patch, failed testing. View results

The last submitted patch, 11: 3194120-test-only-11.patch, failed testing. View results

jhodgdon’s picture

Status: Needs review » Needs work

I am not sure I agree that this belongs in the system module. The system module doesn't care about this time stamp. The only places that get/set this state variable outside of tests are:

// This is in function system_requirements(), for the Status report.
system/system.install:    $cron_last = \Drupal::state()->get('system.cron_last');

// This is on the Cron admin page, to display when it was last run.
system/src/Form/CronForm.php:    $status = '<p>' . $this->t('Last run: %time ago.', ['%time' => $this->dateFormatter->formatTimeDiffSince($this->state->get('system.cron_last'))]) . '</p>';

// This is Automated Cron deciding if it's time to run cron.
automated_cron/src/EventSubscriber/AutomatedCron.php:      $cron_next = $this->state->get('system.cron_last', 0) + $interval;

// This is the core Cron system setting the variable during cron.
lib/Drupal/Core/Cron.php:    $this->state->set('system.cron_last', $request_time);

So, the system module only cares about this state variable for displaying it on the status report and on the Cron admin page. The Automated Cron moduel is the only user of the variable for making decisions, and if you don't have the Automated Cron module installed, you shouldn't reset this variable when modules are installed, in my opinion. For purposes of status report, it is better if it accurately reports that cron was last run at the actual time (that is the functionality in the System module). Most sites will have it running from an external source anyway.

I think we should go back to the patch on #7. Fixing the existing test should be a separate issue (definitely out of scope for this issue).

andypost’s picture

Status: Needs work » Needs review

lib/Drupal/Core/Cron.php: $this->state->set('system.cron_last', $request_time);
is where the state is changed, so any contrib/custom code could use cron service and use the state, see http://grep.xnddx.ru/search?text=system.cron_last

mjpa’s picture

If this is only affecting automated cron, wouldn't it be better to have a state flag that tells automated cron to run anyway?

I agree with @jhodgdon that the system report should still repot the last time cron ran so having a flag that automated cron sets when modules/themes are installed, and then it'd run cron if the flag is set, or enough time has elapsed since the last cron ran.

Slight nit pick with the patch in #7 in that I wouldn't have the hook_themes_installed call the automated_cron_modules_installed() function directly in case (for some reason that I can't think of!) more code is added to automated_cron_modules_installed() that shouldn't be executed if a theme is installed.

jhodgdon’s picture

Status: Needs review » Needs work

I agree with @mjpa here -- that is an excellent idea actually. So the existing state variable would continue to keep track of the last time cron was actually run, so it can be displayed in various places. and we wouldn't delete that time. But we would have a new state variable that any module/theme/etc. could set saying "We need to run cron ASAP", and the Cron.php class could reset it after a cron run is complete. Or better yet at the beginning of a cron run, so that perhaps things like Search could set it again after its cron if there is still stuff to index, so it would run again sooner?

And I am coming around to the idea that this new state variable and hooks for module/theme install should be in the System module and not automated cron. Still think we need a different patch. If we have this new state variable, we'll need to add to the tests that I patched in #7 to verify automatic cron triggers off this state variable.

jhodgdon’s picture

Title: Cron time stamp should be reset when any module/theme is installed » Need a way to say it's time to run cron
Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new5.7 KB

OK, here's a new patch. Interdiff is not useful as pretty much every line in the patch is different from the last one. Updating title and summary.

This patch:
- Introduces and documents a new state variable for "cron run is needed".
- Sets this when any module or theme is installed.
- Sets this in search_cron if indexing is not complete.
- Checks this in Automated Cron to decide if it's time to run cron.
- Tests the above.

If we decide we like this patch, we should probably make a Change Record about the new state variable.

andypost’s picture

Issue tags: +Needs change record

Awesome 👍 That needs few words about new flag I guess

++To approach!

$this->state->set('system.cron_needed', FALSE);
Please do not store false in database, that's why I used to delete this "semaphore"

jhodgdon’s picture

StatusFileSize
new5.7 KB
new535 bytes

OK, how's this? And I'll make a change record after I upload this patch.

jhodgdon’s picture

andypost’s picture

Exactly that, thank you!

One mire nit It is reset to FALSE at the start of a cron run. needs to paraphrase to missing state instead of false

jhodgdon’s picture

StatusFileSize
new5.72 KB
new880 bytes

Good point.

andypost’s picture

StatusFileSize
new923 bytes
new5.93 KB

Just a minor clean-up and nit

+++ b/core/lib/Drupal/Core/Cron.php
@@ -133,6 +142,9 @@ public function run() {
+      // Reset the cron needed state variable, so it can be reset during
+      // cron run if another run is needed.
+      $this->state->delete('system.cron_needed');
       $this->invokeCronHandlers();

I guess `can be reset` should be `can be set during run`

jhodgdon’s picture

StatusFileSize
new5.92 KB

The interdiff looks good to me. I like your idea of updated comment too. Actually it should probably be this:

+      // Delete the cron needed state variable, so it can be set during
+      // cron run if another run is needed.

I edited these two lines in the patch file. Interdiff is: these two lines in place of what andypost showed in previous comment. :)

From my point of view, this is RTBC. Probably we need to get a reviewer who didn't write these patches... @mjpa perhaps, please?

The last submitted patch, 26: 3194120-25.patch, failed testing. View results

andypost’s picture

Status: Needs review » Reviewed & tested by the community

I think it's ok to RTBC by me as approach by mjpa who needs to be added to commit credit

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

I have a concern about the approach. Since search_cron sets the cron_needed flag nearly every request could end up running cron if there is a lot of content to index. I'm not sure that's a desired behaviour.

jhodgdon’s picture

It seems to me it is more desirable to get the content indexed, than to wait for days potentially while it is not indexed. If someone is actually using core Node Search, wouldn't they want it to work?

jhodgdon’s picture

I think we need the feedback of Product Manager here to decide on the approach. The question is this, given the issue summary:

Should the Search module set this new flag after a cron run, if there is still content to index, to indicate to the Automated Cron run that another cron run is warranted?

Pro: The search index will be completed sooner, so searches will be working correctly sooner.
Con: Automated Cron will run more often.

andypost’s picture

++ to get PM POV, meantime linked the issue to commerce recurring issue, it also wins if this flag will appear in core. Ref #2931290: Multiple recurring orders get created if cron is badly configured

As a bag-o-feature it may also help contrib that trying to clean-up "orphan files/media" but it brings division cron becomes more like queue

catch’s picture

It seems to me it is more desirable to get the content indexed, than to wait for days potentially while it is not indexed. If someone is actually using core Node Search, wouldn't they want it to work?

Yes but they also need the rest of their site to work, which is why we set the default automated cron to three hours and encourage people to set up a regular cron job. Some sites have both a regular cron job and automated cron - which will never run if cron runs every 5 minutes, but it's a fallback in case the regular cron stops for some reason. With this change, you could end up with automated cron running all the time, which at the least will potentially tied up an apache process.

I do think it's a good idea for module install/uninstall though.

jhodgdon’s picture

StatusFileSize
new5.1 KB

OK, here's a patch without the search.module part. Interdiff is: no search.module changes, which were:

--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -117,8 +117,14 @@ function search_preprocess_block(&$variables) {
 function search_cron() {
   /** @var $search_page_repository \Drupal\search\SearchPageRepositoryInterface */
   $search_page_repository = \Drupal::service('search.search_page_repository');
+  $state = \Drupal::state();
   foreach ($search_page_repository->getIndexableSearchPages() as $entity) {
-    $entity->getPlugin()->updateIndex();
+    $plugin = $entity->getPlugin();
+    $plugin->updateIndex();
+    $status = $plugin->indexStatus();
+    if ($status['remaining'] > 0) {
+      $state->set('system.cron_needed', TRUE);
+    }
   }
 }
andypost’s picture

I recall a "throttle" core module in early days, as it gone every module forced to throttle execution (aggregator, update, locale,...)

All modules share cron hook so extra api to balance throttling would be great to adopt for help topics in blocked issue

gauravvvv’s picture

StatusFileSize
new63.15 KB

Patch #35 applied cleanly. Moving to RTBC +1

gauravvvv’s picture

Status: Needs review » Reviewed & tested by the community
gauravvvv’s picture

gauravvvv’s picture

Status: Reviewed & tested by the community » Needs review
mjpa’s picture

Patch in #35 looks fine to me.

On the topic of the search module setting the flag each cron run if it's not fully indexed, only 1 request can run the automated cron, and once it's running, no others will run the automated cron. So at worst, you lose a single process that can serve the site. That shouldn't stop a site working, unless the server only serves 1 request at a time. Yes, it could stop people browsing the site if the site is busy as it's 1 less useable process, but then the site is already slow anyway.

The only issue with the patch from #26 I can think of, would be if indexing some content consistently fails, so you end up running cron all the time...

jhodgdon’s picture

Issue summary: View changes
Issue tags: -Needs product manager review

Let's move the Search module question to a follow-up, and put the latest patch here back to RTBC since I think that part is not controversial.
#3197264: Node searches fail if index is not complete, and users do not know why

jhodgdon’s picture

Status: Needs review » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/system/system.module
@@ -1304,3 +1304,19 @@ function system_theme_registry_alter(array &$theme_registry) {
+/**
+ * Implements hook_modules_installed().
+ */
+function system_modules_installed() {
+  // Set flag saying cron needs to be run.
+  \Drupal::state()->set('system.cron_needed', TRUE);
+}

Do we need this flag in system module?

The reason we use this flag is for automated cron only. Imo we could implement this better if we listened to these events in the automated cron module and set something on the event. To run the automated cron. This would have the additional benefit that you know for sure that it is the request that has installed the module that has run cron - and therefore has the correct container. With the patch as it currently stands it is theoretically possible that a slow request could end up running cron with a container built before the module / theme was installed.

jhodgdon’s picture

Status: Needs work » Reviewed & tested by the community

We've been discussing that exact question... The consensus among people on this issue was it should be in the system module, because the system cron system is what is going to set it back to empty during the cron run. Also it could theoretically be detected by other systems running cron, not just the Automated Cron module.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

@jhodgdon the system module does not need to be involved at all and we should not use state because of the container reason I gave in #44

jhodgdon’s picture

OK. It's time for someone else to make a patch then. I am not sure what to do here.

alexpott’s picture

Here's what needs to be done:

  • Move the hooks to automated_cron
  • Remove the use of state
  • Add a new public method to \Drupal\automated_cron\EventSubscriber\AutomatedCron - ::forceCronRun() - which sets a protected property $this->forceCronRun to TRUE.
  • In \Drupal\automated_cron\EventSubscriber\AutomatedCron::onTerminate() run cron if $this->forceCronRun === TRUE.

Alternatively, we could consider running cron when a module or theme is installed. We'd need to determine if this should be via the API (ModuleInstaller / ThemeInstaller) or only via the UI.

I do partially feel that this is the wrong solution for the problem. I feel like if an admin does a search and any of the search indexes involved in the search are not at 100% they should get a message about the search indexes not being complete. I think trying to automatically run cron in these situations is bound to end up triggering edge cases that are hard to debug - for example, some static cache that is not tied to the container and is incorrect after module installation but is correct on the next request.

jhodgdon’s picture

Hm... Maybe this issue should just be a Won't Fix then. On #3087218: Help searches fail if site is not fully indexed, and users do not know why, we did what you suggested -- put out a warning for Help Search if the index is not complete and someone searches. On that issue, we decided that we probably should not do that for Node Search, but we could repurpose the child issue I created to consider setting up a configuration variable for whether/when to do it for Node Search.

The problem with node search is on a large site with active commenting and content editing, the search index will practically never be 100% complete, so you'd get the warning all the time. Help search is another question -- there the topics are relatively stable, and it's only admins seeing the message, which is why #3087218: Help searches fail if site is not fully indexed, and users do not know why is only for help search.

So here's what I propose:
a) Close this issue as Won't Fix.
b) Repurpose #3197264: Node searches fail if index is not complete, and users do not know why to be an issue about Node Search only, to configure whether and when to put out a warning to end users if indexing is incomplete.

Thoughts?

andypost’s picture

Status: Needs work » Needs review

protected property $this->forceCronRun to TRUE.

It will not work because this state is not stored between requests and as #45 states it orthogonal to automated cron - it's about ability to react on any event and request cron time for something that any extension may need - content re-index is just one of cases.

Moreover cron is core service so only possible namespace for cron state is system module

@alexpott if automated cron is not enabled help_topics's search or any other module needs ability to say "run cron to make me working after install"

alexpott’s picture

@andypost well solution in #35 only works via automated cron... and it will work when a module is enabled via the UI. Maybe not drush because the request terminate will not fire but then #35 wouldn't work for drush because it opens up the possibility of cron being run with the wrong container...

Also "it's about ability to react on any event" - this is highly suspicious. I don't think we should have the ability to react on any event and force a cron run. I think cron should either be run by cron (or a cron like thing - hence automated cron) or by user interaction saying run cron. There might be a case for on module/theme install but I think that that is quite fraught for the reasons already outlined.

Re #49 - I think a message for privileged users on search when a search index is incomplete would be a great addition. I think this is one of the things that @catch asked for in https://www.drupal.org/project/drupal/issues/3087218#comment-13972153

Given the above I would close this as won't fix.

jhodgdon’s picture

Status: Needs review » Closed (won't fix)

Given the opposition from committers on this one, I don't think we can move forward on this issue as it is. I've fixed up #3197264: Node searches fail if index is not complete, and users do not know why so it's about putting out a message if the index isn't complete, which I think is kind of controversial for Node search. On #3087218: Help searches fail if site is not fully indexed, and users do not know why we have a patch to do that for Help search, which I think is less controversial (RTBC...).