r107193 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107192‎ | r107193 | r107194 >
Date:00:16, 24 December 2011
Author:aaron
Status:ok (Comments)
Tags:
Comment:
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 (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/lockmanager/FSLockManager.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/lockmanager/LockManager.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/filerepo/backend/lockmanager/DBLockManager.php
@@ -27,8 +27,6 @@
2828 protected $safeDelay; // integer number of seconds
2929
3030 protected $session = 0; // random integer
31 - /** @var Array Map of (locked key => lock type => count) */
32 - protected $locksHeld = array();
3331 /** @var Array Map Database connections (DB name => Database) */
3432 protected $conns = array();
3533
@@ -86,66 +84,72 @@
8785 $this->session = wfBaseConvert( sha1( $this->session ), 16, 36, 31 );
8886 }
8987
90 - protected function doLock( array $keys, $type ) {
 88+ /**
 89+ * @see LockManager::doLock()
 90+ */
 91+ protected function doLock( array $paths, $type ) {
9192 $status = Status::newGood();
9293
93 - $keysToLock = array();
 94+ $pathsToLock = array();
9495 // Get locks that need to be acquired (buckets => locks)...
95 - foreach ( $keys as $key ) {
96 - if ( isset( $this->locksHeld[$key][$type] ) ) {
97 - ++$this->locksHeld[$key][$type];
98 - } elseif ( isset( $this->locksHeld[$key][self::LOCK_EX] ) ) {
99 - $this->locksHeld[$key][$type] = 1;
 96+ foreach ( $paths as $path ) {
 97+ if ( isset( $this->locksHeld[$path][$type] ) ) {
 98+ ++$this->locksHeld[$path][$type];
 99+ } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
 100+ $this->locksHeld[$path][$type] = 1;
100101 } else {
101 - $bucket = $this->getBucketFromKey( $key );
102 - $keysToLock[$bucket][] = $key;
 102+ $bucket = $this->getBucketFromKey( $path );
 103+ $pathsToLock[$bucket][] = $path;
103104 }
104105 }
105106
106 - $lockedKeys = array(); // files locked in this attempt
 107+ $lockedPaths = array(); // files locked in this attempt
107108 // Attempt to acquire these locks...
108 - foreach ( $keysToLock as $bucket => $keys ) {
 109+ foreach ( $pathsToLock as $bucket => $paths ) {
109110 // Try to acquire the locks for this bucket
110 - $res = $this->doLockingQueryAll( $bucket, $keys, $type );
 111+ $res = $this->doLockingQueryAll( $bucket, $paths, $type );
111112 if ( $res === 'cantacquire' ) {
112113 // Resources already locked by another process.
113114 // Abort and unlock everything we just locked.
114 - $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $keys ) );
115 - $status->merge( $this->doUnlock( $lockedKeys, $type ) );
 115+ $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $paths ) );
 116+ $status->merge( $this->doUnlock( $lockedPaths, $type ) );
116117 return $status;
117118 } elseif ( $res !== true ) {
118119 // Couldn't contact any DBs for this bucket.
119120 // Abort and unlock everything we just locked.
120121 $status->fatal( 'lockmanager-fail-db-bucket', $bucket );
121 - $status->merge( $this->doUnlock( $lockedKeys, $type ) );
 122+ $status->merge( $this->doUnlock( $lockedPaths, $type ) );
122123 return $status;
123124 }
124125 // Record these locks as active
125 - foreach ( $keys as $key ) {
126 - $this->locksHeld[$key][$type] = 1; // locked
 126+ foreach ( $paths as $path ) {
 127+ $this->locksHeld[$path][$type] = 1; // locked
127128 }
128129 // Keep track of what locks were made in this attempt
129 - $lockedKeys = array_merge( $lockedKeys, $keys );
 130+ $lockedPaths = array_merge( $lockedPaths, $paths );
130131 }
131132
132133 return $status;
133134 }
134135
135 - protected function doUnlock( array $keys, $type ) {
 136+ /**
 137+ * @see LockManager::doUnlock()
 138+ */
 139+ protected function doUnlock( array $paths, $type ) {
136140 $status = Status::newGood();
137141
138 - foreach ( $keys as $key ) {
139 - if ( !isset( $this->locksHeld[$key] ) ) {
140 - $status->warning( 'lockmanager-notlocked', $key );
141 - } elseif ( !isset( $this->locksHeld[$key][$type] ) ) {
142 - $status->warning( 'lockmanager-notlocked', $key );
 142+ foreach ( $paths as $path ) {
 143+ if ( !isset( $this->locksHeld[$path] ) ) {
 144+ $status->warning( 'lockmanager-notlocked', $path );
 145+ } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
 146+ $status->warning( 'lockmanager-notlocked', $path );
143147 } else {
144 - --$this->locksHeld[$key][$type];
145 - if ( $this->locksHeld[$key][$type] <= 0 ) {
146 - unset( $this->locksHeld[$key][$type] );
 148+ --$this->locksHeld[$path][$type];
 149+ if ( $this->locksHeld[$path][$type] <= 0 ) {
 150+ unset( $this->locksHeld[$path][$type] );
147151 }
148 - if ( !count( $this->locksHeld[$key] ) ) {
149 - unset( $this->locksHeld[$key] ); // no SH or EX locks left for key
 152+ if ( !count( $this->locksHeld[$path] ) ) {
 153+ unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
150154 }
151155 }
152156 }
@@ -159,21 +163,23 @@
160164 }
161165
162166 /**
163 - * Get a connection to a lock DB and acquire locks on $keys.
 167+ * Get a connection to a lock DB and acquire locks on $paths.
164168 * This does not use GET_LOCK() per http://bugs.mysql.com/bug.php?id=1118.
165169 *
166170 * @param $lockDb string
167 - * @param $keys Array
 171+ * @param $paths Array
168172 * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
169173 * @return bool Resources able to be locked
170174 * @throws DBError
171175 */
172 - protected function doLockingQuery( $lockDb, array $keys, $type ) {
 176+ protected function doLockingQuery( $lockDb, array $paths, $type ) {
173177 if ( $type == self::LOCK_EX ) { // writer locks
174178 $db = $this->getConnection( $lockDb );
175179 if ( !$db ) {
176180 return false; // bad config
177181 }
 182+ $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
 183+ # Build up values for INSERT clause
178184 $data = array();
179185 foreach ( $keys as $key ) {
180186 $data[] = array( 'fle_key' => $key );
@@ -189,11 +195,11 @@
190196 * This should avoid throwing any exceptions.
191197 *
192198 * @param $bucket integer
193 - * @param $keys Array List of resource keys to lock
 199+ * @param $paths Array List of resource keys to lock
194200 * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
195201 * @return bool|string One of (true, 'cantacquire', 'dberrors')
196202 */
197 - protected function doLockingQueryAll( $bucket, array $keys, $type ) {
 203+ protected function doLockingQueryAll( $bucket, array $paths, $type ) {
198204 $yesVotes = 0; // locks made on trustable DBs
199205 $votesLeft = count( $this->dbsByBucket[$bucket] ); // remaining DBs
200206 $quorum = floor( $votesLeft/2 + 1 ); // simple majority
@@ -203,7 +209,7 @@
204210 if ( $this->cacheCheckFailures( $lockDb ) ) {
205211 try {
206212 // Attempt to acquire the lock on this DB
207 - if ( !$this->doLockingQuery( $lockDb, $keys, $type ) ) {
 213+ if ( !$this->doLockingQuery( $lockDb, $paths, $type ) ) {
208214 return 'cantacquire'; // vetoed; resource locked
209215 }
210216 ++$yesVotes; // success for this peer
@@ -218,7 +224,7 @@
219225 }
220226 }
221227 }
222 - $votesLeft--;
 228+ --$votesLeft;
223229 $votesNeeded = $quorum - $yesVotes;
224230 if ( $votesNeeded > $votesLeft ) {
225231 // In "trust cache" mode we don't have to meet the quorum
@@ -322,8 +328,8 @@
323329 */
324330 protected function cacheCheckFailures( $lockDb ) {
325331 if ( $this->statusCache && $this->safeDelay > 0 ) {
326 - $key = $this->getMissKey( $lockDb );
327 - $misses = $this->statusCache->get( $key );
 332+ $path = $this->getMissKey( $lockDb );
 333+ $misses = $this->statusCache->get( $path );
328334 return !$misses;
329335 }
330336 return true;
@@ -337,12 +343,12 @@
338344 */
339345 protected function cacheRecordFailure( $lockDb ) {
340346 if ( $this->statusCache && $this->safeDelay > 0 ) {
341 - $key = $this->getMissKey( $lockDb );
342 - $misses = $this->statusCache->get( $key );
 347+ $path = $this->getMissKey( $lockDb );
 348+ $misses = $this->statusCache->get( $path );
343349 if ( $misses ) {
344 - return $this->statusCache->incr( $key );
 350+ return $this->statusCache->incr( $path );
345351 } else {
346 - return $this->statusCache->add( $key, 1, $this->safeDelay );
 352+ return $this->statusCache->add( $path, 1, $this->safeDelay );
347353 }
348354 }
349355 return true;
@@ -359,14 +365,14 @@
360366 }
361367
362368 /**
363 - * Get the bucket for lock key.
 369+ * Get the bucket for resource path.
364370 * This should avoid throwing any exceptions.
365371 *
366 - * @param $key string (31 char hex key)
 372+ * @param $path string
367373 * @return integer
368374 */
369 - protected function getBucketFromKey( $key ) {
370 - $prefix = substr( $key, 0, 2 ); // first 2 hex chars (8 bits)
 375+ protected function getBucketFromKey( $path ) {
 376+ $prefix = substr( sha1( $path ), 0, 2 ); // first 2 hex chars (8 bits)
371377 return intval( base_convert( $prefix, 16, 10 ) ) % count( $this->dbsByBucket );
372378 }
373379
@@ -406,11 +412,13 @@
407413 $db->query( "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;" );
408414 }
409415
410 - protected function doLockingQuery( $lockDb, array $keys, $type ) {
 416+ protected function doLockingQuery( $lockDb, array $paths, $type ) {
411417 $db = $this->getConnection( $lockDb );
412418 if ( !$db ) {
413419 return false;
414420 }
 421+ $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
 422+ # Build up values for INSERT clause
415423 $data = array();
416424 foreach ( $keys as $key ) {
417425 $data[] = array( 'fls_key' => $key, 'fls_session' => $this->session );
@@ -436,6 +444,7 @@
437445 __METHOD__
438446 );
439447 if ( !$blocked ) {
 448+ # Build up values for INSERT clause
440449 $data = array();
441450 foreach ( $keys as $key ) {
442451 $data[] = array( 'fle_key' => $key );
Index: trunk/phase3/includes/filerepo/backend/lockmanager/FSLockManager.php
@@ -21,8 +21,6 @@
2222
2323 protected $lockDir; // global dir for all servers
2424
25 - /** @var Array Map of (locked key => lock type => count) */
26 - protected $locksHeld = array();
2725 /** @var Array Map of (locked key => lock type => lock file handle) */
2826 protected $handles = array();
2927
@@ -30,22 +28,22 @@
3129 $this->lockDir = $config['lockDirectory'];
3230 }
3331
34 - protected function doLock( array $keys, $type ) {
 32+ protected function doLock( array $paths, $type ) {
3533 $status = Status::newGood();
3634
37 - $lockedKeys = array(); // files locked in this attempt
38 - foreach ( $keys as $key ) {
39 - $subStatus = $this->doSingleLock( $key, $type );
 35+ $lockedPaths = array(); // files locked in this attempt
 36+ foreach ( $paths as $path ) {
 37+ $subStatus = $this->doSingleLock( $path, $type );
4038 $status->merge( $subStatus );
4139 if ( $status->isOK() ) {
42 - // Don't append to $lockedKeys if $key is already locked.
 40+ // Don't append to $lockedPaths if $path is already locked.
4341 // We do NOT want to unlock the key if we have to rollback.
4442 if ( $subStatus->isGood() ) { // no warnings/fatals?
45 - $lockedKeys[] = $key;
 43+ $lockedPaths[] = $path;
4644 }
4745 } else {
4846 // Abort and unlock everything
49 - $status->merge( $this->doUnlock( $lockedKeys, $type ) );
 47+ $status->merge( $this->doUnlock( $lockedPaths, $type ) );
5048 return $status;
5149 }
5250 }
@@ -53,11 +51,11 @@
5452 return $status;
5553 }
5654
57 - protected function doUnlock( array $keys, $type ) {
 55+ protected function doUnlock( array $paths, $type ) {
5856 $status = Status::newGood();
5957
60 - foreach ( $keys as $key ) {
61 - $status->merge( $this->doSingleUnlock( $key, $type ) );
 58+ foreach ( $paths as $path ) {
 59+ $status->merge( $this->doSingleUnlock( $path, $type ) );
6260 }
6361
6462 return $status;
@@ -66,38 +64,38 @@
6765 /**
6866 * Lock a single resource key
6967 *
70 - * @param $key string
 68+ * @param $path string
7169 * @param $type integer
7270 * @return Status
7371 */
74 - protected function doSingleLock( $key, $type ) {
 72+ protected function doSingleLock( $path, $type ) {
7573 $status = Status::newGood();
7674
77 - if ( isset( $this->locksHeld[$key][$type] ) ) {
78 - ++$this->locksHeld[$key][$type];
79 - } elseif ( isset( $this->locksHeld[$key][self::LOCK_EX] ) ) {
80 - $this->locksHeld[$key][$type] = 1;
 75+ if ( isset( $this->locksHeld[$path][$type] ) ) {
 76+ ++$this->locksHeld[$path][$type];
 77+ } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
 78+ $this->locksHeld[$path][$type] = 1;
8179 } else {
8280 wfSuppressWarnings();
83 - $handle = fopen( $this->getLockPath( $key ), 'a+' );
 81+ $handle = fopen( $this->getLockPath( $path ), 'a+' );
8482 wfRestoreWarnings();
8583 if ( !$handle ) { // lock dir missing?
8684 wfMkdirParents( $this->lockDir );
87 - $handle = fopen( $this->getLockPath( $key ), 'a+' ); // try again
 85+ $handle = fopen( $this->getLockPath( $path ), 'a+' ); // try again
8886 }
8987 if ( $handle ) {
9088 // Either a shared or exclusive lock
9189 $lock = ( $type == self::LOCK_SH ) ? LOCK_SH : LOCK_EX;
9290 if ( flock( $handle, $lock | LOCK_NB ) ) {
9391 // Record this lock as active
94 - $this->locksHeld[$key][$type] = 1;
95 - $this->handles[$key][$type] = $handle;
 92+ $this->locksHeld[$path][$type] = 1;
 93+ $this->handles[$path][$type] = $handle;
9694 } else {
9795 fclose( $handle );
98 - $status->fatal( 'lockmanager-fail-acquirelock', $key );
 96+ $status->fatal( 'lockmanager-fail-acquirelock', $path );
9997 }
10098 } else {
101 - $status->fatal( 'lockmanager-fail-openlock', $key );
 99+ $status->fatal( 'lockmanager-fail-openlock', $path );
102100 }
103101 }
104102
@@ -107,28 +105,28 @@
108106 /**
109107 * Unlock a single resource key
110108 *
111 - * @param $key string
 109+ * @param $path string
112110 * @param $type integer
113111 * @return Status
114112 */
115 - protected function doSingleUnlock( $key, $type ) {
 113+ protected function doSingleUnlock( $path, $type ) {
116114 $status = Status::newGood();
117115
118 - if ( !isset( $this->locksHeld[$key] ) ) {
119 - $status->warning( 'lockmanager-notlocked', $key );
120 - } elseif ( !isset( $this->locksHeld[$key][$type] ) ) {
121 - $status->warning( 'lockmanager-notlocked', $key );
 116+ if ( !isset( $this->locksHeld[$path] ) ) {
 117+ $status->warning( 'lockmanager-notlocked', $path );
 118+ } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
 119+ $status->warning( 'lockmanager-notlocked', $path );
122120 } else {
123121 $handlesToClose = array();
124 - --$this->locksHeld[$key][$type];
125 - if ( $this->locksHeld[$key][$type] <= 0 ) {
126 - unset( $this->locksHeld[$key][$type] );
 122+ --$this->locksHeld[$path][$type];
 123+ if ( $this->locksHeld[$path][$type] <= 0 ) {
 124+ unset( $this->locksHeld[$path][$type] );
127125 // If a LOCK_SH comes in while we have a LOCK_EX, we don't
128126 // actually add a handler, so check for handler existence.
129 - if ( isset( $this->handles[$key][$type] ) ) {
 127+ if ( isset( $this->handles[$path][$type] ) ) {
130128 // Mark this handle to be unlocked and closed
131 - $handlesToClose[] = $this->handles[$key][$type];
132 - unset( $this->handles[$key][$type] );
 129+ $handlesToClose[] = $this->handles[$path][$type];
 130+ unset( $this->handles[$path][$type] );
133131 }
134132 }
135133 // Unlock handles to release locks and delete
@@ -136,62 +134,64 @@
137135 if ( wfIsWindows() ) {
138136 // Windows: for any process, including this one,
139137 // calling unlink() on a locked file will fail
140 - $status->merge( $this->closeLockHandles( $key, $handlesToClose ) );
141 - $status->merge( $this->pruneKeyLockFiles( $key ) );
 138+ $status->merge( $this->closeLockHandles( $path, $handlesToClose ) );
 139+ $status->merge( $this->pruneKeyLockFiles( $path ) );
142140 } else {
143141 // Unix: unlink() can be used on files currently open by this
144142 // process and we must do so in order to avoid race conditions
145 - $status->merge( $this->pruneKeyLockFiles( $key ) );
146 - $status->merge( $this->closeLockHandles( $key, $handlesToClose ) );
 143+ $status->merge( $this->pruneKeyLockFiles( $path ) );
 144+ $status->merge( $this->closeLockHandles( $path, $handlesToClose ) );
147145 }
148146 }
149147
150148 return $status;
151149 }
152150
153 - private function closeLockHandles( $key, array $handlesToClose ) {
 151+ private function closeLockHandles( $path, array $handlesToClose ) {
154152 $status = Status::newGood();
155153 foreach ( $handlesToClose as $handle ) {
156154 wfSuppressWarnings();
157155 if ( !flock( $handle, LOCK_UN ) ) {
158 - $status->fatal( 'lockmanager-fail-releaselock', $key );
 156+ $status->fatal( 'lockmanager-fail-releaselock', $path );
159157 }
160158 if ( !fclose( $handle ) ) {
161 - $status->warning( 'lockmanager-fail-closelock', $key );
 159+ $status->warning( 'lockmanager-fail-closelock', $path );
162160 }
163161 wfRestoreWarnings();
164162 }
165163 return $status;
166164 }
167165
168 - private function pruneKeyLockFiles( $key ) {
 166+ private function pruneKeyLockFiles( $path ) {
169167 $status = Status::newGood();
170 - if ( !count( $this->locksHeld[$key] ) ) {
 168+ if ( !count( $this->locksHeld[$path] ) ) {
171169 wfSuppressWarnings();
172170 # No locks are held for the lock file anymore
173 - if ( !unlink( $this->getLockPath( $key ) ) ) {
174 - $status->warning( 'lockmanager-fail-deletelock', $key );
 171+ if ( !unlink( $this->getLockPath( $path ) ) ) {
 172+ $status->warning( 'lockmanager-fail-deletelock', $path );
175173 }
176174 wfRestoreWarnings();
177 - unset( $this->locksHeld[$key] );
178 - unset( $this->handles[$key] );
 175+ unset( $this->locksHeld[$path] );
 176+ unset( $this->handles[$path] );
179177 }
180178 return $status;
181179 }
182180
183181 /**
184182 * Get the path to the lock file for a key
185 - * @param $key string
 183+ * @param $path string
186184 * @return string
187185 */
188 - protected function getLockPath( $key ) {
189 - return "{$this->lockDir}/{$key}.lock";
 186+ protected function getLockPath( $path ) {
 187+ $hash = self::sha1Base36( $path );
 188+ return "{$this->lockDir}/{$hash}.lock";
190189 }
191190
192191 function __destruct() {
193192 // Make sure remaining locks get cleared for sanity
194 - foreach ( $this->locksHeld as $key => $locks ) {
195 - $this->doSingleUnlock( $key, 0 );
 193+ foreach ( $this->locksHeld as $path => $locks ) {
 194+ $this->doSingleUnlock( $path, self::LOCK_EX );
 195+ $this->doSingleUnlock( $path, self::LOCK_SH );
196196 }
197197 }
198198 }
Index: trunk/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php
@@ -25,8 +25,6 @@
2626 /** @var Array Map of bucket indexes to peer server lists */
2727 protected $srvsByBucket; // (bucket index => (lsrv1, lsrv2, ...))
2828
29 - /** @var Array Map of (locked key => lock type => count) */
30 - protected $locksHeld = array();
3129 /** @var Array Map Server connections (server name => resource) */
3230 protected $conns = array();
3331
@@ -66,66 +64,66 @@
6765 $this->session = wfBaseConvert( sha1( $this->session ), 16, 36, 31 );
6866 }
6967
70 - protected function doLock( array $keys, $type ) {
 68+ protected function doLock( array $paths, $type ) {
7169 $status = Status::newGood();
7270
73 - $keysToLock = array();
 71+ $pathsToLock = array();
7472 // Get locks that need to be acquired (buckets => locks)...
75 - foreach ( $keys as $key ) {
76 - if ( isset( $this->locksHeld[$key][$type] ) ) {
77 - ++$this->locksHeld[$key][$type];
78 - } elseif ( isset( $this->locksHeld[$key][self::LOCK_EX] ) ) {
79 - $this->locksHeld[$key][$type] = 1;
 73+ foreach ( $paths as $path ) {
 74+ if ( isset( $this->locksHeld[$path][$type] ) ) {
 75+ ++$this->locksHeld[$path][$type];
 76+ } elseif ( isset( $this->locksHeld[$path][self::LOCK_EX] ) ) {
 77+ $this->locksHeld[$path][$type] = 1;
8078 } else {
81 - $bucket = $this->getBucketFromKey( $key );
82 - $keysToLock[$bucket][] = $key;
 79+ $bucket = $this->getBucketFromKey( $path );
 80+ $pathsToLock[$bucket][] = $path;
8381 }
8482 }
8583
86 - $lockedKeys = array(); // files locked in this attempt
 84+ $lockedPaths = array(); // files locked in this attempt
8785 // Attempt to acquire these locks...
88 - foreach ( $keysToLock as $bucket => $keys ) {
 86+ foreach ( $pathsToLock as $bucket => $paths ) {
8987 // Try to acquire the locks for this bucket
90 - $res = $this->doLockingRequestAll( $bucket, $keys, $type );
 88+ $res = $this->doLockingRequestAll( $bucket, $paths, $type );
9189 if ( $res === 'cantacquire' ) {
9290 // Resources already locked by another process.
9391 // Abort and unlock everything we just locked.
94 - $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $keys ) );
95 - $status->merge( $this->doUnlock( $lockedKeys, $type ) );
 92+ $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $paths ) );
 93+ $status->merge( $this->doUnlock( $lockedPaths, $type ) );
9694 return $status;
9795 } elseif ( $res !== true ) {
9896 // Couldn't contact any servers for this bucket.
9997 // Abort and unlock everything we just locked.
100 - $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $keys ) );
101 - $status->merge( $this->doUnlock( $lockedKeys, $type ) );
 98+ $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $paths ) );
 99+ $status->merge( $this->doUnlock( $lockedPaths, $type ) );
102100 return $status;
103101 }
104102 // Record these locks as active
105 - foreach ( $keys as $key ) {
106 - $this->locksHeld[$key][$type] = 1; // locked
 103+ foreach ( $paths as $path ) {
 104+ $this->locksHeld[$path][$type] = 1; // locked
107105 }
108106 // Keep track of what locks were made in this attempt
109 - $lockedKeys = array_merge( $lockedKeys, $keys );
 107+ $lockedPaths = array_merge( $lockedPaths, $paths );
110108 }
111109
112110 return $status;
113111 }
114112
115 - protected function doUnlock( array $keys, $type ) {
 113+ protected function doUnlock( array $paths, $type ) {
116114 $status = Status::newGood();
117115
118 - foreach ( $keys as $key ) {
119 - if ( !isset( $this->locksHeld[$key] ) ) {
120 - $status->warning( 'lockmanager-notlocked', $key );
121 - } elseif ( !isset( $this->locksHeld[$key][$type] ) ) {
122 - $status->warning( 'lockmanager-notlocked', $key );
 116+ foreach ( $paths as $path ) {
 117+ if ( !isset( $this->locksHeld[$path] ) ) {
 118+ $status->warning( 'lockmanager-notlocked', $path );
 119+ } elseif ( !isset( $this->locksHeld[$path][$type] ) ) {
 120+ $status->warning( 'lockmanager-notlocked', $path );
123121 } else {
124 - --$this->locksHeld[$key][$type];
125 - if ( $this->locksHeld[$key][$type] <= 0 ) {
126 - unset( $this->locksHeld[$key][$type] );
 122+ --$this->locksHeld[$path][$type];
 123+ if ( $this->locksHeld[$path][$type] <= 0 ) {
 124+ unset( $this->locksHeld[$path][$type] );
127125 }
128 - if ( !count( $this->locksHeld[$key] ) ) {
129 - unset( $this->locksHeld[$key] ); // no SH or EX locks left for key
 126+ if ( !count( $this->locksHeld[$path] ) ) {
 127+ unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
130128 }
131129 }
132130 }
@@ -139,14 +137,14 @@
140138 }
141139
142140 /**
143 - * Get a connection to a lock server and acquire locks on $keys
 141+ * Get a connection to a lock server and acquire locks on $paths
144142 *
145143 * @param $lockSrv string
146 - * @param $keys Array
 144+ * @param $paths Array
147145 * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
148146 * @return bool Resources able to be locked
149147 */
150 - protected function doLockingRequest( $lockSrv, array $keys, $type ) {
 148+ protected function doLockingRequest( $lockSrv, array $paths, $type ) {
151149 if ( $type == self::LOCK_SH ) { // reader locks
152150 $type = 'SH';
153151 } elseif ( $type == self::LOCK_EX ) { // writer locks
@@ -156,6 +154,7 @@
157155 }
158156
159157 // Send out the command and get the response...
 158+ $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
160159 $response = $this->sendCommand( $lockSrv, 'ACQUIRE', $type, $keys );
161160
162161 return ( $response === 'ACQUIRED' );
@@ -195,25 +194,25 @@
196195 * Attempt to acquire locks with the peers for a bucket
197196 *
198197 * @param $bucket integer
199 - * @param $keys Array List of resource keys to lock
 198+ * @param $paths Array List of resource keys to lock
200199 * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH
201200 * @return bool|string One of (true, 'cantacquire', 'srverrors')
202201 */
203 - protected function doLockingRequestAll( $bucket, array $keys, $type ) {
 202+ protected function doLockingRequestAll( $bucket, array $paths, $type ) {
204203 $yesVotes = 0; // locks made on trustable servers
205204 $votesLeft = count( $this->srvsByBucket[$bucket] ); // remaining peers
206205 $quorum = floor( $votesLeft/2 + 1 ); // simple majority
207206 // Get votes for each peer, in order, until we have enough...
208207 foreach ( $this->srvsByBucket[$bucket] as $index => $lockSrv ) {
209208 // Attempt to acquire the lock on this peer
210 - if ( !$this->doLockingRequest( $lockSrv, $keys, $type ) ) {
 209+ if ( !$this->doLockingRequest( $lockSrv, $paths, $type ) ) {
211210 return 'cantacquire'; // vetoed; resource locked
212211 }
213212 ++$yesVotes; // success for this peer
214213 if ( $yesVotes >= $quorum ) {
215214 return true; // lock obtained
216215 }
217 - $votesLeft--;
 216+ --$votesLeft;
218217 $votesNeeded = $quorum - $yesVotes;
219218 if ( $votesNeeded > $votesLeft ) {
220219 // In "trust cache" mode we don't have to meet the quorum
@@ -265,14 +264,15 @@
266265 }
267266
268267 /**
269 - * Get the bucket for lock key
 268+ * Get the bucket for resource path.
 269+ * This should avoid throwing any exceptions.
270270 *
271 - * @param $key string (31 char hex key)
 271+ * @param $path string
272272 * @return integer
273273 */
274 - protected function getBucketFromKey( $key ) {
275 - $prefix = substr( $key, 0, 2 ); // first 2 hex chars (8 bits)
276 - return intval( base_convert( $prefix, 16, 10 ) ) % count( $this->srvsByBucket );
 274+ protected function getBucketFromKey( $path ) {
 275+ $prefix = substr( sha1( $path ), 0, 2 ); // first 2 hex chars (8 bits)
 276+ return intval( base_convert( $prefix, 16, 10 ) ) % count( $this->dbsByBucket );
277277 }
278278
279279 /**
Index: trunk/phase3/includes/filerepo/backend/lockmanager/LockManager.php
@@ -20,11 +20,6 @@
2121 * @since 1.19
2222 */
2323 abstract class LockManager {
24 - /* Lock types; stronger locks have higher values */
25 - const LOCK_SH = 1; // shared lock (for reads)
26 - const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
27 - const LOCK_EX = 3; // exclusive lock (for writes)
28 -
2924 /** @var Array Mapping of lock types to the type actually used */
3025 protected $lockTypeMap = array(
3126 self::LOCK_SH => self::LOCK_SH,
@@ -32,6 +27,14 @@
3328 self::LOCK_EX => self::LOCK_EX
3429 );
3530
 31+ /** @var Array Map of (resource path => lock type => count) */
 32+ protected $locksHeld = array();
 33+
 34+ /* Lock types; stronger locks have higher values */
 35+ const LOCK_SH = 1; // shared lock (for reads)
 36+ const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
 37+ const LOCK_EX = 3; // exclusive lock (for writes)
 38+
3639 /**
3740 * Construct a new instance from configuration
3841 *
@@ -47,8 +50,7 @@
4851 * @return Status
4952 */
5053 final public function lock( array $paths, $type = self::LOCK_EX ) {
51 - $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
52 - return $this->doLock( $keys, $this->lockTypeMap[$type] );
 54+ return $this->doLock( array_unique( $paths ), $this->lockTypeMap[$type] );
5355 }
5456
5557 /**
@@ -59,8 +61,7 @@
6062 * @return Status
6163 */
6264 final public function unlock( array $paths, $type = self::LOCK_EX ) {
63 - $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
64 - return $this->doUnlock( $keys, $this->lockTypeMap[$type] );
 65+ return $this->doUnlock( array_unique( $paths ), $this->lockTypeMap[$type] );
6566 }
6667
6768 /**
@@ -76,20 +77,20 @@
7778 /**
7879 * Lock resources with the given keys and lock type
7980 *
80 - * @param $key Array List of keys to lock (40 char hex hashes)
 81+ * @param $paths Array List of storage paths
8182 * @param $type integer LockManager::LOCK_* constant
8283 * @return string
8384 */
84 - abstract protected function doLock( array $keys, $type );
 85+ abstract protected function doLock( array $paths, $type );
8586
8687 /**
8788 * Unlock resources with the given keys and lock type
8889 *
89 - * @param $key Array List of keys to unlock (40 char hex hashes)
 90+ * @param $paths Array List of storage paths
9091 * @param $type integer LockManager::LOCK_* constant
9192 * @return string
9293 */
93 - abstract protected function doUnlock( array $keys, $type );
 94+ abstract protected function doUnlock( array $paths, $type );
9495 }
9596
9697 /**
@@ -162,11 +163,11 @@
163164 * Simple version of LockManager that does nothing
164165 */
165166 class NullLockManager extends LockManager {
166 - protected function doLock( array $keys, $type ) {
 167+ protected function doLock( array $paths, $type ) {
167168 return Status::newGood();
168169 }
169170
170 - protected function doUnlock( array $keys, $type ) {
 171+ protected function doUnlock( array $paths, $type ) {
171172 return Status::newGood();
172173 }
173174 }

Comments

#Comment by Aaron Schulz (talk | contribs)   21:42, 11 January 2012

getBucketFromKey() is now a misnomer. Should be renamed for clarity.

Status & tagging log