From c5afd254bd0147de752d72de40548842cf5afeed Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Thu, 29 Aug 2019 18:53:36 -0700 Subject: [PATCH] lockmanager: sort key by bucket in QuorumLockManager::doLockByType Change-Id: Ida9b3979a7d6ad36e669761f2e23eeef6c67efae --- .../libs/lockmanager/QuorumLockManager.php | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/includes/libs/lockmanager/QuorumLockManager.php b/includes/libs/lockmanager/QuorumLockManager.php index 950b283670..6478a61b14 100644 --- a/includes/libs/lockmanager/QuorumLockManager.php +++ b/includes/libs/lockmanager/QuorumLockManager.php @@ -38,7 +38,7 @@ abstract class QuorumLockManager extends LockManager { final protected function doLockByType( array $pathsByType ) { $status = StatusValue::newGood(); - $pathsToLock = []; // (bucket => type => paths) + $pathsByTypeByBucket = []; // (bucket => type => paths) // Get locks that need to be acquired (buckets => locks)... foreach ( $pathsByType as $type => $paths ) { foreach ( $paths as $path ) { @@ -46,23 +46,27 @@ abstract class QuorumLockManager extends LockManager { ++$this->locksHeld[$path][$type]; } else { $bucket = $this->getBucketFromPath( $path ); - $pathsToLock[$bucket][$type][] = $path; + $pathsByTypeByBucket[$bucket][$type][] = $path; } } } + // Acquire locks in each bucket in bucket order to reduce contention. Any blocking + // mutexes during the acquisition step will not involve circular waiting on buckets. + ksort( $pathsByTypeByBucket ); + $lockedPaths = []; // files locked in this attempt (type => paths) // Attempt to acquire these locks... - foreach ( $pathsToLock as $bucket => $pathsToLockByType ) { + foreach ( $pathsByTypeByBucket as $bucket => $bucketPathsByType ) { // Try to acquire the locks for this bucket - $status->merge( $this->doLockingRequestBucket( $bucket, $pathsToLockByType ) ); + $status->merge( $this->doLockingRequestBucket( $bucket, $bucketPathsByType ) ); if ( !$status->isOK() ) { $status->merge( $this->doUnlockByType( $lockedPaths ) ); return $status; } // Record these locks as active - foreach ( $pathsToLockByType as $type => $paths ) { + foreach ( $bucketPathsByType as $type => $paths ) { foreach ( $paths as $path ) { $this->locksHeld[$path][$type] = 1; // locked // Keep track of what locks were made in this attempt @@ -77,7 +81,7 @@ abstract class QuorumLockManager extends LockManager { protected function doUnlockByType( array $pathsByType ) { $status = StatusValue::newGood(); - $pathsToUnlock = []; // (bucket => type => paths) + $pathsByTypeByBucket = []; // (bucket => type => paths) foreach ( $pathsByType as $type => $paths ) { foreach ( $paths as $path ) { if ( !isset( $this->locksHeld[$path][$type] ) ) { @@ -88,7 +92,7 @@ abstract class QuorumLockManager extends LockManager { if ( $this->locksHeld[$path][$type] <= 0 ) { unset( $this->locksHeld[$path][$type] ); $bucket = $this->getBucketFromPath( $path ); - $pathsToUnlock[$bucket][$type][] = $path; + $pathsByTypeByBucket[$bucket][$type][] = $path; } if ( $this->locksHeld[$path] === [] ) { unset( $this->locksHeld[$path] ); // no SH or EX locks left for key @@ -99,8 +103,8 @@ abstract class QuorumLockManager extends LockManager { // Remove these specific locks if possible, or at least release // all locks once this process is currently not holding any locks. - foreach ( $pathsToUnlock as $bucket => $pathsToUnlockByType ) { - $status->merge( $this->doUnlockingRequestBucket( $bucket, $pathsToUnlockByType ) ); + foreach ( $pathsByTypeByBucket as $bucket => $bucketPathsByType ) { + $status->merge( $this->doUnlockingRequestBucket( $bucket, $bucketPathsByType ) ); } if ( $this->locksHeld === [] ) { $status->merge( $this->releaseAllLocks() ); -- 2.20.1