*/
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;
$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.
}
$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 ) );
}
}
$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 );
$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__
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__ .
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 ] ) );
}
}
}
* @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'];
}
* @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 );
}
* @return array
*/
protected function substOpPaths( array $ops, FileBackendStore $backend ) {
- $newOps = $this->substOpBatchPaths( array( $ops ), $backend );
+ $newOps = $this->substOpBatchPaths( [ $ops ], $backend );
return $newOps[0];
}
);
}
+ /**
+ * @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...
}
$realOps = $this->substOpBatchPaths( $ops, $backend );
- if ( $this->asyncWrites ) {
+ if ( $this->asyncWrites && !$this->hasVolatileSources( $ops ) ) {
DeferredUpdates::addCallableUpdate(
function() use ( $backend, $realOps ) {
$backend->doQuickOperations( $realOps );
$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;
}
$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;
}
$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;
}
// 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 );