Forum Moderators: coopster

Message Too Old, No Replies

PHP and Simultaneous Read/Write to File

         

demiurgeinc

2:10 am on Apr 18, 2010 (gmt 0)

10+ Year Member



In a simple stock application, I need to retrieve stock data from a 3rd party source based on the desired symbol provided by a user. Since it can take a few seconds to retrieve the data, I'd like to save the data to a file. On later requests, if the file exists, I'll load the file instead of accessing the 3rd party site.

In a high volume site, if two people access the site at the same time; will there be an issue?

I know I can lock the file after I open it, to prevent other concurrent requests from writing to it. Is there a possibility that one request could happen in between the other requests open and lock?

A friend of mine told me that PHP handles this internally and will simply hold the second request until the file is unlocked. Is this true?

Another friend said to use a sempahore technique. But I'd rather not if PHP handles this on its own. Thank you.

coopster

12:25 pm on Apr 19, 2010 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Hi demiurgeinc and welcome to WebmasterWorld.

flock [php.net] is the portable advisory file locking function in PHP.

Is there a possibility that one request could happen in between the other requests open and lock?


I've never tested it myself. You could do a quick test by putting a sleep() in between your file open and your file lock. But then again, it all depends on which mode you are using when you fopen [php.net] the file in the first place.

eelixduppy

1:25 pm on Apr 19, 2010 (gmt 0)



From my understanding of the problem, once a file is written it wouldn't be re-written. Is this true? If it is and you are trying to do multiple reads of the same file at once you shouldn't have any issues.

TheMadScientist

1:27 pm on Apr 19, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



You might want to have a look at file_put_contents() [us2.php.net]
LOCK_EX = Acquire an exclusive lock on the file while proceeding to the writing. Mutually exclusive with FILE_APPEND.

demiurgeinc

1:52 pm on Apr 19, 2010 (gmt 0)

10+ Year Member



eelixduppy:
The files would get rewritten all the time. You'd want the data to be updated every now and then. The point is to reduce the need to go to yahoo on every request.

coopster:

The desired effect of the fopen mode would be to write, to create the file if it doesn't exist. After locking, I want to truncate and move the pointer to the beginning of the file (truncate does that ftruncate($fh, 0); ).

In a single PHP file executing locally, each command will complete in sequence; so a simple test will never result produce an issue.

The question is, on a webserver that is a getting high volume of traffic, what happens when multiple requests occur at the exact same time?

My research suggests that code in any thread will not continue until it obtains the lock; and the server will never let more than one thread have a lock on a file.

I suppose there would be problems if you had multiple web servers accessing the same files (like on a SAN)?

eelixduppy

1:59 pm on Apr 19, 2010 (gmt 0)



>> multiple web servers

I'm not sure this would be an issue, actually. Since PHP was written in C, I don't see why they wouldn't utilizing the locking mechanisms of the OS instead of implementing a lock themselves. With that said, if the OS is handling the lock, every process trying to access that resource will have to wait to acquire the lock.

One thing you have to worry about when dealing with concurrency issues like this is starving a particular user who needs the data. So if you are writing to the file faster than you can process the read requests, you might be not allowing a particular user to get at those resources, or it might just slow the process down. I'm not sure it will be a big issue here, but just something to keep in mind.

TheMadScientist

2:22 pm on Apr 19, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Why not something like this:

/* On the page(s) the file is accessed from: This contains the location of the most recent version of the file in a variable called $file. Use it to insert the file location, unless you need it to be indexed by SEs IOW the name MUST stay the same because there may be links to it. */

include_once 'file-location.php';

/* Set a bit of javascript on the same page as the file is accessed from to open a php page asynchronously. (Don't return anything to the visitor, just open the page.) On the asynchronously opened page do this: */

include_once 'file-location.php';
if(filemtime($file) < time()-60*60) {
$new_file_location='file-name-'.time().'.php';
$set_file_location='<?php $file=\''.$new_file_location.'\' ?>';
file_put_contents($new_file_location,'http://www.example.com/the-file');
file_put_contents('file-location.php',$set_file_location,LOCK_EX);
unlink($file);
}
else { exit; }


You could do the same thing with a CRON and either way you'd only be tying up one 'highly accessed' file for the time it takes to write about 30 characters. (Actually, it'd be better with a CRON, but I'll leave the other suggestion, because maybe there are times when someone could use the asynchronous idea... It might 'jumble things' if you get too much access during the write time.) There is a way you could do it using the asynchronous suggestion and ensure the file is only written once, but I've got to get to work soon so I'm not going to re-type or re-suggest... I'll leave it to readers to figure out how to make sure the old file's filemtime() is only expired for one visitor... ;)

file_put_contents($file,PHP_EOL,FILE_APPEND);