Problem/Motivation
In #3357969: For web server dependent unattended updates run the entire life cycle in a separate process that will not be affected by hosting time limits I am separating background updates from Automated Cron.
If that issue is success and gets committed we can just close this issue.
If we decide we cannot do #3357969 then some stuff I learned there still made me realize that we missing test coverage. In that issue I am trying to do updates in Kernel Terminate event, which is what Automated Cron uses and it seems to be very tricky. This made realize we don't actually test using automated cron. We test by clicking "Run cron" which does not run the update during the kernel terminate event.
Steps to reproduce
Proposed resolution
If #3357969: For web server dependent unattended updates run the entire life cycle in a separate process that will not be affected by hosting time limits get committed close this issue as we will not be using cron.
If that issue does not get committed create the test coverage.
Remaining tasks
User interface changes
API changes
Data model changes
Issue fork automatic_updates-3362978
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
tedbowChanging to major because if end up still relying on cron this major missing test coverage
Comment #4
tedbowun-Postponing because I have a test that think proves it does not work.
Not sure why yet but I think #3357969: For web server dependent unattended updates run the entire life cycle in a separate process that will not be affected by hosting time limits will be affected also unless we don't switch to the anonymous user when performing the update which is what Cron does.
I think it is combo of
\Drupal\Core\Cronswitching to the anonymous user and\Drupal\automated_cron\EventSubscriber\AutomatedCronrunning inKernelEvents::TERMINATEthis core issue may be related #3181946: ReplicaKillSwitch unnecessarily starts a session on each request
Comment #5
tedbowI tried the patch in #3181946 and did not fix it
Comment #6
wim leersComment #7
wim leersI went very deep in debugging this. Could not figure it out so far
https://symfony.com/doc/current/components/http_kernel.html#component-ht... does not indicate this should not work.
Eventually, after ~1.5h of debugging at ever deeper levels, I tried to find existing test coverage for Automated Cron to see if that at least works. There's only one test:
\Drupal\Tests\system\Functional\System\CronRunTest::testAutomatedCron()Turns out that ALSO FAILS 🤪
👆 That does not execute cron at all!
Comment #8
wim leersI previously debugged this problem >4 years ago: #2941687-19: Subrequests in an KernelEvents::TERMINATE event subscriber (f.e. cron tasks doing subrequests) try to restart a session, resulting in runtime exceptions. 😬🤣
Comment #9
wim leersAfter a lot of debugging, the culprit is:
in
automatic_updates_cron(). During construction of that service, it calls\Drupal\Core\TempStore\SharedTempStoreFactory::get(), which in turn eventually calls\Drupal\Core\TempStore\SharedTempStore::ensureAnonymousSession().That is what triggers the session.
We can fix that by adding an optional
?string $owner = NULLtoStageBaseandUpdateStage, and havingCronUpdateStagehardcode that to'cron'as the owner.BUT! Fixing that is not enough!
Because
automatic_updates_cron()also does:… which happens to need these services during construction:
… we still end up calling
UpdateStage's construction, which still has a NULL owner, and hence still starts the session 🙃P.S.: note that
\Drupal\Core\Cron::run()specifically has logic around this:Comment #10
wim leersSame exact test failure output.
But apply
and you'll see that the problem went from
change to
The first difference (earliest in the stack):
→ changes from line 173 to line 177, in a file which my commit
a620b00fdidn't touch!Line 173:
Line 177:
👆 This proves that I did fix the original problem!
Let's see if I can fix this problem too…
Comment #11
wim leershook_modules_installed()triggered the above problem too. Worked around that in 488dac69.Comment #12
wim leersAfter those 3 commits, I now have an actually meaningful error message in the output:
… which makes sense because that's what
\Drupal\Tests\automatic_updates\Functional\AutomaticUpdatesFunctionalTestBase::installModulesFromClassProperty()pretends is the latest security release …… which AFAICT means this test was wrong all along? 😅 But it still revealed genuine bugs though!
The reason behind commit 030809aa:
Even if one disables the whole status checker thing (as shown in #10 to definitely cause some problems), it STILL fails. Because
\Drupal\package_manager\StageBase::create()is doing:which means we AGAIN call
\Drupal\Core\TempStore\SharedTempStoreFactory::get()without$owner.Comment #13
wim leersI've run out of steam.
Notes of what I have not been able to figure out:
index.phpin the request (</code>) that <code>\Drupal\Tests\automatic_updates\Functional\AutomatedCronIntegrationTest::testAutomatedCronUpdate()makes prior to performing its assertionsSymfony\Component\Validator\Exception\InvalidOptionsException: The options "requiredKeys" do not exist in constraint "Symfony\Component\Validator\Constraints\Type".in\Drupal\update\ProjectRelease::validateReleaseData()😳 (I've added an early return for now)automatic_updates_cron()— it must somehow be crashing earlier, but … I literally can't see it. Yet$this->drupalGet('');does return a working page. Although I will say that it appears to be hitting the Dynamic Page cache.P.S.: I already tried logic similar to what
\Drupal\Core\Cron::run()does:but no dice either.
Comment #14
tedbowre #13.3
It this might be because
$this->drupalGet('')will be return when the response is returned from the server but only after that does the Kernel Terminate event get fired. So I think if you are debugging it will hit the line afterdrupalGetwhile cron is happening in kernel terminate.I am not sure if this is true in functional tests but I think this is true for build tests.
I am guessing this is why
\Drupal\Tests\system\Functional\System\CronRunTest::testAutomatedCrondoesThough this might not work for us since our operation make take longer.
In a branch I was using to debug #3360656: For web cron updates run each stage life cycle phase in a different request where I first found the kernel terminate problem I actually created a
<a href="https://git.drupalcode.org/issue/automatic_updates-3360656/-/tree/3360656-split-requests_2debug">visitAndWaitForTerminate</a>function that worked with a test module that would write a file at beginning of the request and delete when the background update operation had finished.this was crude but I couldn't think of another way to test this and I needed to figure out what was going on
Comment #15
tedbowre #14 I pushed a change module that allows test to wait on cron update in kernel terminate
Re #9
did this actually fix it? That is not what I am seeing. Checked out Allow specifying the owner of the stage to avoid triggering a session in cron/CLI contexts. was actually because
\Drupal\automatic_updates\CronUpdateStage::handleCrondidn't find a$next_releaseComment #16
wim leers"Fix" in the sense that no new session was triggered anymore.
The problem you're reporting is in line with what I reported at the start of #12 — i.e. that the test itself is wrong.
Comment #17
tedbowGoing to say we won't fix this at least until we first try #3357969: For web server dependent unattended updates run the entire life cycle in a separate process that will not be affected by hosting time limits
that method probably has advantages of using Automated Cron even if we could