atomic_file_put_contents for concurrent cache access#4
Open
peterpostmann wants to merge 1 commit intoinouet:masterfrom
Open
atomic_file_put_contents for concurrent cache access#4peterpostmann wants to merge 1 commit intoinouet:masterfrom
peterpostmann wants to merge 1 commit intoinouet:masterfrom
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
If multiple instances try to write a new cache file simultaneously the file will be inconsistent, because file_put_contents "is identical to calling fopen(), fwrite() and fclose() successively to write data to a file" [1]. This could be solved by using a lock as suggested in [2], but there is also another problem: If the file is read wile being written, only a partial file be read [3].
To solve both issues, the file is not written directly, but to a temporary file first and then moved.
$tmpName = $filename.'-'.static::guid();file_put_contents($tmpName, $data));rename($tmpName, $filename);Rename is atomically overwrites the existing file, hence a read will return either the old or the new file, but no partial file. The file may be overwritten multiple times (by concurrent instances of the script, if the cache is expired), but the data will be the same, so this is not an issue.
For the temporary file a GUID is appended (to avoid writes to the same file). The cleanest solution would be to use
$_SERVER['UNIQUE_ID']but this depends on the Apache Module mod_unique_id, which is not a portable solution [4][1] http://php.net/manual/en/function.file-put-contents.php
[2] https://stackoverflow.com/questions/5479580/is-there-a-risk-in-running-file-put-contents-on-the-same-file-from-different-p
[3] https://stackoverflow.com/questions/4899737/should-lock-ex-on-both-read-write-be-atomic/4899822#4899822
[4] https://stackoverflow.com/questions/25196343/what-is-the-value-in-serverunique-id-used-for