Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes race condition when building merged css/js file during simultaneous requests #21756

Conversation

Ian410
Copy link
Member

@Ian410 Ian410 commented Mar 14, 2019

Description (*)

Fixes bug when many requests to a page are made and multiple streams request to build the same merged css or js file sometimes results in serving incomplete files to the client if a second request came in and truncated the part of the work from the first request.

If two requests try and build a merged css or js file both attempt to do so. Request A starts to build the file, when Request B starts it truncates the temporary file and begins to build its own. Request A finishes the process and copied a now incomplete file into the final directory and serves it to the customer. When Request B finishes, it copes the now complete file into the final directory and serves it to the customer. Only the first customer would notice a problem and it would be resolved on refresh.

This can manifest itself more severely with a CDN. In the scenario above, the incomplete file would be cached by a CDN edge node. If request A came from Chicago and request B came from Miami then Chicago node would have the incomplete file and Miami would have the complete file. All users from Chicago and surrounding areas would have the site broken while it would appear fine to people in Miami.

Manual testing scenarios (*)

  1. Enable css merge/minify
  2. Enable js merge/minify/bundle
  3. Set instance to production mode.
  4. Prime cache (load homepage)
  5. Flush block_html and full_page cache
  6. Load homepage from 10-20 connections simultaneously, save each instance css and js

Contribution checklist (*)

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • All automated tests passed successfully (all builds on Travis CI are green)

…request to build the same merged css or js file sometimes results in serving incomplete files to the client if a second request came in and truncated the part of the work from the first request.
@magento-engcom-team
Copy link
Contributor

Hi @Ian410. Thank you for your contribution
Here is some useful tips how you can test your changes using Magento test environment.
Add the comment under your pull request to deploy test or vanilla Magento instance:

  • @magento-engcom-team give me test instance - deploy test instance based on PR changes
  • @magento-engcom-team give me 2.3-develop instance - deploy vanilla Magento instance

For more details, please, review the Magento Contributor Assistant documentation

/**
* @var \Magento\Framework\Math\Random
*/
protected $mathRandom;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Ian410. Thanks for collaboration. According to [Magento Technical Guidelines] we can provide new public/protected properties (https://devdocs.magento.com/guides/v2.3/coding-standards/technical-guidelines.html)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in latest commit.

/**
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Framework\View\Url\CssResolver $cssUrlResolver
*/
public function __construct(
\Magento\Framework\Filesystem $filesystem,
\Magento\Framework\View\Url\CssResolver $cssUrlResolver
\Magento\Framework\View\Url\CssResolver $cssUrlResolver,
\Magento\Framework\Math\Random $mathRandom
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to Magento backward-compatible guide we can't add required parameters to the constructor. Please look Adding a constructor parameter section to do it in the correct way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in latest commit.

$staticDir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW);
$tmpDir = $this->filesystem->getDirectoryWrite(DirectoryList::TMP);
$tmpDir->writeFile($filePath, $mergedContent);
$tmpDir->renameFile($filePath, $filePath, $staticDir);
$tmpDir->writeFile($tmpFilePath, $mergedContent);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the file always will have a different name. I'm sure that resolves the problem, but the file will not caching and always will regenerate. If I am right that is not pretty cool.

Copy link
Member Author

@Ian410 Ian410 Mar 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct that each temporary file will have a different filename. However, the next line not commented is $tmpDir->renameFile($tmpFilePath, $filePath, $staticDir); which always renames the file to the correct path.

For example, in my two request example:

  1. Request A creates file var/tmp/_cache/merged/abc123abc123.min.css_ZpAA9ljK7QDGf5az2pTLEv55adF7
  2. Request B creates file var/tmp/_cache/merged/abc123abc123.min.css_cOgz75FD4tcK0ALxDpCM
  3. Request A finishes creation of file and renames var/tmp/_cache/merged/abc123abc123.min.css_ZpAA9ljK7QDGf5az2pTLEv55adF7 to pub/static/_cache/merged/abc123abc123.min
  4. Request B finishes creation of file and renames var/tmp/_cache/merged/abc123abc123.min.css_cOgz75FD4tcK0ALxDpCM to pub/static/_cache/merged/abc123abc123.min

Request B does override the Request A file that was built, but it is the same file so it doesn't matter. The important part is that Request A didn't copy over a truncated file as Request A was building it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That last sentence should read:
The important part is that Request A didn't copy over a truncated file as Request B started building it.

…l guidelines

Made mathrandom class optional to meet backwards compatibility requirements.
@ghost
Copy link

ghost commented Mar 19, 2019

@Ian410 unfortunately, only members of the maintainers team are allowed to remove progress related labels to the pull request

@VladimirZaets
Copy link
Contributor

@Ian410 thanks for changes

@magento-engcom-team
Copy link
Contributor

Hi @VladimirZaets, thank you for the review.
ENGCOM-4534 has been created to process this Pull Request

@magento-engcom-team
Copy link
Contributor

@Ian410 thank you for contributing. Please accept Community Contributors team invitation here to gain extended permissions for this repository.

@p-bystritsky
Copy link
Contributor

QA passed.

@p-bystritsky p-bystritsky force-pushed the feature/merged-static-content-race-condition branch from d6aaa2b to 4d37719 Compare April 17, 2019 08:43
@magento-engcom-team magento-engcom-team merged commit 4d37719 into magento:2.3-develop Apr 23, 2019
@m2-assistant
Copy link

m2-assistant bot commented Apr 23, 2019

Hi @Ian410, thank you for your contribution!
Please, complete Contribution Survey, it will take less than a minute.
Your feedback will help us to improve contribution process.

taskula pushed a commit to Hypernova-Oy/magento2 that referenced this pull request Apr 23, 2019
@magento-engcom-team magento-engcom-team added this to the Release: 2.3.2 milestone Apr 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants