https://www.mediawiki.org/wiki/Special:Code/MediaWiki/107193

Revision: 107193
Author:   aaron
Date:     2011-12-24 00:16:06 +0000 (Sat, 24 Dec 2011)
Log Message:
-----------
In LockManager classes:
* Only use hash keys later on in the data flow rather than right when doLock() 
is called. This allows for error messages in Status objects to use human 
readable paths rather than ugly hash keys.
* Moved $locksHeld declaration duplication up to the base class.
* Fixed __destruct() in FSLockManager to not use bogus doSingleUnlock() lock 
type parameter.

Modified Paths:
--------------
    trunk/phase3/includes/filerepo/backend/lockmanager/DBLockManager.php
    trunk/phase3/includes/filerepo/backend/lockmanager/FSLockManager.php
    trunk/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php
    trunk/phase3/includes/filerepo/backend/lockmanager/LockManager.php

Modified: trunk/phase3/includes/filerepo/backend/lockmanager/DBLockManager.php
===================================================================
--- trunk/phase3/includes/filerepo/backend/lockmanager/DBLockManager.php        
2011-12-24 00:13:11 UTC (rev 107192)
+++ trunk/phase3/includes/filerepo/backend/lockmanager/DBLockManager.php        
2011-12-24 00:16:06 UTC (rev 107193)
@@ -27,8 +27,6 @@
        protected $safeDelay; // integer number of seconds
 
        protected $session = 0; // random integer
-       /** @var Array Map of (locked key => lock type => count) */
-       protected $locksHeld = array();
        /** @var Array Map Database connections (DB name => Database) */
        protected $conns = array();
 
@@ -86,66 +84,72 @@
                $this->session = wfBaseConvert( sha1( $this->session ), 16, 36, 
31 );
        }
 
-       protected function doLock( array $keys, $type ) {
+       /**
+        * @see LockManager::doLock()
+        */
+       protected function doLock( array $paths, $type ) {
                $status = Status::newGood();
 
-               $keysToLock = array();
+               $pathsToLock = array();
                // Get locks that need to be acquired (buckets => locks)...
-               foreach ( $keys as $key ) {
-                       if ( isset( $this->locksHeld[$key][$type] ) ) {
-                               ++$this->locksHeld[$key][$type];
-                       } elseif ( isset( $this->locksHeld[$key][self::LOCK_EX] 
) ) {
-                               $this->locksHeld[$key][$type] = 1;
+               foreach ( $paths as $path ) {
+                       if ( isset( $this->locksHeld[$path][$type] ) ) {
+                               ++$this->locksHeld[$path][$type];
+                       } elseif ( isset( 
$this->locksHeld[$path][self::LOCK_EX] ) ) {
+                               $this->locksHeld[$path][$type] = 1;
                        } else {
-                               $bucket = $this->getBucketFromKey( $key );
-                               $keysToLock[$bucket][] = $key;
+                               $bucket = $this->getBucketFromKey( $path );
+                               $pathsToLock[$bucket][] = $path;
                        }
                }
 
-               $lockedKeys = array(); // files locked in this attempt
+               $lockedPaths = array(); // files locked in this attempt
                // Attempt to acquire these locks...
-               foreach ( $keysToLock as $bucket => $keys ) {
+               foreach ( $pathsToLock as $bucket => $paths ) {
                        // Try to acquire the locks for this bucket
-                       $res = $this->doLockingQueryAll( $bucket, $keys, $type 
);
+                       $res = $this->doLockingQueryAll( $bucket, $paths, $type 
);
                        if ( $res === 'cantacquire' ) {
                                // Resources already locked by another process.
                                // Abort and unlock everything we just locked.
-                               $status->fatal( 
'lockmanager-fail-acquirelocks', implode( ', ', $keys ) );
-                               $status->merge( $this->doUnlock( $lockedKeys, 
$type ) );
+                               $status->fatal( 
'lockmanager-fail-acquirelocks', implode( ', ', $paths ) );
+                               $status->merge( $this->doUnlock( $lockedPaths, 
$type ) );
                                return $status;
                        } elseif ( $res !== true ) {
                                // Couldn't contact any DBs for this bucket.
                                // Abort and unlock everything we just locked.
                                $status->fatal( 'lockmanager-fail-db-bucket', 
$bucket );
-                               $status->merge( $this->doUnlock( $lockedKeys, 
$type ) );
+                               $status->merge( $this->doUnlock( $lockedPaths, 
$type ) );
                                return $status;
                        }
                        // Record these locks as active
-                       foreach ( $keys as $key ) {
-                               $this->locksHeld[$key][$type] = 1; // locked
+                       foreach ( $paths as $path ) {
+                               $this->locksHeld[$path][$type] = 1; // locked
                        }
                        // Keep track of what locks were made in this attempt
-                       $lockedKeys = array_merge( $lockedKeys, $keys );
+                       $lockedPaths = array_merge( $lockedPaths, $paths );
                }
 
                return $status;
        }
 
-       protected function doUnlock( array $keys, $type ) {
+       /**
+        * @see LockManager::doUnlock()
+        */
+       protected function doUnlock( array $paths, $type ) {
                $status = Status::newGood();
 
-               foreach ( $keys as $key ) {
-                       if ( !isset( $this->locksHeld[$key] ) ) {
-                               $status->warning( 'lockmanager-notlocked', $key 
);
-                       } elseif ( !isset( $this->locksHeld[$key][$type] ) ) {
-                               $status->warning( 'lockmanager-notlocked', $key 
);
+               foreach ( $paths as $path ) {
+                       if ( !isset( $this->locksHeld[$path] ) ) {
+                               $status->warning( 'lockmanager-notlocked', 
$path );
+                       } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
+                               $status->warning( 'lockmanager-notlocked', 
$path );
                        } else {
-                               --$this->locksHeld[$key][$type];
-                               if ( $this->locksHeld[$key][$type] <= 0 ) {
-                                       unset( $this->locksHeld[$key][$type] );
+                               --$this->locksHeld[$path][$type];
+                               if ( $this->locksHeld[$path][$type] <= 0 ) {
+                                       unset( $this->locksHeld[$path][$type] );
                                }
-                               if ( !count( $this->locksHeld[$key] ) ) {
-                                       unset( $this->locksHeld[$key] ); // no 
SH or EX locks left for key
+                               if ( !count( $this->locksHeld[$path] ) ) {
+                                       unset( $this->locksHeld[$path] ); // no 
SH or EX locks left for key
                                }
                        }
                }
@@ -159,21 +163,23 @@
        }
 
        /**
-        * Get a connection to a lock DB and acquire locks on $keys.
+        * Get a connection to a lock DB and acquire locks on $paths.
         * This does not use GET_LOCK() per 
http://bugs.mysql.com/bug.php?id=1118.
         *
         * @param $lockDb string
-        * @param $keys Array
+        * @param $paths Array
         * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
         * @return bool Resources able to be locked
         * @throws DBError
         */
-       protected function doLockingQuery( $lockDb, array $keys, $type ) {
+       protected function doLockingQuery( $lockDb, array $paths, $type ) {
                if ( $type == self::LOCK_EX ) { // writer locks
                        $db = $this->getConnection( $lockDb );
                        if ( !$db ) {
                                return false; // bad config
                        }
+                       $keys = array_unique( array_map( 
'LockManager::sha1Base36', $paths ) );
+                       # Build up values for INSERT clause
                        $data = array();
                        foreach ( $keys as $key ) {
                                $data[] = array( 'fle_key' => $key );
@@ -189,11 +195,11 @@
         * This should avoid throwing any exceptions.
         *
         * @param $bucket integer
-        * @param $keys Array List of resource keys to lock
+        * @param $paths Array List of resource keys to lock
         * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
         * @return bool|string One of (true, 'cantacquire', 'dberrors')
         */
-       protected function doLockingQueryAll( $bucket, array $keys, $type ) {
+       protected function doLockingQueryAll( $bucket, array $paths, $type ) {
                $yesVotes = 0; // locks made on trustable DBs
                $votesLeft = count( $this->dbsByBucket[$bucket] ); // remaining 
DBs
                $quorum = floor( $votesLeft/2 + 1 ); // simple majority
@@ -203,7 +209,7 @@
                        if ( $this->cacheCheckFailures( $lockDb ) ) {
                                try {
                                        // Attempt to acquire the lock on this 
DB
-                                       if ( !$this->doLockingQuery( $lockDb, 
$keys, $type ) ) {
+                                       if ( !$this->doLockingQuery( $lockDb, 
$paths, $type ) ) {
                                                return 'cantacquire'; // 
vetoed; resource locked
                                        }
                                        ++$yesVotes; // success for this peer
@@ -218,7 +224,7 @@
                                        }
                                }
                        }
-                       $votesLeft--;
+                       --$votesLeft;
                        $votesNeeded = $quorum - $yesVotes;
                        if ( $votesNeeded > $votesLeft ) {
                                // In "trust cache" mode we don't have to meet 
the quorum
@@ -322,8 +328,8 @@
         */
        protected function cacheCheckFailures( $lockDb ) {
                if ( $this->statusCache && $this->safeDelay > 0 ) {
-                       $key = $this->getMissKey( $lockDb );
-                       $misses = $this->statusCache->get( $key );
+                       $path = $this->getMissKey( $lockDb );
+                       $misses = $this->statusCache->get( $path );
                        return !$misses;
                }
                return true;
@@ -337,12 +343,12 @@
         */
        protected function cacheRecordFailure( $lockDb ) {
                if ( $this->statusCache && $this->safeDelay > 0 ) {
-                       $key = $this->getMissKey( $lockDb );
-                       $misses = $this->statusCache->get( $key );
+                       $path = $this->getMissKey( $lockDb );
+                       $misses = $this->statusCache->get( $path );
                        if ( $misses ) {
-                               return $this->statusCache->incr( $key );
+                               return $this->statusCache->incr( $path );
                        } else {
-                               return $this->statusCache->add( $key, 1, 
$this->safeDelay );
+                               return $this->statusCache->add( $path, 1, 
$this->safeDelay );
                        }
                }
                return true;
@@ -359,14 +365,14 @@
        }
 
        /**
-        * Get the bucket for lock key.
+        * Get the bucket for resource path.
         * This should avoid throwing any exceptions.
         *
-        * @param $key string (31 char hex key)
+        * @param $path string
         * @return integer
         */
-       protected function getBucketFromKey( $key ) {
-               $prefix = substr( $key, 0, 2 ); // first 2 hex chars (8 bits)
+       protected function getBucketFromKey( $path ) {
+               $prefix = substr( sha1( $path ), 0, 2 ); // first 2 hex chars 
(8 bits)
                return intval( base_convert( $prefix, 16, 10 ) ) % count( 
$this->dbsByBucket );
        }
 
@@ -406,11 +412,13 @@
                $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ 
UNCOMMITTED;" );
        }
 
-       protected function doLockingQuery( $lockDb, array $keys, $type ) {
+       protected function doLockingQuery( $lockDb, array $paths, $type ) {
                $db = $this->getConnection( $lockDb );
                if ( !$db ) {
                        return false;
                }
+               $keys = array_unique( array_map( 'LockManager::sha1Base36', 
$paths ) );
+               # Build up values for INSERT clause
                $data = array();
                foreach ( $keys as $key ) {
                        $data[] = array( 'fls_key' => $key, 'fls_session' => 
$this->session );
@@ -436,6 +444,7 @@
                                __METHOD__
                        );
                        if ( !$blocked ) {
+                               # Build up values for INSERT clause
                                $data = array();
                                foreach ( $keys as $key ) {
                                        $data[] = array( 'fle_key' => $key );

Modified: trunk/phase3/includes/filerepo/backend/lockmanager/FSLockManager.php
===================================================================
--- trunk/phase3/includes/filerepo/backend/lockmanager/FSLockManager.php        
2011-12-24 00:13:11 UTC (rev 107192)
+++ trunk/phase3/includes/filerepo/backend/lockmanager/FSLockManager.php        
2011-12-24 00:16:06 UTC (rev 107193)
@@ -21,8 +21,6 @@
 
        protected $lockDir; // global dir for all servers
 
-       /** @var Array Map of (locked key => lock type => count) */
-       protected $locksHeld = array();
        /** @var Array Map of (locked key => lock type => lock file handle) */
        protected $handles = array();
 
@@ -30,22 +28,22 @@
                $this->lockDir = $config['lockDirectory'];
        }
 
-       protected function doLock( array $keys, $type ) {
+       protected function doLock( array $paths, $type ) {
                $status = Status::newGood();
 
-               $lockedKeys = array(); // files locked in this attempt
-               foreach ( $keys as $key ) {
-                       $subStatus = $this->doSingleLock( $key, $type );
+               $lockedPaths = array(); // files locked in this attempt
+               foreach ( $paths as $path ) {
+                       $subStatus = $this->doSingleLock( $path, $type );
                        $status->merge( $subStatus );
                        if ( $status->isOK() ) {
-                               // Don't append to $lockedKeys if $key is 
already locked.
+                               // Don't append to $lockedPaths if $path is 
already locked.
                                // We do NOT want to unlock the key if we have 
to rollback.
                                if ( $subStatus->isGood() ) { // no 
warnings/fatals?
-                                       $lockedKeys[] = $key;
+                                       $lockedPaths[] = $path;
                                }
                        } else {
                                // Abort and unlock everything
-                               $status->merge( $this->doUnlock( $lockedKeys, 
$type ) );
+                               $status->merge( $this->doUnlock( $lockedPaths, 
$type ) );
                                return $status;
                        }
                }
@@ -53,11 +51,11 @@
                return $status;
        }
 
-       protected function doUnlock( array $keys, $type ) {
+       protected function doUnlock( array $paths, $type ) {
                $status = Status::newGood();
 
-               foreach ( $keys as $key ) {
-                       $status->merge( $this->doSingleUnlock( $key, $type ) );
+               foreach ( $paths as $path ) {
+                       $status->merge( $this->doSingleUnlock( $path, $type ) );
                }
 
                return $status;
@@ -66,38 +64,38 @@
        /**
         * Lock a single resource key
         *
-        * @param $key string
+        * @param $path string
         * @param $type integer
         * @return Status 
         */
-       protected function doSingleLock( $key, $type ) {
+       protected function doSingleLock( $path, $type ) {
                $status = Status::newGood();
 
-               if ( isset( $this->locksHeld[$key][$type] ) ) {
-                       ++$this->locksHeld[$key][$type];
-               } elseif ( isset( $this->locksHeld[$key][self::LOCK_EX] ) ) {
-                       $this->locksHeld[$key][$type] = 1;
+               if ( isset( $this->locksHeld[$path][$type] ) ) {
+                       ++$this->locksHeld[$path][$type];
+               } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
+                       $this->locksHeld[$path][$type] = 1;
                } else {
                        wfSuppressWarnings();
-                       $handle = fopen( $this->getLockPath( $key ), 'a+' );
+                       $handle = fopen( $this->getLockPath( $path ), 'a+' );
                        wfRestoreWarnings();
                        if ( !$handle ) { // lock dir missing?
                                wfMkdirParents( $this->lockDir );
-                               $handle = fopen( $this->getLockPath( $key ), 
'a+' ); // try again
+                               $handle = fopen( $this->getLockPath( $path ), 
'a+' ); // try again
                        }
                        if ( $handle ) {
                                // Either a shared or exclusive lock
                                $lock = ( $type == self::LOCK_SH ) ? LOCK_SH : 
LOCK_EX;
                                if ( flock( $handle, $lock | LOCK_NB ) ) {
                                        // Record this lock as active
-                                       $this->locksHeld[$key][$type] = 1;
-                                       $this->handles[$key][$type] = $handle;
+                                       $this->locksHeld[$path][$type] = 1;
+                                       $this->handles[$path][$type] = $handle;
                                } else {
                                        fclose( $handle );
-                                       $status->fatal( 
'lockmanager-fail-acquirelock', $key );
+                                       $status->fatal( 
'lockmanager-fail-acquirelock', $path );
                                }
                        } else {
-                               $status->fatal( 'lockmanager-fail-openlock', 
$key );
+                               $status->fatal( 'lockmanager-fail-openlock', 
$path );
                        }
                }
 
@@ -107,28 +105,28 @@
        /**
         * Unlock a single resource key
         * 
-        * @param $key string
+        * @param $path string
         * @param $type integer
         * @return Status 
         */
-       protected function doSingleUnlock( $key, $type ) {
+       protected function doSingleUnlock( $path, $type ) {
                $status = Status::newGood();
 
-               if ( !isset( $this->locksHeld[$key] ) ) {
-                       $status->warning( 'lockmanager-notlocked', $key );
-               } elseif ( !isset( $this->locksHeld[$key][$type] ) ) {
-                       $status->warning( 'lockmanager-notlocked', $key );
+               if ( !isset( $this->locksHeld[$path] ) ) {
+                       $status->warning( 'lockmanager-notlocked', $path );
+               } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
+                       $status->warning( 'lockmanager-notlocked', $path );
                } else {
                        $handlesToClose = array();
-                       --$this->locksHeld[$key][$type];
-                       if ( $this->locksHeld[$key][$type] <= 0 ) {
-                               unset( $this->locksHeld[$key][$type] );
+                       --$this->locksHeld[$path][$type];
+                       if ( $this->locksHeld[$path][$type] <= 0 ) {
+                               unset( $this->locksHeld[$path][$type] );
                                // If a LOCK_SH comes in while we have a 
LOCK_EX, we don't
                                // actually add a handler, so check for handler 
existence.
-                               if ( isset( $this->handles[$key][$type] ) ) {
+                               if ( isset( $this->handles[$path][$type] ) ) {
                                        // Mark this handle to be unlocked and 
closed
-                                       $handlesToClose[] = 
$this->handles[$key][$type];
-                                       unset( $this->handles[$key][$type] );
+                                       $handlesToClose[] = 
$this->handles[$path][$type];
+                                       unset( $this->handles[$path][$type] );
                                }
                        }
                        // Unlock handles to release locks and delete
@@ -136,62 +134,64 @@
                        if ( wfIsWindows() ) {
                                // Windows: for any process, including this one,
                                // calling unlink() on a locked file will fail
-                               $status->merge( $this->closeLockHandles( $key, 
$handlesToClose ) );
-                               $status->merge( $this->pruneKeyLockFiles( $key 
) );
+                               $status->merge( $this->closeLockHandles( $path, 
$handlesToClose ) );
+                               $status->merge( $this->pruneKeyLockFiles( $path 
) );
                        } else {
                                // Unix: unlink() can be used on files 
currently open by this 
                                // process and we must do so in order to avoid 
race conditions
-                               $status->merge( $this->pruneKeyLockFiles( $key 
) );
-                               $status->merge( $this->closeLockHandles( $key, 
$handlesToClose ) );
+                               $status->merge( $this->pruneKeyLockFiles( $path 
) );
+                               $status->merge( $this->closeLockHandles( $path, 
$handlesToClose ) );
                        }
                }
 
                return $status;
        }
 
-       private function closeLockHandles( $key, array $handlesToClose ) {
+       private function closeLockHandles( $path, array $handlesToClose ) {
                $status = Status::newGood();
                foreach ( $handlesToClose as $handle ) {
                        wfSuppressWarnings();
                        if ( !flock( $handle, LOCK_UN ) ) {
-                               $status->fatal( 'lockmanager-fail-releaselock', 
$key );
+                               $status->fatal( 'lockmanager-fail-releaselock', 
$path );
                        }
                        if ( !fclose( $handle ) ) {
-                               $status->warning( 'lockmanager-fail-closelock', 
$key );
+                               $status->warning( 'lockmanager-fail-closelock', 
$path );
                        }
                        wfRestoreWarnings();
                }
                return $status;
        }
 
-       private function pruneKeyLockFiles( $key ) {
+       private function pruneKeyLockFiles( $path ) {
                $status = Status::newGood();
-               if ( !count( $this->locksHeld[$key] ) ) {
+               if ( !count( $this->locksHeld[$path] ) ) {
                        wfSuppressWarnings();
                        # No locks are held for the lock file anymore
-                       if ( !unlink( $this->getLockPath( $key ) ) ) {
-                               $status->warning( 
'lockmanager-fail-deletelock', $key );
+                       if ( !unlink( $this->getLockPath( $path ) ) ) {
+                               $status->warning( 
'lockmanager-fail-deletelock', $path );
                        }
                        wfRestoreWarnings();
-                       unset( $this->locksHeld[$key] );
-                       unset( $this->handles[$key] );
+                       unset( $this->locksHeld[$path] );
+                       unset( $this->handles[$path] );
                }
                return $status;
        }
 
        /**
         * Get the path to the lock file for a key
-        * @param $key string
+        * @param $path string
         * @return string
         */
-       protected function getLockPath( $key ) {
-               return "{$this->lockDir}/{$key}.lock";
+       protected function getLockPath( $path ) {
+               $hash = self::sha1Base36( $path );
+               return "{$this->lockDir}/{$hash}.lock";
        }
 
        function __destruct() {
                // Make sure remaining locks get cleared for sanity
-               foreach ( $this->locksHeld as $key => $locks ) {
-                       $this->doSingleUnlock( $key, 0 );
+               foreach ( $this->locksHeld as $path => $locks ) {
+                       $this->doSingleUnlock( $path, self::LOCK_EX );
+                       $this->doSingleUnlock( $path, self::LOCK_SH );
                }
        }
 }

Modified: trunk/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php
===================================================================
--- trunk/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php        
2011-12-24 00:13:11 UTC (rev 107192)
+++ trunk/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php        
2011-12-24 00:16:06 UTC (rev 107193)
@@ -25,8 +25,6 @@
        /** @var Array Map of bucket indexes to peer server lists */
        protected $srvsByBucket; // (bucket index => (lsrv1, lsrv2, ...))
 
-       /** @var Array Map of (locked key => lock type => count) */
-       protected $locksHeld = array();
        /** @var Array Map Server connections (server name => resource) */
        protected $conns = array();
 
@@ -66,66 +64,66 @@
                $this->session = wfBaseConvert( sha1( $this->session ), 16, 36, 
31 );
        }
 
-       protected function doLock( array $keys, $type ) {
+       protected function doLock( array $paths, $type ) {
                $status = Status::newGood();
 
-               $keysToLock = array();
+               $pathsToLock = array();
                // Get locks that need to be acquired (buckets => locks)...
-               foreach ( $keys as $key ) {
-                       if ( isset( $this->locksHeld[$key][$type] ) ) {
-                               ++$this->locksHeld[$key][$type];
-                       } elseif ( isset( $this->locksHeld[$key][self::LOCK_EX] 
) ) {
-                               $this->locksHeld[$key][$type] = 1;
+               foreach ( $paths as $path ) {
+                       if ( isset( $this->locksHeld[$path][$type] ) ) {
+                               ++$this->locksHeld[$path][$type];
+                       } elseif ( isset( 
$this->locksHeld[$path][self::LOCK_EX] ) ) {
+                               $this->locksHeld[$path][$type] = 1;
                        } else {
-                               $bucket = $this->getBucketFromKey( $key );
-                               $keysToLock[$bucket][] = $key;
+                               $bucket = $this->getBucketFromKey( $path );
+                               $pathsToLock[$bucket][] = $path;
                        }
                }
 
-               $lockedKeys = array(); // files locked in this attempt
+               $lockedPaths = array(); // files locked in this attempt
                // Attempt to acquire these locks...
-               foreach ( $keysToLock as $bucket => $keys ) {
+               foreach ( $pathsToLock as $bucket => $paths ) {
                        // Try to acquire the locks for this bucket
-                       $res = $this->doLockingRequestAll( $bucket, $keys, 
$type );
+                       $res = $this->doLockingRequestAll( $bucket, $paths, 
$type );
                        if ( $res === 'cantacquire' ) {
                                // Resources already locked by another process.
                                // Abort and unlock everything we just locked.
-                               $status->fatal( 
'lockmanager-fail-acquirelocks', implode( ', ', $keys ) );
-                               $status->merge( $this->doUnlock( $lockedKeys, 
$type ) );
+                               $status->fatal( 
'lockmanager-fail-acquirelocks', implode( ', ', $paths ) );
+                               $status->merge( $this->doUnlock( $lockedPaths, 
$type ) );
                                return $status;
                        } elseif ( $res !== true ) {
                                // Couldn't contact any servers for this bucket.
                                // Abort and unlock everything we just locked.
-                               $status->fatal( 
'lockmanager-fail-acquirelocks', implode( ', ', $keys ) );
-                               $status->merge( $this->doUnlock( $lockedKeys, 
$type ) );
+                               $status->fatal( 
'lockmanager-fail-acquirelocks', implode( ', ', $paths ) );
+                               $status->merge( $this->doUnlock( $lockedPaths, 
$type ) );
                                return $status;
                        }
                        // Record these locks as active
-                       foreach ( $keys as $key ) {
-                               $this->locksHeld[$key][$type] = 1; // locked
+                       foreach ( $paths as $path ) {
+                               $this->locksHeld[$path][$type] = 1; // locked
                        }
                        // Keep track of what locks were made in this attempt
-                       $lockedKeys = array_merge( $lockedKeys, $keys );
+                       $lockedPaths = array_merge( $lockedPaths, $paths );
                }
 
                return $status;
        }
 
-       protected function doUnlock( array $keys, $type ) {
+       protected function doUnlock( array $paths, $type ) {
                $status = Status::newGood();
 
-               foreach ( $keys as $key ) {
-                       if ( !isset( $this->locksHeld[$key] ) ) {
-                               $status->warning( 'lockmanager-notlocked', $key 
);
-                       } elseif ( !isset( $this->locksHeld[$key][$type] ) ) {
-                               $status->warning( 'lockmanager-notlocked', $key 
);
+               foreach ( $paths as $path ) {
+                       if ( !isset( $this->locksHeld[$path] ) ) {
+                               $status->warning( 'lockmanager-notlocked', 
$path );
+                       } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
+                               $status->warning( 'lockmanager-notlocked', 
$path );
                        } else {
-                               --$this->locksHeld[$key][$type];
-                               if ( $this->locksHeld[$key][$type] <= 0 ) {
-                                       unset( $this->locksHeld[$key][$type] );
+                               --$this->locksHeld[$path][$type];
+                               if ( $this->locksHeld[$path][$type] <= 0 ) {
+                                       unset( $this->locksHeld[$path][$type] );
                                }
-                               if ( !count( $this->locksHeld[$key] ) ) {
-                                       unset( $this->locksHeld[$key] ); // no 
SH or EX locks left for key
+                               if ( !count( $this->locksHeld[$path] ) ) {
+                                       unset( $this->locksHeld[$path] ); // no 
SH or EX locks left for key
                                }
                        }
                }
@@ -139,14 +137,14 @@
        }
 
        /**
-        * Get a connection to a lock server and acquire locks on $keys
+        * Get a connection to a lock server and acquire locks on $paths
         *
         * @param $lockSrv string
-        * @param $keys Array
+        * @param $paths Array
         * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
         * @return bool Resources able to be locked
         */
-       protected function doLockingRequest( $lockSrv, array $keys, $type ) {
+       protected function doLockingRequest( $lockSrv, array $paths, $type ) {
                if ( $type == self::LOCK_SH ) { // reader locks
                        $type = 'SH';
                } elseif ( $type == self::LOCK_EX ) { // writer locks
@@ -156,6 +154,7 @@
                }
 
                // Send out the command and get the response...
+               $keys = array_unique( array_map( 'LockManager::sha1Base36', 
$paths ) );
                $response = $this->sendCommand( $lockSrv, 'ACQUIRE', $type, 
$keys );
 
                return ( $response === 'ACQUIRED' );
@@ -195,25 +194,25 @@
         * Attempt to acquire locks with the peers for a bucket
         *
         * @param $bucket integer
-        * @param $keys Array List of resource keys to lock
+        * @param $paths Array List of resource keys to lock
         * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
         * @return bool|string One of (true, 'cantacquire', 'srverrors')
         */
-       protected function doLockingRequestAll( $bucket, array $keys, $type ) {
+       protected function doLockingRequestAll( $bucket, array $paths, $type ) {
                $yesVotes = 0; // locks made on trustable servers
                $votesLeft = count( $this->srvsByBucket[$bucket] ); // 
remaining peers
                $quorum = floor( $votesLeft/2 + 1 ); // simple majority
                // Get votes for each peer, in order, until we have enough...
                foreach ( $this->srvsByBucket[$bucket] as $index => $lockSrv ) {
                        // Attempt to acquire the lock on this peer
-                       if ( !$this->doLockingRequest( $lockSrv, $keys, $type ) 
) {
+                       if ( !$this->doLockingRequest( $lockSrv, $paths, $type 
) ) {
                                return 'cantacquire'; // vetoed; resource locked
                        }
                        ++$yesVotes; // success for this peer
                        if ( $yesVotes >= $quorum ) {
                                return true; // lock obtained
                        }
-                       $votesLeft--;
+                       --$votesLeft;
                        $votesNeeded = $quorum - $yesVotes;
                        if ( $votesNeeded > $votesLeft ) {
                                // In "trust cache" mode we don't have to meet 
the quorum
@@ -265,14 +264,15 @@
        }
 
        /**
-        * Get the bucket for lock key
+        * Get the bucket for resource path.
+        * This should avoid throwing any exceptions.
         *
-        * @param $key string (31 char hex key)
+        * @param $path string
         * @return integer
         */
-       protected function getBucketFromKey( $key ) {
-               $prefix = substr( $key, 0, 2 ); // first 2 hex chars (8 bits)
-               return intval( base_convert( $prefix, 16, 10 ) ) % count( 
$this->srvsByBucket );
+       protected function getBucketFromKey( $path ) {
+               $prefix = substr( sha1( $path ), 0, 2 ); // first 2 hex chars 
(8 bits)
+               return intval( base_convert( $prefix, 16, 10 ) ) % count( 
$this->dbsByBucket );
        }
 
        /**

Modified: trunk/phase3/includes/filerepo/backend/lockmanager/LockManager.php
===================================================================
--- trunk/phase3/includes/filerepo/backend/lockmanager/LockManager.php  
2011-12-24 00:13:11 UTC (rev 107192)
+++ trunk/phase3/includes/filerepo/backend/lockmanager/LockManager.php  
2011-12-24 00:16:06 UTC (rev 107193)
@@ -20,11 +20,6 @@
  * @since 1.19
  */
 abstract class LockManager {
-       /* Lock types; stronger locks have higher values */
-       const LOCK_SH = 1; // shared lock (for reads)
-       const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
-       const LOCK_EX = 3; // exclusive lock (for writes)
-
        /** @var Array Mapping of lock types to the type actually used */
        protected $lockTypeMap = array(
                self::LOCK_SH => self::LOCK_SH,
@@ -32,6 +27,14 @@
                self::LOCK_EX => self::LOCK_EX
        );
 
+       /** @var Array Map of (resource path => lock type => count) */
+       protected $locksHeld = array();
+
+       /* Lock types; stronger locks have higher values */
+       const LOCK_SH = 1; // shared lock (for reads)
+       const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
+       const LOCK_EX = 3; // exclusive lock (for writes)
+
        /**
         * Construct a new instance from configuration
         *
@@ -47,8 +50,7 @@
         * @return Status 
         */
        final public function lock( array $paths, $type = self::LOCK_EX ) {
-               $keys = array_unique( array_map( 'LockManager::sha1Base36', 
$paths ) );
-               return $this->doLock( $keys, $this->lockTypeMap[$type] );
+               return $this->doLock( array_unique( $paths ), 
$this->lockTypeMap[$type] );
        }
 
        /**
@@ -59,8 +61,7 @@
         * @return Status 
         */
        final public function unlock( array $paths, $type = self::LOCK_EX ) {
-               $keys = array_unique( array_map( 'LockManager::sha1Base36', 
$paths ) );
-               return $this->doUnlock( $keys, $this->lockTypeMap[$type] );
+               return $this->doUnlock( array_unique( $paths ), 
$this->lockTypeMap[$type] );
        }
 
        /**
@@ -76,20 +77,20 @@
        /**
         * Lock resources with the given keys and lock type
         * 
-        * @param $key Array List of keys to lock (40 char hex hashes)
+        * @param $paths Array List of storage paths
         * @param $type integer LockManager::LOCK_* constant
         * @return string
         */
-       abstract protected function doLock( array $keys, $type );
+       abstract protected function doLock( array $paths, $type );
 
        /**
         * Unlock resources with the given keys and lock type
         * 
-        * @param $key Array List of keys to unlock (40 char hex hashes)
+        * @param $paths Array List of storage paths
         * @param $type integer LockManager::LOCK_* constant
         * @return string
         */
-       abstract protected function doUnlock( array $keys, $type );
+       abstract protected function doUnlock( array $paths, $type );
 }
 
 /**
@@ -162,11 +163,11 @@
  * Simple version of LockManager that does nothing
  */
 class NullLockManager extends LockManager {
-       protected function doLock( array $keys, $type ) {
+       protected function doLock( array $paths, $type ) {
                return Status::newGood();
        }
 
-       protected function doUnlock( array $keys, $type ) {
+       protected function doUnlock( array $paths, $type ) {
                return Status::newGood();
        }
 }


_______________________________________________
MediaWiki-CVS mailing list
MediaWiki-CVS@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to