* Made use of FileBackend function 'latest' param in FileOp.
authorAaron Schulz <aaron@users.mediawiki.org>
Thu, 5 Jan 2012 06:18:36 +0000 (06:18 +0000)
committerAaron Schulz <aaron@users.mediawiki.org>
Thu, 5 Jan 2012 06:18:36 +0000 (06:18 +0000)
* Added FileBackend process cache for fileExists(), getFileTimestamp(), and getLocalReference().
* Refactored getFileSha1Base36() into parent class and subclass functions.
* Removed some FileBackendMultiWrite comment duplication.

includes/filerepo/backend/FSFileBackend.php
includes/filerepo/backend/FileBackend.php
includes/filerepo/backend/FileBackendMultiWrite.php
includes/filerepo/backend/FileOp.php

index 6eb73b3..5f2d04a 100644 (file)
@@ -337,9 +337,9 @@ class FSFileBackend extends FileBackend {
        }
 
        /**
-        * @see FileBackend::fileExists()
+        * @see FileBackend::doFileExists()
         */
-       public function fileExists( array $params ) {
+       protected function doFileExists( array $params ) {
                list( $c, $source ) = $this->resolveStoragePathReal( $params['src'] );
                if ( $source === null ) {
                        return false; // invalid storage path
@@ -351,9 +351,9 @@ class FSFileBackend extends FileBackend {
        }
 
        /**
-        * @see FileBackend::getFileTimestamp()
+        * @see FileBackend::doGetFileTimestamp()
         */
-       public function getFileTimestamp( array $params ) {
+       public function doGetFileTimestamp( array $params ) {
                list( $c, $source ) = $this->resolveStoragePathReal( $params['src'] );
                if ( $source === null ) {
                        return false; // invalid storage path
index 5f4cacc..902f4cc 100644 (file)
@@ -37,9 +37,9 @@ abstract class FileBackendBase {
         * This should only be called from within FileBackendGroup.
         * 
         * $config includes:
-        *     'name'        : The unique name of this backend
-        *     'wikiId'      : Prefix to container names that is unique to this wiki
-        *     'lockManager' : Registered name of a file lock manager to use
+        *     'name'        : The unique name of this backend.
+        *     'wikiId'      : Prefix to container names that is unique to this wiki.
+        *     'lockManager' : Registered name of a file lock manager to use.
         *     'readOnly'    : Write operations are disallowed if this is a non-empty string.
         *                     It should be an explanation for the backend being read-only.
         * 
@@ -782,6 +782,48 @@ abstract class FileBackend extends FileBackendBase {
                return Status::newGood();
        }
 
+       /**
+        * @see FileBackendBase::fileExists()
+        */
+       final public function fileExists( array $params ) {
+               $path = $params['src'];
+               if ( isset( $this->cache[$path]['exists'] ) ) {
+                       return $this->cache[$path]['exists'];
+               }
+               $exists = $this->doFileExists( $params );
+               if ( $exists ) { // don't cache negatives
+                       $this->trimCache(); // limit memory
+                       $this->cache[$path]['exists'] = $exists;
+               }
+               return $exists;
+       }
+
+       /**
+        * @see FileBackend::fileExists()
+        */
+       abstract protected function doFileExists( array $params );
+
+       /**
+        * @see FileBackendBase::getFileTimestamp()
+        */
+       final public function getFileTimestamp( array $params ) {
+               $path = $params['src'];
+               if ( isset( $this->cache[$path]['timestamp'] ) ) {
+                       return $this->cache[$path]['timestamp'];
+               }
+               $timestamp = $this->doGetFileTimestamp( $params );
+               if ( $timestamp ) { // don't cache negatives
+                       $this->trimCache(); // limit memory
+                       $this->cache[$path]['timestamp'] = $timestamp;
+               }
+               return $timestamp;
+       }
+
+       /**
+        * @see FileBackend::getFileTimestamp()
+        */
+       abstract protected function doGetFileTimestamp( array $params );
+
        /**
         * @see FileBackendBase::getFileContents()
         */
@@ -804,16 +846,23 @@ abstract class FileBackend extends FileBackendBase {
                if ( isset( $this->cache[$path]['sha1'] ) ) {
                        return $this->cache[$path]['sha1'];
                }
+               $hash = $this->doGetFileSha1Base36( $params );
+               if ( $hash ) { // don't cache negatives
+                       $this->trimCache(); // limit memory
+                       $this->cache[$path]['sha1'] = $hash;
+               }
+               return $hash;
+       }
+
+       /**
+        * @see FileBackend::getFileSha1Base36()
+        */
+       protected function doGetFileSha1Base36( array $params ) {
                $fsFile = $this->getLocalReference( $params );
                if ( !$fsFile ) {
                        return false;
                } else {
-                       $sha1 = $fsFile->getSha1Base36();
-                       if ( $sha1 !== false ) { // don't cache negatives
-                               $this->trimCache(); // limit memory
-                               $this->cache[$path]['sha1'] = $sha1;
-                       }
-                       return $sha1;
+                       return $fsFile->getSha1Base36();
                }
        }
 
@@ -833,7 +882,16 @@ abstract class FileBackend extends FileBackendBase {
         * @see FileBackendBase::getLocalReference()
         */
        public function getLocalReference( array $params ) {
-               return $this->getLocalCopy( $params );
+               $path = $params['src'];
+               if ( isset( $this->cache[$path]['localRef'] ) ) {
+                       return $this->cache[$path]['localRef'];
+               }
+               $tmpFile = $this->getLocalCopy( $params );
+               if ( $tmpFile ) { // don't cache negatives
+                       $this->trimCache(); // limit memory
+                       $this->cache[$path]['localRef'] = $tmpFile;
+               }
+               return $tmpFile;
        }
 
        /**
index 241858e..c9fafa5 100644 (file)
@@ -32,9 +32,7 @@ class FileBackendMultiWrite extends FileBackendBase {
 
        /**
         * Construct a proxy backend that consists of several internal backends.
-        * $config contains:
-        *     'name'        : The name of the proxy backend
-        *     'lockManager' : Registered name of the file lock manager to use
+        * Additional $config params include:
         *     'backends'    : Array of backend config and multi-backend settings.
         *                     Each value is the config used in the constructor of a
         *                     FileBackend class, but with these additional settings:
index 38e61ac..c4bd7c9 100644 (file)
@@ -26,6 +26,7 @@ abstract class FileOp {
        protected $state = self::STATE_NEW; // integer
        protected $failed = false; // boolean
        protected $useBackups = true; // boolean
+       protected $useLatest = true; // boolean
        protected $destSameAsSource = false; // boolean
        protected $destAlreadyExists = false; // boolean
 
@@ -60,6 +61,17 @@ abstract class FileOp {
                $this->useBackups = false;
        }
 
+       /**
+        * Allow stale data for file reads and existence checks.
+        * If this is called, then disableBackups() should also be called
+        * unless the affected files are known to have not changed recently.
+        *
+        * @return void
+        */
+       final protected function allowStaleReads() {
+               $this->useLatest = false;
+       }
+
        /**
         * Attempt a series of file operations.
         * Callers are responsible for handling file locking.
@@ -71,10 +83,14 @@ abstract class FileOp {
        final public static function attemptBatch( array $performOps, array $opts ) {
                $status = Status::newGood();
 
+               $allowStale = isset( $opts['allowStale'] ) && $opts['allowStale'];
                $ignoreErrors = isset( $opts['ignoreErrors'] ) && $opts['ignoreErrors'];
                $predicates = FileOp::newPredicates(); // account for previous op in prechecks
                // Do pre-checks for each operation; abort on failure...
                foreach ( $performOps as $index => $fileOp ) {
+                       if ( $allowStale ) {
+                               $fileOp->allowStaleReads(); // allow potentially stale reads
+                       }
                        $status->merge( $fileOp->precheck( $predicates ) );
                        if ( !$status->isOK() ) { // operation failed?
                                if ( $ignoreErrors ) {
@@ -320,7 +336,7 @@ abstract class FileOp {
                $status = Status::newGood();
                if ( $this->useBackups ) {
                        // Check if a file already exists at the source...
-                       $params = array( 'src' => $this->params['src'] );
+                       $params = array( 'src' => $this->params['src'], 'latest' => $this->useLatest );
                        if ( $this->backend->fileExists( $params ) ) {
                                // Create a temporary backup copy...
                                $this->tmpSourcePath = $this->backend->getLocalCopy( $params );
@@ -350,7 +366,7 @@ abstract class FileOp {
                if ( $this->getParam( 'overwriteDest' ) ) {
                        if ( $this->useBackups ) {
                                // Create a temporary backup copy...
-                               $params = array( 'src' => $this->params['dst'] );
+                               $params = array( 'src' => $this->params['dst'], 'latest' => $this->useLatest );
                                $this->tmpDestFile = $this->backend->getLocalCopy( $params );
                                if ( !$this->tmpDestFile ) {
                                        $status->fatal( 'backend-fail-backup', $this->params['dst'] );
@@ -402,7 +418,8 @@ abstract class FileOp {
                if ( FileBackend::isStoragePath( $path ) ) {
                        // For some backends (e.g. Swift, Azure) we can get
                        // standard hashes to use for this types of comparisons.
-                       $hash = $this->backend->getFileSha1Base36( array( 'src' => $path ) );
+                       $params = array( 'src' => $path, 'latest' => $this->useLatest );
+                       $hash = $this->backend->getFileSha1Base36( $params );
                // Source file is on file system
                } else {
                        wfSuppressWarnings();
@@ -470,7 +487,8 @@ abstract class FileOp {
                if ( isset( $predicates['exists'][$source] ) ) {
                        return $predicates['exists'][$source]; // previous op assures this
                } else {
-                       return $this->backend->fileExists( array( 'src' => $source ) );
+                       $params = array( 'src' => $source, 'latest' => $this->useLatest );
+                       return $this->backend->fileExists( $params );
                }
        }