Reduced max TempFSFile::factory() attempts since it uses 12 hex chars.
[lhc/web/wiklou.git] / includes / filebackend / FileBackendMultiWrite.php
index f4a7eac..9efa0db 100644 (file)
@@ -43,7 +43,10 @@ class FileBackendMultiWrite extends FileBackend {
        /** @var Array Prioritized list of FileBackendStore objects */
        protected $backends = array(); // array of (backend index => backends)
        protected $masterIndex = -1; // integer; index of master backend
-       protected $syncChecks = 0; // integer bitfield
+       protected $syncChecks = 0; // integer; bitfield
+       /** @var Array */
+       protected $noPushDirConts = array();
+       protected $noPushQuickOps = false; // boolean
 
        /* Possible internal backend consistency checks */
        const CHECK_SIZE = 1;
@@ -55,18 +58,21 @@ class FileBackendMultiWrite extends FileBackend {
         * Locking, journaling, and read-only checks are handled by the proxy backend.
         *
         * Additional $config params include:
-        *   - backends   : Array of backend config and multi-backend settings.
-        *                  Each value is the config used in the constructor of a
-        *                  FileBackendStore class, but with these additional settings:
-        *                    - class         : The name of the backend class
-        *                    - isMultiMaster : This must be set for one backend.
-        *                    - template:     : If given a backend name, this will use
-        *                                      the config of that backend as a template.
-        *                                      Values specified here take precedence.
-        *   - syncChecks : Integer bitfield of internal backend sync checks to perform.
-        *                  Possible bits include the FileBackendMultiWrite::CHECK_* constants.
-        *                  There are constants for SIZE, TIME, and SHA1.
-        *                  The checks are done before allowing any file operations.
+        *   - backends       : Array of backend config and multi-backend settings.
+        *                      Each value is the config used in the constructor of a
+        *                          FileBackendStore class, but with these additional settings:
+        *                        - class         : The name of the backend class
+        *                        - isMultiMaster : This must be set for one backend.
+        *                        - template:     : If given a backend name, this will use
+        *                                          the config of that backend as a template.
+        *                                          Values specified here take precedence.
+        *   - syncChecks     : Integer bitfield of internal backend sync checks to perform.
+        *                      Possible bits include the FileBackendMultiWrite::CHECK_* constants.
+        *                      There are constants for SIZE, TIME, and SHA1.
+        *                      The checks are done before allowing any file operations.
+        *   - noPushQuickOps : (hack) Only apply doQuickOperations() to the master backend.
+        *   - noPushDirConts : (hack) Only apply directory functions to the master backend.
+        *
         * @param $config Array
         * @throws MWException
         */
@@ -75,6 +81,12 @@ class FileBackendMultiWrite extends FileBackend {
                $this->syncChecks = isset( $config['syncChecks'] )
                        ? $config['syncChecks']
                        : self::CHECK_SIZE;
+               $this->noPushQuickOps = isset( $config['noPushQuickOps'] )
+                       ? $config['noPushQuickOps']
+                       : false;
+               $this->noPushDirConts = isset( $config['noPushDirConts'] )
+                       ? $config['noPushDirConts']
+                       : array();
                // Construct backends here rather than via registration
                // to keep these backends hidden from outside the proxy.
                $namesUsed = array();
@@ -139,6 +151,7 @@ class FileBackendMultiWrite extends FileBackend {
                }
                // Clear any cache entries (after locks acquired)
                $this->clearCache();
+               $opts['preserveCache'] = true; // only locked files are cached
                // Do a consistency check to see if the backends agree
                $status->merge( $this->consistencyCheck( $this->fileStoragePathsForOps( $ops ) ) );
                if ( !$status->isOK() ) {
@@ -147,11 +160,14 @@ class FileBackendMultiWrite extends FileBackend {
                // Actually attempt the operation batch on the master backend...
                $masterStatus = $mbe->doOperations( $realOps, $opts );
                $status->merge( $masterStatus );
-               // Propagate the operations to the clone backends...
-               foreach ( $this->backends as $index => $backend ) {
-                       if ( $index !== $this->masterIndex ) { // not done already
-                               $realOps = $this->substOpBatchPaths( $ops, $backend );
-                               $status->merge( $backend->doOperations( $realOps, $opts ) );
+               // Propagate the operations to the clone backends if there were no fatal errors.
+               // If $ops only had one operation, this might avoid backend inconsistencies.
+               if ( !count( $masterStatus->getErrorsArray() ) ) {
+                       foreach ( $this->backends as $index => $backend ) {
+                               if ( $index !== $this->masterIndex ) { // not done already
+                                       $realOps = $this->substOpBatchPaths( $ops, $backend );
+                                       $status->merge( $backend->doOperations( $realOps, $opts ) );
+                               }
                        }
                }
                // Make 'success', 'successCount', and 'failCount' fields reflect
@@ -368,10 +384,12 @@ class FileBackendMultiWrite extends FileBackend {
                $masterStatus = $this->backends[$this->masterIndex]->doQuickOperations( $realOps );
                $status->merge( $masterStatus );
                // Propagate the operations to the clone backends...
-               foreach ( $this->backends as $index => $backend ) {
-                       if ( $index !== $this->masterIndex ) { // not done already
-                               $realOps = $this->substOpBatchPaths( $ops, $backend );
-                               $status->merge( $backend->doQuickOperations( $realOps ) );
+               if ( !$this->noPushQuickOps ) {
+                       foreach ( $this->backends as $index => $backend ) {
+                               if ( $index !== $this->masterIndex ) { // not done already
+                                       $realOps = $this->substOpBatchPaths( $ops, $backend );
+                                       $status->merge( $backend->doQuickOperations( $realOps ) );
+                               }
                        }
                }
                // Make 'success', 'successCount', and 'failCount' fields reflect
@@ -383,15 +401,27 @@ class FileBackendMultiWrite extends FileBackend {
                return $status;
        }
 
+       /**
+        * @param $path string Storage path
+        * @return bool Path container should have dir changes pushed to all backends
+        */
+       protected function replicateContainerDirChanges( $path ) {
+               list( $b, $shortCont, $r ) = self::splitStoragePath( $path );
+               return !in_array( $shortCont, $this->noPushDirConts );
+       }
+
        /**
         * @see FileBackend::doPrepare()
         * @return Status
         */
        protected function doPrepare( array $params ) {
                $status = Status::newGood();
-               foreach ( $this->backends as $backend ) {
-                       $realParams = $this->substOpPaths( $params, $backend );
-                       $status->merge( $backend->doPrepare( $realParams ) );
+               $replicate = $this->replicateContainerDirChanges( $params['dir'] );
+               foreach ( $this->backends as $index => $backend ) {
+                       if ( $replicate || $index == $this->masterIndex ) {
+                               $realParams = $this->substOpPaths( $params, $backend );
+                               $status->merge( $backend->doPrepare( $realParams ) );
+                       }
                }
                return $status;
        }
@@ -403,9 +433,12 @@ class FileBackendMultiWrite extends FileBackend {
         */
        protected function doSecure( array $params ) {
                $status = Status::newGood();
-               foreach ( $this->backends as $backend ) {
-                       $realParams = $this->substOpPaths( $params, $backend );
-                       $status->merge( $backend->doSecure( $realParams ) );
+               $replicate = $this->replicateContainerDirChanges( $params['dir'] );
+               foreach ( $this->backends as $index => $backend ) {
+                       if ( $replicate || $index == $this->masterIndex ) {
+                               $realParams = $this->substOpPaths( $params, $backend );
+                               $status->merge( $backend->doSecure( $realParams ) );
+                       }
                }
                return $status;
        }
@@ -417,9 +450,12 @@ class FileBackendMultiWrite extends FileBackend {
         */
        protected function doPublish( array $params ) {
                $status = Status::newGood();
-               foreach ( $this->backends as $backend ) {
-                       $realParams = $this->substOpPaths( $params, $backend );
-                       $status->merge( $backend->doPublish( $realParams ) );
+               $replicate = $this->replicateContainerDirChanges( $params['dir'] );
+               foreach ( $this->backends as $index => $backend ) {
+                       if ( $replicate || $index == $this->masterIndex ) {
+                               $realParams = $this->substOpPaths( $params, $backend );
+                               $status->merge( $backend->doPublish( $realParams ) );
+                       }
                }
                return $status;
        }
@@ -431,9 +467,12 @@ class FileBackendMultiWrite extends FileBackend {
         */
        protected function doClean( array $params ) {
                $status = Status::newGood();
-               foreach ( $this->backends as $backend ) {
-                       $realParams = $this->substOpPaths( $params, $backend );
-                       $status->merge( $backend->doClean( $realParams ) );
+               $replicate = $this->replicateContainerDirChanges( $params['dir'] );
+               foreach ( $this->backends as $index => $backend ) {
+                       if ( $replicate || $index == $this->masterIndex ) {
+                               $realParams = $this->substOpPaths( $params, $backend );
+                               $status->merge( $backend->doClean( $realParams ) );
+                       }
                }
                return $status;
        }