Search code examples
phpmultiprocessing

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);
}

Solution

  • 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