oOo--STAR--oOo
oOo--STAR--oOo

Reputation: 438

php-fpm and fwrite issues using 100% cpu

I have an unusual problem that I managed to find the root of. I am currently set up on CentOS Linux 6.3 nginx/1.0.15 PHP Version 5.3.18 PHP-FPM 5.3.18-1

I have a refresh.txt file that is written every time someone posts a shout to update the new time stamp. We also have JS checking the value of this time stamp every 1 second. Now the problem with this is.. Say 5 or more people shout at the same time writing to the refresh.txt file it will use 100% cpu. just to write a time stamp!!

I don't know why it is doing this..

Here is my php code.

if(!empty($rf_clear))
    $tb_send = "clear";
else
    $tb_send = time();

// Add flatfile check
$tbcheck = "refresh.txt";
    $tbsend = fopen($tbcheck, 'w');
    $tbtime = $tb_send;
fwrite($tbsend, $tbtime);
fclose($tbsend);

JS

talk_jax.open("GET", boardurl+"/talkbox/refresh.txt?nocache="+nocache);

How can I fix this or maybe work around this? Any help would me massively appreciated.

Thanks.

Edit: still no solution to this. Is there a way to limit the requests? Or is there a better way todo this all together.

I have tried APC cache, the problem with that is its not serving the php file fast enough so shouts are really slow, unless I was doing something wrong?

apc_store("refresh", time());

JS

talk_jax.open("GET", boardurl+"/talkbox/refresh.php?nocache="+nocache);

I have also tried using a database. Its same too slow to serve the php file.

Upvotes: 1

Views: 1693

Answers (2)

Alex
Alex

Reputation: 12019

Consider using Apc instead.
apc_store('refresh', time()); to store
apc_get('refresh'); to retreive

yum install php-apc

Besides, you don't even have to store the time in it.
You could just store a counter apc_inc('refresher'); to store
that will increase every time there is a change and check in js if the new value is higher than the previous one you had.

Upvotes: 1

doublesharp
doublesharp

Reputation: 27599

The best option is to use flock() to lock your file for writing - https://www.php.net/flock - using a loop for Windows compatibility as flock does not have a blocking option (not valid in your case for CentOS, but unharmful).

$max_tries = 5; // don't loop forever
$tbcheck = "refresh.txt";
$tbsend = fopen($tbcheck, 'w');
for ($try=0; $try<$max_tries, $try++){
    if (flock($tbsend, LOCK_EX, true)) {  // acquire an exclusive blocking lock
        fwrite($tbsend, $tb_send);  // write to file
        fflush($tbsend);            // flush output before releasing the lock
        flock($tbsend, LOCK_UN);    // release the lock
        break;                      // exit the loop
    }
    usleep(100);                    // sleep for 100ms if we couldn't get a lock
}
fclose($tbsend);

Another option would be to use APC or memcached to store a lock, which can then be checked from other PHP processes. Assuming that you have memcached your code would look something like this:

$timeout = 5; // set a cache expiration so a broken process doesn't lock forever
$key = "file_locked";
$max_tries = 5; // don't loop forever
for ($try=0; $try<$max_tries, $try++){
    // check to see if there is a lock
    if (!Memcached::get($key)){
        // not locked, so set a lock before processing
        Memcached::set($key, true, $timeout);

        // write to the file
        $tbcheck = "refresh.txt";
        $tbsend = fopen($tbcheck, 'w');
        $tbtime = $tb_send;
        fwrite($tbsend, $tbtime);
        fclose($tbsend);

        // delete the lock
        Memcached::delete($key);
       
        // exit the loop
        break;
    }
    // locked, pause for 100ms
    usleep(100);
}

Upvotes: 3

Related Questions