From 7d1ae1167ddf733278841824aa0df7dab2f34661 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 13 May 2011 22:34:19 -0400 Subject: - Patch #802856 by catch, pillarsdotnet: make lock_wait() wait less. --- includes/lock.inc | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'includes/lock.inc') diff --git a/includes/lock.inc b/includes/lock.inc index 2eb7b9a90..42f1906f2 100644 --- a/includes/lock.inc +++ b/includes/lock.inc @@ -187,7 +187,7 @@ function lock_may_be_available($name) { * lock. This will block further execution until the lock is available or the * specified delay in seconds is reached. This should not be used with locks * that are acquired very frequently, since the lock is likely to be acquired - * again by a different request during the sleep(). + * again by a different request while waiting. * * @param $name * The name of the lock. @@ -198,12 +198,32 @@ function lock_may_be_available($name) { * TRUE if the lock holds, FALSE if it is available. */ function lock_wait($name, $delay = 30) { - $delay = (int) $delay; - while ($delay--) { + // Pause the process for short periods between calling + // lock_may_be_available(). This prevents hitting the database with constant + // database queries while waiting, which could lead to performance issues. + // However, if the wait period is too long, there is the potential for a + // large number of processes to be blocked waiting for a lock, especially + // if the item being rebuilt is commonly requested. To address both of these + // concerns, begin waiting for 25ms, then add 25ms to the wait period each + // time until it reaches 500ms. After this point polling will continue every + // 500ms until $delay is reached. + + // $delay is passed in seconds, but we will be using usleep(), which takes + // microseconds as a parameter. Multiply it by 1 million so that all + // further numbers are equivalent. + $delay = (int) $delay * 1000000; + + // Begin sleeping at 25ms. + $sleep = 25000; + while ($delay > 0) { // This function should only be called by a request that failed to get a // lock, so we sleep first to give the parallel request a chance to finish // and release the lock. - sleep(1); + usleep($sleep); + // After each sleep, increase the value of $sleep until it reaches + // 500ms, to reduce the potential for a lock stampede. + $delay = $delay - $sleep; + $sleep = min(500000, $sleep + 25000, $delay); if (lock_may_be_available($name)) { // No longer need to wait. return FALSE; -- cgit v1.2.3