Reputation: 22386
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
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
Reputation: 24146
Here are several possibilities to "simulate" locks
Upvotes: 3