Merge "Enable filtering by action on Special:Log"
[lhc/web/wiklou.git] / includes / filebackend / FileBackendMultiWrite.php
index 3841f2e..6f40bda 100644 (file)
@@ -43,7 +43,7 @@
  */
 class FileBackendMultiWrite extends FileBackend {
        /** @var FileBackendStore[] Prioritized list of FileBackendStore objects */
-       protected $backends = array();
+       protected $backends = [];
 
        /** @var int Index of master backend */
        protected $masterIndex = -1;
@@ -105,7 +105,7 @@ class FileBackendMultiWrite extends FileBackend {
                $this->asyncWrites = isset( $config['replication'] ) && $config['replication'] === 'async';
                // Construct backends here rather than via registration
                // to keep these backends hidden from outside the proxy.
-               $namesUsed = array();
+               $namesUsed = [];
                foreach ( $config['backends'] as $index => $config ) {
                        if ( isset( $config['template'] ) ) {
                                // Config is just a modified version of a registered backend's.
@@ -199,14 +199,20 @@ class FileBackendMultiWrite extends FileBackend {
                                }
 
                                $realOps = $this->substOpBatchPaths( $ops, $backend );
-                               if ( $this->asyncWrites ) {
+                               if ( $this->asyncWrites && !$this->hasVolatileSources( $ops ) ) {
                                        // Bind $scopeLock to the callback to preserve locks
                                        DeferredUpdates::addCallableUpdate(
-                                               function() use ( $backend, $realOps, $opts, $scopeLock ) {
+                                               function() use ( $backend, $realOps, $opts, $scopeLock, $relevantPaths ) {
+                                                       wfDebugLog( 'FileOperationReplication',
+                                                               "'{$backend->getName()}' async replication; paths: " .
+                                                               FormatJson::encode( $relevantPaths ) );
                                                        $backend->doOperations( $realOps, $opts );
                                                }
                                        );
                                } else {
+                                       wfDebugLog( 'FileOperationReplication',
+                                               "'{$backend->getName()}' sync replication; paths: " .
+                                               FormatJson::encode( $relevantPaths ) );
                                        $status->merge( $backend->doOperations( $realOps, $opts ) );
                                }
                        }
@@ -235,7 +241,7 @@ class FileBackendMultiWrite extends FileBackend {
 
                $mBackend = $this->backends[$this->masterIndex];
                foreach ( $paths as $path ) {
-                       $params = array( 'src' => $path, 'latest' => true );
+                       $params = [ 'src' => $path, 'latest' => true ];
                        $mParams = $this->substOpPaths( $params, $mBackend );
                        // Stat the file on the 'master' backend
                        $mStat = $mBackend->getFileStat( $mParams );
@@ -324,8 +330,8 @@ class FileBackendMultiWrite extends FileBackend {
                $mBackend = $this->backends[$this->masterIndex];
                foreach ( $paths as $path ) {
                        $mPath = $this->substPaths( $path, $mBackend );
-                       $mSha1 = $mBackend->getFileSha1Base36( array( 'src' => $mPath, 'latest' => true ) );
-                       $mStat = $mBackend->getFileStat( array( 'src' => $mPath, 'latest' => true ) );
+                       $mSha1 = $mBackend->getFileSha1Base36( [ 'src' => $mPath, 'latest' => true ] );
+                       $mStat = $mBackend->getFileStat( [ 'src' => $mPath, 'latest' => true ] );
                        if ( $mStat === null || ( $mSha1 !== false && !$mStat ) ) { // sanity
                                $status->fatal( 'backend-fail-internal', $this->name );
                                wfDebugLog( 'FileOperation', __METHOD__
@@ -338,8 +344,8 @@ class FileBackendMultiWrite extends FileBackend {
                                        continue; // master
                                }
                                $cPath = $this->substPaths( $path, $cBackend );
-                               $cSha1 = $cBackend->getFileSha1Base36( array( 'src' => $cPath, 'latest' => true ) );
-                               $cStat = $cBackend->getFileStat( array( 'src' => $cPath, 'latest' => true ) );
+                               $cSha1 = $cBackend->getFileSha1Base36( [ 'src' => $cPath, 'latest' => true ] );
+                               $cStat = $cBackend->getFileStat( [ 'src' => $cPath, 'latest' => true ] );
                                if ( $cStat === null || ( $cSha1 !== false && !$cStat ) ) { // sanity
                                        $status->fatal( 'backend-fail-internal', $cBackend->getName() );
                                        wfDebugLog( 'FileOperation', __METHOD__ .
@@ -356,16 +362,16 @@ class FileBackendMultiWrite extends FileBackend {
                                                continue; // don't rollback data
                                        }
                                        $fsFile = $mBackend->getLocalReference(
-                                               array( 'src' => $mPath, 'latest' => true ) );
+                                               [ 'src' => $mPath, 'latest' => true ] );
                                        $status->merge( $cBackend->quickStore(
-                                               array( 'src' => $fsFile->getPath(), 'dst' => $cPath )
+                                               [ 'src' => $fsFile->getPath(), 'dst' => $cPath ]
                                        ) );
                                } elseif ( $mStat === false ) { // file is not in master
                                        if ( $this->autoResync === 'conservative' ) {
                                                $status->fatal( 'backend-fail-synced', $path );
                                                continue; // don't delete data
                                        }
-                                       $status->merge( $cBackend->quickDelete( array( 'src' => $cPath ) ) );
+                                       $status->merge( $cBackend->quickDelete( [ 'src' => $cPath ] ) );
                                }
                        }
                }
@@ -380,13 +386,13 @@ class FileBackendMultiWrite extends FileBackend {
         * @return array List of storage paths to files (does not include directories)
         */
        protected function fileStoragePathsForOps( array $ops ) {
-               $paths = array();
+               $paths = [];
                foreach ( $ops as $op ) {
                        if ( isset( $op['src'] ) ) {
                                // For things like copy/move/delete with "ignoreMissingSource" and there
                                // is no source file, nothing should happen and there should be no errors.
                                if ( empty( $op['ignoreMissingSource'] )
-                                       || $this->fileExists( array( 'src' => $op['src'] ) )
+                                       || $this->fileExists( [ 'src' => $op['src'] ] )
                                ) {
                                        $paths[] = $op['src'];
                                }
@@ -411,10 +417,10 @@ class FileBackendMultiWrite extends FileBackend {
         * @return array
         */
        protected function substOpBatchPaths( array $ops, FileBackendStore $backend ) {
-               $newOps = array(); // operations
+               $newOps = []; // operations
                foreach ( $ops as $op ) {
                        $newOp = $op; // operation
-                       foreach ( array( 'src', 'srcs', 'dst', 'dir' ) as $par ) {
+                       foreach ( [ 'src', 'srcs', 'dst', 'dir' ] as $par ) {
                                if ( isset( $newOp[$par] ) ) { // string or array
                                        $newOp[$par] = $this->substPaths( $newOp[$par], $backend );
                                }
@@ -433,7 +439,7 @@ class FileBackendMultiWrite extends FileBackend {
         * @return array
         */
        protected function substOpPaths( array $ops, FileBackendStore $backend ) {
-               $newOps = $this->substOpBatchPaths( array( $ops ), $backend );
+               $newOps = $this->substOpBatchPaths( [ $ops ], $backend );
 
                return $newOps[0];
        }
@@ -467,6 +473,20 @@ class FileBackendMultiWrite extends FileBackend {
                );
        }
 
+       /**
+        * @param array $ops File operations for FileBackend::doOperations()
+        * @return bool Whether there are file path sources with outside lifetime/ownership
+        */
+       protected function hasVolatileSources( array $ops ) {
+               foreach ( $ops as $op ) {
+                       if ( $op['op'] === 'store' && !isset( $op['srcRef'] ) ) {
+                               return true; // source file might be deleted anytime after do*Operations()
+                       }
+               }
+
+               return false;
+       }
+
        protected function doQuickOperationsInternal( array $ops ) {
                $status = Status::newGood();
                // Do the operations on the master backend; setting Status fields...
@@ -480,7 +500,7 @@ class FileBackendMultiWrite extends FileBackend {
                        }
 
                        $realOps = $this->substOpBatchPaths( $ops, $backend );
-                       if ( $this->asyncWrites ) {
+                       if ( $this->asyncWrites && !$this->hasVolatileSources( $ops ) ) {
                                DeferredUpdates::addCallableUpdate(
                                        function() use ( $backend, $realOps ) {
                                                $backend->doQuickOperations( $realOps );
@@ -597,7 +617,7 @@ class FileBackendMultiWrite extends FileBackend {
 
                $contentsM = $this->backends[$index]->getFileContentsMulti( $realParams );
 
-               $contents = array(); // (path => FSFile) mapping using the proxy backend's name
+               $contents = []; // (path => FSFile) mapping using the proxy backend's name
                foreach ( $contentsM as $path => $data ) {
                        $contents[$this->unsubstPaths( $path )] = $data;
                }
@@ -632,7 +652,7 @@ class FileBackendMultiWrite extends FileBackend {
 
                $fsFilesM = $this->backends[$index]->getLocalReferenceMulti( $realParams );
 
-               $fsFiles = array(); // (path => FSFile) mapping using the proxy backend's name
+               $fsFiles = []; // (path => FSFile) mapping using the proxy backend's name
                foreach ( $fsFilesM as $path => $fsFile ) {
                        $fsFiles[$this->unsubstPaths( $path )] = $fsFile;
                }
@@ -646,7 +666,7 @@ class FileBackendMultiWrite extends FileBackend {
 
                $tempFilesM = $this->backends[$index]->getLocalCopyMulti( $realParams );
 
-               $tempFiles = array(); // (path => TempFSFile) mapping using the proxy backend's name
+               $tempFiles = []; // (path => TempFSFile) mapping using the proxy backend's name
                foreach ( $tempFilesM as $path => $tempFile ) {
                        $tempFiles[$this->unsubstPaths( $path )] = $tempFile;
                }
@@ -708,10 +728,10 @@ class FileBackendMultiWrite extends FileBackend {
                // Get the paths to lock from the master backend
                $paths = $this->backends[$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );
                // Get the paths under the proxy backend's name
-               $pbPaths = array(
+               $pbPaths = [
                        LockManager::LOCK_UW => $this->unsubstPaths( $paths[LockManager::LOCK_UW] ),
                        LockManager::LOCK_EX => $this->unsubstPaths( $paths[LockManager::LOCK_EX] )
-               );
+               ];
 
                // Actually acquire the locks
                return $this->getScopedFileLocks( $pbPaths, 'mixed', $status );