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

Write Blade templates cache atomically #54989

Closed
wants to merge 1 commit into from

Conversation

joshuaruesweg
Copy link
Contributor

This change ensures that the blade cache file is written atomically. Without atomic writing, there is a brief period during which the file is created but empty. By ensuring the file is written atomically, we can guarantee that it always has a consistent state.

Reproducing this bug is quite difficult because it requires sending two requests that compile the same blade component. The second request must access the blade component before the content is written but after the file is created. To reproduce the bug, I slowed down the filesystem's write speed using the following package: https://github.com/bandwidth-throttle/bandwidth-throttle, and patched the filesystem (Patch file to reproduce; requires the Composer package bandwidth-throttle/bandwidth-throttle, which is not installed with the attached patch). Afterward, we can easily trigger the bug with PHPUnit and Parallel Testing using various tests that send the same mailable (in different tests):

ValueError: DOMDocument::loadHTML(): Argument #1 ($source) must not be empty

[…]/vendor/tijsverkoyen/css-to-inline-styles/src/CssToInlineStyles.php:117
[…]/vendor/tijsverkoyen/css-to-inline-styles/src/CssToInlineStyles.php:37
[…]/vendor/laravel/framework/src/Illuminate/Mail/Markdown.php:75
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailable.php:379
[…]/vendor/laravel/framework/src/Illuminate/Collections/helpers.php:236
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailer.php:441
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailer.php:420
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailer.php:313
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailable.php:206
[…]/vendor/laravel/framework/src/Illuminate/Support/Traits/Localizable.php:29
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailable.php:199
[…]/vendor/laravel/framework/src/Illuminate/Mail/SendOrderReceivedMailable.php:83
[…]/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
[…]/vendor/laravel/framework/src/Illuminate/Container/Util.php:43
[…]/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:95
[…]/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:35
[…]/vendor/laravel/framework/src/Illuminate/Container/Container.php:696
[…]/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:126
[…]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:170
[…]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:127
[…]/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:130
[…]/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:126
[…]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:170
[…]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:127
[…]/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:121
[…]/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:69
[…]/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php:102
[…]/vendor/laravel/framework/src/Illuminate/Queue/SyncQueue.php:76
[…]/vendor/laravel/framework/src/Illuminate/Queue/SyncQueue.php:56
[…]/vendor/laravel/framework/src/Illuminate/Queue/Queue.php:59
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailable.php:234
[…]/vendor/laravel/framework/src/Illuminate/Mail/Mailer.php:483
[…]/vendor/laravel/framework/src/Illuminate/Mail/PendingMail.php:146
[…]/app/Helpers/Order/Create.php:532
[…]/tests/SimpleOrderTest.php:17

This ensures, that the file is written atomically. Otherwise, it could lead to empty files, which could lead to further errors. However, reproducing this is quit tricky, because you must compile a template and access the same template before the content is written, but after the file is created.
@browner12
Copy link
Contributor

is this "bug" moot if people are correctly caching their views on deploy?

@joshuaruesweg
Copy link
Contributor Author

joshuaruesweg commented Mar 12, 2025

is this "bug" moot if people are correctly caching their views on deploy?

Yes, it is a bug. Files like this should always be written atomically. Even if you pre-compile the templates before deploying the files to a production system, this is mostly not the case for testing (for example, locally).

Why do you mind the change at all? Can you explain it to me so that I can understand it?

@taylorotwell
Copy link
Member

A bit hesitant to change how views are written in a patch release after so many years. 😬

@browner12
Copy link
Contributor

Same boat as Taylor. don't think the change is "wrong", but trying to weigh the risk/reward.

Your original comment even said it's incredibly hard to reproduce. And if it only occurs when people are not correctly caching their views on deploy, it's not fixing a real world problem.

@joshuaruesweg
Copy link
Contributor Author

And if it only occurs when people are not correctly caching their views on deploy, it's not fixing a real world problem.

This is incorrect. Mail-Templates are not compiled with the php artisan view:cache command. So, this is a real world problem. If I see it right, there is currently no command for ensuring that the mail templates are generated correctly. Am I missing something?

A bit hesitant to change how views are written in a patch release after so many years. 😬

@taylorotwell Totally understandable. However, since the problem persists even when the application is deployed correctly, it may be worth considering potential solutions, particularly for the email templates. Without having fully assessed the practicality of this, I was wondering if a (clean) pull request to extend the view:cache command to also pre-cache email templates would have a chance of being merged. Additionally, could you kindly let me know the likelihood of this request being included in the next major release (the master branch)? If it is likely, I would rebase the commit on the current master and open a new Pull Request.

@browner12
Copy link
Contributor

Thanks for the clarification. I wasn't aware mailable views weren't cached.

Since your PR is so small, I'd just re-branch off of master, and try again there. You're more likely to get a response that way than on this thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants