Problem/Motivation

#3036494: Race condition in ImageStyle::createDerivative() partially fixed race conditions when generating image styles. However it did not fix the problem completely.

Steps to reproduce

A fail due to this can be caused by running \Drupal\Tests\thunder\FunctionalJavascript\ArticleCreationTest on apache or nginx. The test randomly fails with:
Failed to create style directory: public://styles/entity_browser_thumbnail/public/2016-05

Proposed resolution

The problem is that image derivatives use the recursive feature of \Drupal\Core\File\FileSystem::mkdir() so while #3036494: Race condition in ImageStyle::createDerivative() fixed the race when creating the last directory in the path ie 2016-05 - it doesn't for any of the others.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

alexpott created an issue. See original summary.

alexpott’s picture

Status: Active » Needs review
FileSize
901 bytes
alexpott’s picture

Here's a test that proves that finally we have this race condition fixed! Yay.

alexpott’s picture

Hmmmm.... I don't think pcntl_fork() is available on our CI.... the test failed reliably without the fix for me.

alexpott’s picture

Hmmmm.... nope PHP is compiled with --enable-pcntl see https://git.drupalcode.org/project/drupalci_environments/-/blob/producti... and the test in #4 is not going to fail. Maybe DrupalCI is just too quick to cause this to happen. The test in #3 reliably fails for me.

alexpott’s picture

Let's see if waiting a bit and all starting at the same time gets us a fail.

alexpott’s picture

Let's do this a little bit more elegantly with less looping.

alexpott’s picture

And now let's add the fix back in...

The last submitted patch, 7: 3211936-fail-test-7.patch, failed testing. View results

The last submitted patch, 7: 3211936-fail-test-7.patch, failed testing. View results

daffie’s picture

Patch looks good:

  1. +++ b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
    @@ -196,4 +197,51 @@ public function testDirectoryCreation() {
    +    foreach ($directories as $i => $directory) {
    

    The variable $i is not used and can be removed.

  2. +++ b/core/lib/Drupal/Core/File/FileSystem.php
    @@ -214,7 +214,10 @@ public function mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
    +          $success = $this->mkdirCall($recursive_path, $mode, FALSE, $context);
    ...
    +          if (!$success && !file_exists($recursive_path)) {
    

    The variable $succes is not necessary and can be removed or should the if-statement be !$success || !file_exists($recursive_path).

  3. +++ b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
    @@ -196,4 +197,51 @@ public function testDirectoryCreation() {
    +    $time_to_start = microtime(TRUE) + 0.1;
    ...
    +        usleep(($time_to_start - microtime(TRUE)) * 1000000);
    

    Can these be replaced with usleep(100000)?

alexpott’s picture

Status: Needs work » Needs review
FileSize
1.34 KB
3.24 KB

Thanks for the review @daffie

#11.1 Fixed
#11.2 This is correct. If $success is TRUE then we will not bother evaluating file_exists($recursive_path) and therefore save I/O.
#11.3 No. This is necessary to ensure that the race condition occurs. Looping and forking takes time that we have to account for. And is documented.

        // Sleep so that all the forks start preparing the directory at the same
        // time.
daffie’s picture

Status: Needs review » Reviewed & tested by the community

@alexpott: Thank you for the explanations.

There is a fix and testing for the fix.
The test fails on my local machine when I remove the fix.
All code changes look good to me.
For me it is RTBC.

catch’s picture

  1. +++ b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
    @@ -196,4 +197,51 @@ public function testDirectoryCreation() {
    +   *
    +   * Image style generation call result in many calls to create similar
    +   * directory paths. This test forks the process to create the same situation.
    +   */
    

    Should this be 'can result'?

  2. +++ b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
    @@ -196,4 +197,51 @@ public function testDirectoryCreation() {
    +    }
    

    One the one hand it's great we can test this, on the other I'm a bit concerned about test stability (and maintainability) with this one. But seems worth a go.

alexpott’s picture

Thanks for the review @catch.

Fixed #14.1
Re #14.2 - in some ways this test is a good thing if we to move Drupal to a place where we support event-loop PHP frameworks like react-php. It'll keep us a bit honest about what happens in destructors. I think the benefits of have test coverage here outweigh maintainability issues:

  • we skip the test if the environment doesn't support forking
  • the single work around is not that bad
  • the test fails reliably without the fix on CI and locally for me

  • catch committed 2690c63 on 9.3.x
    Issue #3211936 by alexpott, daffie: Race condition when generating sub...

  • catch committed 2f5eb02 on 9.2.x
    Issue #3211936 by alexpott, daffie: Race condition when generating sub...

  • catch committed 220c6c9 on 9.1.x
    Issue #3211936 by alexpott, daffie: Race condition when generating sub...
catch’s picture

Title: Race condition when generating sub directories for image styles » [backport] Race condition when generating sub directories for image styles
Version: 9.2.x-dev » 8.9.x-dev

Committed/pushed to 9.3.x and cherry-picked back to 9.1.x

This can probably go into 8.9.x but the test failed, so asking for a re-run.

larowlan’s picture

Title: [backport] Race condition when generating sub directories for image styles » Race condition when generating sub directories for image styles
Version: 8.9.x-dev » 9.1.x-dev
Status: Reviewed & tested by the community » Fixed

Closing as 8.9.x is now in security-only mode

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.