r105465 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r105464‎ | r105465 | r105466 >
Date:22:07, 7 December 2011
Author:aaron
Status:deferred
Tags:
Comment:
* Improved FileBackendMultiWrite::streamFile() in handling failures.
* Added __destruct() to DBFileLockManager as the FS one already has.
* Fixed __destruct() in FSFileLockManager (same problem as r105409 elsewhere).
* Added FileBackendBase::getScopedLock() function and made doOperations() use it.
* Adde comments to lockFiles()/unlockFiles() to encourage use of getScopedLock().
* Added default getHashType() implementation in FileBackend.
* Various comment cleanups and fixes.
Modified paths:
  • /branches/FileBackend/phase3/includes/filerepo/backend/FileBackend.php (modified) (history)
  • /branches/FileBackend/phase3/includes/filerepo/backend/FileBackendMultiWrite.php (modified) (history)
  • /branches/FileBackend/phase3/includes/filerepo/backend/LockManager.php (modified) (history)

Diff [purge]

Index: branches/FileBackend/phase3/includes/filerepo/backend/FileBackendMultiWrite.php
@@ -83,8 +83,8 @@
8484 }
8585 }
8686
87 - // Try to lock those files...
88 - $status->merge( $this->lockFiles( $filesToLock ) );
 87+ // Try to lock those files for the scope of this function...
 88+ $scopedLock = $this->getScopedLock( $filesToLock, $status );
8989 if ( !$status->isOK() ) {
9090 return $status; // abort
9191 }
@@ -105,7 +105,6 @@
106106 ++$status->failCount;
107107 $status->success[$index] = false;
108108 } else {
109 - $status->merge( $this->unlockFiles( $filesToLock ) );
110109 return $status;
111110 }
112111 }
@@ -132,7 +131,6 @@
133132 }
134133 $pos--;
135134 }
136 - $status->merge( $this->unlockFiles( $filesToLock ) );
137135 return $status;
138136 }
139137 }
@@ -154,12 +152,9 @@
155153 $status->merge( $subStatus );
156154 }
157155
158 - // Unlock all the files
159 - $status->merge( $this->unlockFiles( $filesToLock ) );
 156+ // Make sure status is OK, despite any finish() fatals
 157+ $status->setResult( true, $status->value );
160158
161 - // Make sure status is OK, despite any finish() or unlockFiles() fatals
162 - $status->setResult( true );
163 -
164159 return $status;
165160 }
166161
@@ -232,8 +227,12 @@
233228 // Pass isOK() despite fatals from other backends
234229 $status->setResult( true );
235230 return $status;
236 - } elseif ( headers_sent() ) {
237 - return $status; // died mid-stream...so this is already fubar
 231+ } else { // failure
 232+ if ( headers_sent() ) {
 233+ return $status; // died mid-stream...so this is already fubar
 234+ } elseif ( strval( ob_get_contents() ) !== '' ) {
 235+ ob_clean(); // output was buffered but not sent; clear it
 236+ }
238237 }
239238 }
240239 return $status;
Index: branches/FileBackend/phase3/includes/filerepo/backend/LockManager.php
@@ -251,13 +251,9 @@
252252 }
253253
254254 function __destruct() {
255 - // Make sure remaining files get cleared for sanity
256 - foreach ( $this->handles as $key => $locks ) {
257 - foreach ( $locks as $type => $handle ) {
258 - flock( $handle, LOCK_UN ); // PHP 5.3 will not do this automatically
259 - fclose( $handle );
260 - }
261 - unlink( $this->getLockPath( $key ) );
 255+ // Make sure remaining locks get cleared for sanity
 256+ foreach ( $this->locksHeld as $key => $locks ) {
 257+ $this->doSingleUnlock( $key, 0 );
262258 }
263259 }
264260 }
@@ -692,6 +688,11 @@
693689 $prefix = substr( $key, 0, 2 ); // first 2 hex chars (8 bits)
694690 return intval( base_convert( $prefix, 16, 10 ) ) % count( $this->dbsByBucket );
695691 }
 692+
 693+ function __destruct() {
 694+ // Make sure remaining locks get cleared for sanity
 695+ $this->finishLockTransactions();
 696+ }
696697 }
697698
698699 /**
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileBackend.php
@@ -27,7 +27,8 @@
2828
2929 /**
3030 * Build a new object from configuration.
31 - * This should only be called from within FileRepo classes.
 31+ * This should only be called from within FileBackendGroup.
 32+ *
3233 * $config includes:
3334 * 'name' : The name of this backend
3435 * 'wikiId' : Prefix to container names that is unique to this wiki
@@ -44,9 +45,10 @@
4546 }
4647
4748 /**
 49+ * Get the unique backend name.
 50+ *
4851 * We may have multiple different backends of the same type.
4952 * For example, we can have two Swift backends using different proxies.
50 - * All backend instances must have unique names.
5153 *
5254 * @return string
5355 */
@@ -93,7 +95,7 @@
9496 /**
9597 * Prepare a storage path for usage. This will create containers
9698 * that don't yet exists or, on FS backends, create parent directories.
97 - * Do not call this function from places outside FileBackend and FileOp.
 99+ *
98100 * $params include:
99101 * dir : storage directory
100102 *
@@ -104,8 +106,10 @@
105107
106108 /**
107109 * Take measures to block web access to a directory.
 110+ * In backends like Swift, this might restrict container
 111+ * access to backend user that represents user web request.
108112 * This is not guaranteed to actually do anything.
109 - * Do not call this function from places outside FileBackend and FileOp.
 113+ *
110114 * $params include:
111115 * dir : storage directory
112116 * noAccess : try to deny file access
@@ -119,7 +123,7 @@
120124 /**
121125 * Clean up an empty storage directory.
122126 * On FS backends, the directory will be deleted. Others may do nothing.
123 - * Do not call this function from places outside FileBackend and FileOp.
 127+ *
124128 * $params include:
125129 * dir : storage directory
126130 *
@@ -129,8 +133,8 @@
130134 abstract public function clean( array $params );
131135
132136 /**
133 - * Check if a file exits at a storage path in the backend.
134 - * Do not call this function from places outside FileBackend and FileOp.
 137+ * Check if a file exists at a storage path in the backend.
 138+ *
135139 * $params include:
136140 * src : source storage path
137141 *
@@ -142,6 +146,7 @@
143147 /**
144148 * Get a hash of the file at a storage path in the backend.
145149 * Typically this will be a SHA-1 hash, MD5 hash, or something similar.
 150+ *
146151 * $params include:
147152 * src : source storage path
148153 *
@@ -153,12 +158,13 @@
154159 /**
155160 * Get the format of the hash that getFileHash() uses
156161 *
157 - * @return string (md5, sha1, unknown, ...)
 162+ * @return string (md5, sha1, internal, ...)
158163 */
159164 abstract public function getHashType();
160165
161166 /**
162167 * Get the last-modified timestamp of the file at a storage path.
 168+ *
163169 * $params include:
164170 * src : source storage path
165171 *
@@ -170,6 +176,7 @@
171177 /**
172178 * Get the properties of the file at a storage path in the backend.
173179 * Returns FSFile::placeholderProps() on failure.
 180+ *
174181 * $params include:
175182 * src : source storage path
176183 *
@@ -183,6 +190,7 @@
184191 * Appropriate HTTP headers (Status, Content-Type, Content-Length)
185192 * must be sent if streaming began, while none should be sent otherwise.
186193 * Implementations should flush the output buffer before sending data.
 194+ *
187195 * $params include:
188196 * src : source storage path
189197 * headers : additional HTTP headers to send on success
@@ -198,6 +206,7 @@
199207 * the container should be listed. If of the form "mwstore://container/dir",
200208 * then all items under that container directory should be listed.
201209 * Results should be storage paths relative to the given directory.
 210+ *
202211 * $params include:
203212 * dir : storage path directory.
204213 *
@@ -229,6 +238,7 @@
230239 /**
231240 * Get a local copy on disk of the file at a storage path in the backend.
232241 * The temporary copy will have the same file extension as the source.
 242+ *
233243 * $params include:
234244 * src : source storage path
235245 *
@@ -239,9 +249,10 @@
240250
241251 /**
242252 * Lock the files at the given storage paths in the backend.
243 - * This should either lock all the files or none (on failure).
244 - * Do not call this function from places outside FileBackend and FileOp.
 253+ * This will either lock all the files or none (on failure).
245254 *
 255+ * Avoid using this function outside of FileBackendScopedLock.
 256+ *
246257 * @param $sources Array Source storage paths
247258 * @return Status
248259 */
@@ -251,14 +262,31 @@
252263
253264 /**
254265 * Unlock the files at the given storage paths in the backend.
255 - * Do not call this function from places outside FileBackend and FileOp.
256266 *
 267+ * Avoid using this function outside of FileBackendScopedLock.
 268+ *
257269 * @param $sources Array Source storage paths
258270 * @return Status
259271 */
260272 final public function unlockFiles( array $paths ) {
261273 return $this->lockManager->unlock( $paths );
262274 }
 275+
 276+ /**
 277+ * Lock the files at the given storage paths in the backend.
 278+ * This will either lock all the files or none (on failure).
 279+ * On failure, the status object will be updated with errors.
 280+ *
 281+ * Once the return value goes out scope, the locks will be released and
 282+ * the status updated. Unlock fatals will not change the status "OK" value.
 283+ *
 284+ * @param $sources Array Source storage paths
 285+ * @param $status Status Status to update on lock/unlock
 286+ * @return FileBackendScopedLock|null Returns null on failure
 287+ */
 288+ final public function getScopedLock( array $paths, Status $status ) {
 289+ return FileBackendScopedLock::factory( $this, $paths, $status );
 290+ }
263291 }
264292
265293 /**
@@ -360,7 +388,7 @@
361389 }
362390
363391 /**
364 - * Whether this backend implements move() and is applies to a potential
 392+ * Whether this backend implements move() and it applies to a potential
365393 * move from one storage path to another. No backends hits are required.
366394 * For example, moving objects accross containers may not be supported.
367395 * Do not call this function from places outside FileBackend and FileOp.
@@ -384,6 +412,10 @@
385413 }
386414 }
387415
 416+ public function getHashType() {
 417+ return 'internal';
 418+ }
 419+
388420 function streamFile( array $params ) {
389421 $status = Status::newGood();
390422
@@ -466,8 +498,8 @@
467499 $filesToLock = array_merge( $filesToLock, $fileOp->storagePathsUsed() );
468500 }
469501
470 - // Try to lock those files...
471 - $status->merge( $this->lockFiles( $filesToLock ) );
 502+ // Try to lock those files for the scope of this function...
 503+ $scopedLock = $this->getScopedLock( $filesToLock, $status );
472504 if ( !$status->isOK() ) {
473505 return $status; // abort
474506 }
@@ -483,7 +515,6 @@
484516 ++$status->failCount;
485517 $status->success[$index] = false;
486518 } else {
487 - $status->merge( $this->unlockFiles( $filesToLock ) );
488519 return $status;
489520 }
490521 }
@@ -510,7 +541,6 @@
511542 }
512543 $pos--;
513544 }
514 - $status->merge( $this->unlockFiles( $filesToLock ) );
515545 return $status;
516546 }
517547 }
@@ -532,12 +562,9 @@
533563 $status->merge( $subStatus );
534564 }
535565
536 - // Unlock all the files
537 - $status->merge( $this->unlockFiles( $filesToLock ) );
 566+ // Make sure status is OK, despite any finish() fatals
 567+ $status->setResult( true, $status->value );
538568
539 - // Make sure status is OK, despite any finish() or unlockFiles() fatals
540 - $status->setResult( true );
541 -
542569 return $status;
543570 }
544571
@@ -648,3 +675,57 @@
649676 return $relStoragePath;
650677 }
651678 }
 679+
 680+/**
 681+ * Class to handle scoped locks, which release when the object is destroyed
 682+ */
 683+class FileBackendScopedLock {
 684+ /** @var FileBackendBase */
 685+ protected $backend;
 686+ /** @var Status */
 687+ protected $status;
 688+ /** @var Array List of storage paths*/
 689+ protected $paths;
 690+
 691+ /**
 692+ * @param $backend FileBackendBase
 693+ * @param $paths Array List of storage paths
 694+ * @param $status Status
 695+ */
 696+ protected function __construct( FileBackendBase $backend, array $paths, Status $status ) {
 697+ $this->backend = $backend;
 698+ $this->paths = $paths;
 699+ $this->status = $status;
 700+ }
 701+
 702+ protected function __clone() {}
 703+
 704+ /**
 705+ * Get a status object resulting from an attempt to lock files.
 706+ * If the attempt is sucessful, the value of the status will be
 707+ * FileBackendScopedLock object which releases the locks when
 708+ * it goes out of scope. Otherwise, the value will be null.
 709+ *
 710+ * @param $backend FileBackendBase
 711+ * @param $files Array List of storage paths
 712+ * @param $status Status
 713+ * @return FileBackendScopedLock|null
 714+ */
 715+ public static function factory( FileBackendBase $backend, array $files, Status $status ) {
 716+ $lockStatus = $backend->lockFiles( $files );
 717+ $status->merge( $lockStatus );
 718+ if ( $lockStatus->isOK() ) {
 719+ return new self( $backend, $files, $status );
 720+ }
 721+ return null;
 722+ }
 723+
 724+ function __destruct() {
 725+ $wasOk = $this->status->isOK();
 726+ $this->status->merge( $this->backend->unlockFiles( $this->paths ) );
 727+ if ( $wasOk ) {
 728+ // Make sure status is OK, despite any unlockFiles() fatals
 729+ $this->status->setResult( true, $this->status->value );
 730+ }
 731+ }
 732+}

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r105409* FileBackend/FileBackendBase:...aaron02:07, 7 December 2011

Status & tagging log