There is a race condition in creating the cache file. My read of the code shows it is simply opening the file to write, then writing the contents. There doesn't appear to be any locking to prevent multiple writers, or any attempt to prevent other processes from reading the file while one is writing to the file at the same time.
As it stands, two (or more) processes could be writing to the same file at the same time, resulting in a jumbled mess, or another process could try to serve up the cached file before it is fully written. The simple test of testing if the file exists before creating it is insufficient because the two steps are not atomic.
The simplest thing to do would be to write the file with the PID appended to the name, then rename it to the actual file name after successfully closing it for writing. This way, even if multiple processes try to create the same cache file, it will always be correct (never will two writers try to write the same physical file), and readers will never see a partially written file as it is atomically put in place by the rename.
| Comment | File | Size | Author |
|---|---|---|---|
| #13 | boost-223610.1.patch | 3.42 KB | mikeytown2 |
| #12 | boost-223610.patch | 3.13 KB | mikeytown2 |
| #11 | boost_racecondition_223610.patch | 933 bytes | christefano |
Comments
Comment #1
vivekkhera commentedI went ahead and made a patch to make it atomic as described above.
I don't know how to make an entry into the translation table for this new error message.
Comment #2
Greg Go commentedThanks for contributing the patch! Applied and works.
+1
Comment #3
akalsey commentedThis patch is working for me. Marking as reviewed and tested.
Comment #4
drupdrips commented+1 works for me too and is a critical patch in my opinion.
Comment #5
Arto commentedLooking into this.
Comment #6
Arto commentedConsidering using the
LOCK_EXargument to file_put_contents() (but see PHP bug #43182) or just basing the locking on flock(). Thoughts?Comment #7
moshe weitzman commented@arto - that bug has long since been fixed according to that page. I'd say that file_put_contents() with LOCK_EX is a reasonable solution. You *might* want to declare php 5.1 as a requirement in the module since thats when LOCK_EX became available.
Comment #8
Arto commented@Moshe, well, that PHP bug was apparently fixed. There is no resolution on file other than the automatic "fixed in CVS" at the end (so who knows which option was chosen or what was fixed, exactly), and the version number on the ticket states PHP 5.2.4 in any case, which throws some doubt about relying on consistent
LOCK_EXbehavior prior to that.Also, while I've declared PHP 5.2+ a dependency on all my other modules, I'm somewhat more loath to do so with Boost as the module is most generally useful exactly to people who are running their sites on crappy web hosts. In other words, I need to think about this some more... maybe we should go with the original poster's patch for the time being.
Comment #9
moshe weitzman commentedThe original patch is fine with me as well.
Comment #10
Arto commentedOK, will test it and commit...
Comment #11
christefano commentedRerolled for 5.x-1.x-dev.
Comment #12
mikeytown2 commentedUpdated for 6.x as it is still an issue. Improved watchdog reporting so admin has a better chance of fixing the error if it's a reoccurring issue. Split the file writing off in its own function.
Comment #13
mikeytown2 commentedadded more reporting
Comment #14
mikeytown2 commentedcommitted with a slight fix to the watchdog call in the above patch
http://cvs.drupal.org/viewvc.py/drupal/contributions/modules/boost/boost...
Comment #15
lapith commentedI have a question about this topic as we have noticed the same conditions on both our boost D5 and D6 installs.
From what I understand that the LOCK_EX argument applies an exclusive lock on the file, closing it to reading and writing. My question is what happens when a script tries to read or write to that file while locked?
For instance, if a page is requested and the cache is being written at the exact time that a second page request is made, would Apache try to serve up a locked file, thus sending an error to the end user?
Also what if 2 concurrent page requests are made and both try to write at close to the same time. Does the process that took slightly longer error out when it tries to write?
It seems like ideally, any process that comes across a locked file waits for the file to be unlocked, but I have a feeling that is wishful thinking. Has anyone noticed either of the scenarios, or have a clear understanding of the inner workings of the LOCK_EX argument?
Comment #16
lapith commentedI just looked through the patch, it looks like everyone has decided to go with the create a file then rename it method. Works for me.
Comment #17
mikeytown2 commentedI made some very slight changes to the patch, that is now in
http://cvs.drupal.org/viewvc.py/drupal/contributions/modules/boost/boost...
in short
@unlink($tempfile);when rename fails.Comment #19
mikeytown2 commentedhttp://drupal.org/node/223610#comment-1138711
Comment #20
mikeytown2 commentedComment #21
mikeytown2 commentedClosing all 5.x issues; will only reevaluate if someone steps up #454652: Looking for a co-maintainer - 5.x
Reason is 6.x has 10x as many users as 5.x; also last 5.x dev was over a year ago. The 5.x issue queue needs to go.