2

I have a function that send HTTP request via CURL to www.server.com

My task is to make sure that www.server.com gets no more than one request every 2 seconds.

Possible solution:

Create a function checktime() that will store current call time in database and check with database on every next call and make system pause for 2 seconds:

$oldTime = $this->getTimeFromDatabase();
if ($oldTime < (time() - 2) ) { // if its been 2 seconds
  $this->setNewTimeInDatabase(); 
  return true;
} else {
  sleep(2);
  return false;
}

The problem/question:

Lets say, the last request to www.server.com was on 1361951000. Then 10 other users attempt to do request on 1361951001 (1 seconds later). checktime() Function will be called.

As since it only has been 1 second, the function will return false. All 10 users will wait 2 seconds. Does it means that on 1361951003 there are 10 requests will be sent simultaneously? And is it possible that the time of last request will not be changed in database, because of the missed call of $this->setNewTimeInDatabase() in checktime()?

Thank you!

UPDATE:

I have just been told that using a loop might solve the problem:

for($i=0;$i<300;$i++)
{
   $oldTime = $this->getTimeFromDatabase();
   if ($oldTime < (time() - 2) ) { // if its been 2 seconds
   $this->setNewTimeInDatabase(); 
   return true;
   } else {
    sleep(2);
    return false;
   }
}

But i don't really see logic in it.

5
  • Wouldnt it be better to do the check on the www.server.com? Commented Feb 27, 2013 at 20:11
  • @Zaffy i don't have an access to www.server.com Commented Feb 27, 2013 at 20:11
  • Another option is to make a static variable that works like a que and then, after 2 seconds, the next item from the que is sent to server.com. This would have to be a static variable so it is shared across all instances of your class Commented Feb 27, 2013 at 20:12
  • 1
    Why not just cache the response with a ttl of 2 seconds? Commented Feb 27, 2013 at 20:16
  • @Crisp i get different responses on every request Commented Feb 27, 2013 at 20:44

2 Answers 2

2

I believe you need some implementation of a semaphore. The database could work, as long as you can guarantee that only one thread gets to write to the db and then make the request.

For example, you might use an update request to the db and then check for the updated rows (in order to check whether the update actually happened). If the update was succesful you can assume you got the mutex lock and then make the request (assuming the time is right to make it). Something like this:

$oldTime = $this->getTimeFromDatabase();
if ($oldTime < (time() - 2) && $this->getLock()) { // if its been 2 seconds
  $this->setNewTimeInDatabase(); 
  $this->releaseLock();
  return true;
} else {
  sleep(2);
  return false;
}  

function getLock()
{
    return $mysqli->query('UPDATE locktable set locked = 1 WHERE locked = 0');
}

function releaseLock()
{
     $mysqli->query('UPDATE locktable set locked = 0');
}

I'm not sure about the mysql functions, but I believe it's ok to get the general idea.

Sign up to request clarification or add additional context in comments.

2 Comments

Hmmm.. what if i will just check if DB update happened? if ($oldTime < (time() - 2) && $this->setNewTimeInDatabase())
Well, actually I'll have to change my original answer in order to make it more explicit, but the principle stands. The idea of using the database as a locking mechanism is to ensure atomicity (I mean, to ensure that only ONE thread gets to do the operation at any one time). If you don't implement such mechanism, you can't be sure only one got to setNewTime.
1

Watch out with using a database. For example MySQL is not always 100% in sync with its sessions, and for that reason it is not safe to rely on that for locking purposes.

You could use a file-lock through the method flock, where you would save the access time in. Then you could be sure to lock the file, so no two or more processes would ever access it at the same time.

It would probably go something like this:

$filepath = "lockfile_for_source";
touch($filepath);
$fp = fopen("lockfile_for_resource", "r") or die("Could not open file.");

while(true){
  while(!flock($fp, LOCK_EX)){
    sleep(0.25); //wait to get file-lock.
  }

  $time = file_get_contents($filepath);
  $diff = time() - $time;
  if ($diff >= 2){
    break;
  }else{
    flock($fp, LOCK_UN);
  }
}

//Following code would never be executed simultaneously by two scripts.
//You should access and use your resource here.

fwrite($fp, time());
fflush($fp);
flock($fp, LOCK_UN); //remove lock on file.
fclose($fp);

Please be aware that I have not tested the code.

5 Comments

This could also work, but if you need to do the query from several different servers, a local file won't do.
In that case a distributed lock would solve the problem. That would require one to set up a system for that like flaco-lock-service Or one could mount a share that would be shared between the servers and then go with the file-lock.
I cant set up any systems or mount anything. I need to solve it within couple lines of code :)
If you have multiple servers, then I cant think of any other solutions than distributed locks or shared file-system locks. If you are on a single server, then I think a file lock would be the best.
Hello. Should it be two different file names: lockfile_for_source and lockfile_for_resource ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.