Molecular Man
Molecular Man

Reputation: 22386

How to manage concurrency problems in php?

Method processReport may be called in parallel with the same argument. But processing should be done only once if two processes call processReport with the same $reportId at the same time. To resolve this concurrency problem I've created helper methods isLocked, lock and unlock. lock creates temporary file for the $reportId. isLocked checks if the temp file exists. If the file exists processing should not be done. But the thing is that two parallel processes call processReport in the same time with the same $reportId sometimes this strategy doesn't work and processing is done twice(this obviously happens because of using filesystem which is too slow). Since processes do not share memory I don't know how to solve this concurrency problem. Is there any way to avoid processing the same $reportId twice?

private function getLockFileName($reportId) {
    return sprintf('%s/%s.lock', sys_get_temp_dir(), $reportId);
}

private function isLocked($reportId) {
    return file_exists($this->getLockFileName($reportId));
}

private function lock($reportId) {
    touch($this->getLockFileName($reportId));
}

private function unlock($reportId) {
    unlink($this->getLockFileName($reportId));
}

public function processReport($reportId) {
    if ($this->isLocked($reportId)) return;

    $this->lock($reportId);

    // processing should be done only once for the same $reportId

    $this->unlock($reportId);
}

Upvotes: 3

Views: 1309

Answers (2)

Matt S
Matt S

Reputation: 15364

The semaphore extension was built for this situation. Example:

public function processReport($reportId) {
    $sem = sem_get(crc32("report-$reportId"));

    sem_acquire($sem);  // Wait until the semaphore is free

    // processing should be done only once for the same $reportId

    sem_release($sem);  // Release for another request
}

You can also use APC or memcached to write your own quick lock to shared memory. Or even better write the results of your report to shared memory on first run so future requests get it quicker.

Upvotes: 2

Iłya Bursov
Iłya Bursov

Reputation: 24146

Here are several possibilities to "simulate" locks

  1. flock - you can acquire exclusive lock on file, so other php processes will wait
  2. Memcached::cas - memcache based lock, you will need to organize cas loop
  3. sem_acquire - as mentioned above, semaphore based lock
  4. Shared Memory - php has shared memory module
  5. Same session id - you can use the same session id, as allow only one request per session at one time

Upvotes: 3

Related Questions