Merge "Ensure users are able to edit the page after changing the content model"
[lhc/web/wiklou.git] / includes / filebackend / FileBackendMultiWrite.php
index 6f40bda..52b84d4 100644 (file)
@@ -114,7 +114,7 @@ class FileBackendMultiWrite extends FileBackend {
                        }
                        $name = $config['name'];
                        if ( isset( $namesUsed[$name] ) ) { // don't break FileOp predicates
-                               throw new FileBackendError( "Two or more backends defined with the name $name." );
+                               throw new LogicException( "Two or more backends defined with the name $name." );
                        }
                        $namesUsed[$name] = 1;
                        // Alter certain sub-backend settings for sanity
@@ -124,7 +124,7 @@ class FileBackendMultiWrite extends FileBackend {
                        $config['wikiId'] = $this->wikiId; // use the proxy backend wiki ID
                        if ( !empty( $config['isMultiMaster'] ) ) {
                                if ( $this->masterIndex >= 0 ) {
-                                       throw new FileBackendError( 'More than one master backend defined.' );
+                                       throw new LogicException( 'More than one master backend defined.' );
                                }
                                $this->masterIndex = $index; // this is the "master"
                                $config['fileJournal'] = $this->fileJournal; // log under proxy backend
@@ -134,13 +134,13 @@ class FileBackendMultiWrite extends FileBackend {
                        }
                        // Create sub-backend object
                        if ( !isset( $config['class'] ) ) {
-                               throw new FileBackendError( 'No class given for a backend config.' );
+                               throw new InvalidArgumentException( 'No class given for a backend config.' );
                        }
                        $class = $config['class'];
                        $this->backends[$index] = new $class( $config );
                }
                if ( $this->masterIndex < 0 ) { // need backends and must have a master
-                       throw new FileBackendError( 'No master backend defined.' );
+                       throw new LogicException( 'No master backend defined.' );
                }
                if ( $this->readIndex < 0 ) {
                        $this->readIndex = $this->masterIndex; // default
@@ -148,7 +148,7 @@ class FileBackendMultiWrite extends FileBackend {
        }
 
        final protected function doOperationsInternal( array $ops, array $opts ) {
-               $status = Status::newGood();
+               $status = $this->newStatus();
 
                $mbe = $this->backends[$this->masterIndex]; // convenience
 
@@ -178,7 +178,9 @@ class FileBackendMultiWrite extends FileBackend {
                        wfDebugLog( 'FileOperation', get_class( $this ) .
                                " failed sync check: " . FormatJson::encode( $relevantPaths ) );
                        // Try to resync the clone backends to the master on the spot...
-                       if ( !$this->autoResync || !$this->resyncFiles( $relevantPaths )->isOK() ) {
+                       if ( $this->autoResync === false
+                               || !$this->resyncFiles( $relevantPaths, $this->autoResync )->isOK()
+                       ) {
                                $status->merge( $syncStatus );
 
                                return $status; // abort
@@ -231,14 +233,20 @@ class FileBackendMultiWrite extends FileBackend {
         * Check that a set of files are consistent across all internal backends
         *
         * @param array $paths List of storage paths
-        * @return Status
+        * @return StatusValue
         */
        public function consistencyCheck( array $paths ) {
-               $status = Status::newGood();
+               $status = $this->newStatus();
                if ( $this->syncChecks == 0 || count( $this->backends ) <= 1 ) {
                        return $status; // skip checks
                }
 
+               // Preload all of the stat info in as few round trips as possible...
+               foreach ( $this->backends as $backend ) {
+                       $realPaths = $this->substPaths( $paths, $backend );
+                       $backend->preloadFileStat( [ 'srcs' => $realPaths, 'latest' => true ] );
+               }
+
                $mBackend = $this->backends[$this->masterIndex];
                foreach ( $paths as $path ) {
                        $params = [ 'src' => $path, 'latest' => true ];
@@ -297,10 +305,10 @@ class FileBackendMultiWrite extends FileBackend {
         * Check that a set of file paths are usable across all internal backends
         *
         * @param array $paths List of storage paths
-        * @return Status
+        * @return StatusValue
         */
        public function accessibilityCheck( array $paths ) {
-               $status = Status::newGood();
+               $status = $this->newStatus();
                if ( count( $this->backends ) <= 1 ) {
                        return $status; // skip checks
                }
@@ -322,10 +330,11 @@ class FileBackendMultiWrite extends FileBackend {
         * and re-synchronize those files against the "multi master" if needed.
         *
         * @param array $paths List of storage paths
-        * @return Status
+        * @param string|bool $resyncMode False, True, or "conservative"; see __construct()
+        * @return StatusValue
         */
-       public function resyncFiles( array $paths ) {
-               $status = Status::newGood();
+       public function resyncFiles( array $paths, $resyncMode = true ) {
+               $status = $this->newStatus();
 
                $mBackend = $this->backends[$this->masterIndex];
                foreach ( $paths as $path ) {
@@ -355,7 +364,7 @@ class FileBackendMultiWrite extends FileBackend {
                                if ( $mSha1 === $cSha1 ) {
                                        // already synced; nothing to do
                                } elseif ( $mSha1 !== false ) { // file is in master
-                                       if ( $this->autoResync === 'conservative'
+                                       if ( $resyncMode === 'conservative'
                                                && $cStat && $cStat['mtime'] > $mStat['mtime']
                                        ) {
                                                $status->fatal( 'backend-fail-synced', $path );
@@ -367,7 +376,7 @@ class FileBackendMultiWrite extends FileBackend {
                                                [ 'src' => $fsFile->getPath(), 'dst' => $cPath ]
                                        ) );
                                } elseif ( $mStat === false ) { // file is not in master
-                                       if ( $this->autoResync === 'conservative' ) {
+                                       if ( $resyncMode === 'conservative' ) {
                                                $status->fatal( 'backend-fail-synced', $path );
                                                continue; // don't delete data
                                        }
@@ -376,6 +385,11 @@ class FileBackendMultiWrite extends FileBackend {
                        }
                }
 
+               if ( !$status->isOK() ) {
+                       wfDebugLog( 'FileOperation', get_class( $this ) .
+                               " failed to resync: " . FormatJson::encode( $paths ) );
+               }
+
                return $status;
        }
 
@@ -488,8 +502,8 @@ class FileBackendMultiWrite extends FileBackend {
        }
 
        protected function doQuickOperationsInternal( array $ops ) {
-               $status = Status::newGood();
-               // Do the operations on the master backend; setting Status fields...
+               $status = $this->newStatus();
+               // Do the operations on the master backend; setting StatusValue fields...
                $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
                $masterStatus = $this->backends[$this->masterIndex]->doQuickOperations( $realOps );
                $status->merge( $masterStatus );
@@ -539,10 +553,10 @@ class FileBackendMultiWrite extends FileBackend {
        /**
         * @param string $method One of (doPrepare,doSecure,doPublish,doClean)
         * @param array $params Method arguments
-        * @return Status
+        * @return StatusValue
         */
        protected function doDirectoryOp( $method, array $params ) {
-               $status = Status::newGood();
+               $status = $this->newStatus();
 
                $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
                $masterStatus = $this->backends[$this->masterIndex]->$method( $realParams );
@@ -722,7 +736,7 @@ class FileBackendMultiWrite extends FileBackend {
                return $this->backends[$index]->preloadFileStat( $realParams );
        }
 
-       public function getScopedLocksForOps( array $ops, Status $status ) {
+       public function getScopedLocksForOps( array $ops, StatusValue $status ) {
                $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
                $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $realOps );
                // Get the paths to lock from the master backend