Monday, September 10, 2018

Allowing only one executing PHP request

What if for reasons of performance or serialization you wanted to quickly (without messing with complex apache and/or php configuration options) ensure that only one php request of some web application executes at any one time?

I had this requirement recently and what I did is used a "lock" file. The PHP code goes and checks if that file exists and it it does it considers execution locked and dies. If the file does not exist it creates it and runs.

However there is a potentially fatal flaw in this simple solution; what if a PHP request dies (for whatever reason) before it had time to delete the lock file at the end of processing? Your app would never run ever again! To address this I added timestamps into the mix. Specifically, when the file is created a timestamp representing the exact time of its creation is written into it and then when another incoming request polls for the lock file it actually reads the timestamp, compares it to current time and acquires the lock, even though the lock file exists, if a certain time threshold, during which it is reasonable to assume the previous request would have finished processing, has elapsed.

The file's time attributes could also be used instead of a timestamp in the file but that would bring in complexity in the form of platform considerations, timezones, server admins changing those, etc. Though not perfect, the "timestamp in a lock file" is simple to implement, keeps most variables within the PHP platform space and it works.

So here is the getLock() function I used; x is the minimum number of seconds between requests you would want to enforce.

function getLock()
{
 if (file_exists("steglock.txt"))
 {
  $before = trim(file_get_contents("steglock.txt"));
  $now = strtotime("now");
  
  $difference = abs( ($now) - ($before) );
  
  if ( $difference < x)
  {
   return false;
  }
  else
  {
   file_put_contents("steglock.txt",  strtotime("now"));
   return true;
  }
 }
 else
 {  
  file_put_contents("steglock.txt",  strtotime("now"));
  return true;
 }
}

Then, just before you start your processing in you PHP file (or files):

if (!getLock())
{
 die("You can only submit one request every x seconds");
}

Good luck!

No comments:

Post a Comment