95 parent::__construct( $config );
96 $this->syncChecks = isset( $config[
'syncChecks'] )
97 ? $config[
'syncChecks']
99 $this->autoResync = isset( $config[
'autoResync'] )
100 ? $config[
'autoResync']
102 $this->asyncWrites = isset( $config[
'replication'] ) && $config[
'replication'] ===
'async';
106 foreach ( $config[
'backends']
as $index => $config ) {
107 $name = $config[
'name'];
108 if ( isset( $namesUsed[
$name] ) ) {
109 throw new LogicException(
"Two or more backends defined with the name $name." );
111 $namesUsed[
$name] = 1;
113 unset( $config[
'readOnly'] );
114 unset( $config[
'fileJournal'] );
115 unset( $config[
'lockManager'] );
117 if ( !empty( $config[
'isMultiMaster'] ) ) {
118 if ( $this->masterIndex >= 0 ) {
119 throw new LogicException(
'More than one master backend defined.' );
121 $this->masterIndex = $index;
124 if ( !empty( $config[
'readAffinity'] ) ) {
125 $this->readIndex = $index;
128 if ( !isset( $config[
'class'] ) ) {
129 throw new InvalidArgumentException(
'No class given for a backend config.' );
131 $class = $config[
'class'];
132 $this->backends[$index] =
new $class( $config );
134 if ( $this->masterIndex < 0 ) {
135 throw new LogicException(
'No master backend defined.' );
137 if ( $this->readIndex < 0 ) {
149 if ( empty( $opts[
'nonLocking'] ) ) {
159 $opts[
'preserveCache'] =
true;
169 if ( !$syncStatus->isOK() ) {
173 if ( $this->autoResync ===
false
174 || !$this->
resyncFiles( $relevantPaths, $this->autoResync )->isOK()
183 $masterStatus = $mbe->doOperations( $realOps, $opts );
184 $status->merge( $masterStatus );
189 if ( $masterStatus->isOK() && $masterStatus->successCount > 0 ) {
190 foreach ( $this->backends
as $index => $backend ) {
191 if ( $index === $this->masterIndex ) {
199 function()
use ( $backend, $realOps, $opts, $scopeLock, $relevantPaths ) {
201 "'{$backend->getName()}' async replication; paths: " .
203 $backend->doOperations( $realOps, $opts );
208 "'{$backend->getName()}' sync replication; paths: " .
210 $status->merge( $backend->doOperations( $realOps, $opts ) );
217 $status->success = $masterStatus->success;
218 $status->successCount = $masterStatus->successCount;
219 $status->failCount = $masterStatus->failCount;
232 if ( $this->syncChecks == 0 ||
count( $this->backends ) <= 1 ) {
237 foreach ( $this->backends
as $backend ) {
238 $realPaths = $this->
substPaths( $paths, $backend );
239 $backend->preloadFileStat( [
'srcs' => $realPaths,
'latest' =>
true ] );
247 $mStat = $mBackend->getFileStat( $mParams );
248 if ( $this->syncChecks & self::CHECK_SHA1 ) {
249 $mSha1 = $mBackend->getFileSha1Base36( $mParams );
254 foreach ( $this->backends
as $index => $cBackend ) {
255 if ( $index === $this->masterIndex ) {
259 $cStat = $cBackend->getFileStat( $cParams );
265 if ( $this->syncChecks & self::CHECK_SIZE ) {
266 if ( $cStat[
'size'] != $mStat[
'size'] ) {
271 if ( $this->syncChecks & self::CHECK_TIME ) {
274 if ( abs( $mTs - $cTs ) > 30 ) {
279 if ( $this->syncChecks & self::CHECK_SHA1 ) {
280 if ( $cBackend->getFileSha1Base36( $cParams ) !== $mSha1 ) {
304 if (
count( $this->backends ) <= 1 ) {
309 foreach ( $this->backends
as $backend ) {
310 $realPath = $this->
substPaths( $path, $backend );
311 if ( !$backend->isPathUsableInternal( $realPath ) ) {
333 $mPath = $this->
substPaths( $path, $mBackend );
334 $mSha1 = $mBackend->getFileSha1Base36( [
'src' => $mPath,
'latest' =>
true ] );
335 $mStat = $mBackend->getFileStat( [
'src' => $mPath,
'latest' =>
true ] );
336 if ( $mStat ===
null || ( $mSha1 !==
false && !$mStat ) ) {
337 $status->fatal(
'backend-fail-internal', $this->
name );
339 .
': File is not available on the master backend' );
343 foreach ( $this->backends
as $index => $cBackend ) {
344 if ( $index === $this->masterIndex ) {
347 $cPath = $this->
substPaths( $path, $cBackend );
348 $cSha1 = $cBackend->getFileSha1Base36( [
'src' => $cPath,
'latest' =>
true ] );
349 $cStat = $cBackend->getFileStat( [
'src' => $cPath,
'latest' =>
true ] );
350 if ( $cStat ===
null || ( $cSha1 !==
false && !$cStat ) ) {
351 $status->fatal(
'backend-fail-internal', $cBackend->getName() );
353 ': File is not available on the clone backend' );
356 if ( $mSha1 === $cSha1 ) {
358 } elseif ( $mSha1 !==
false ) {
359 if ( $resyncMode ===
'conservative'
360 && $cStat && $cStat[
'mtime'] > $mStat[
'mtime']
365 $fsFile = $mBackend->getLocalReference(
366 [
'src' => $mPath,
'latest' =>
true ] );
367 $status->merge( $cBackend->quickStore(
368 [
'src' => $fsFile->getPath(),
'dst' => $cPath ]
370 } elseif ( $mStat ===
false ) {
371 if ( $resyncMode ===
'conservative' ) {
375 $status->merge( $cBackend->quickDelete( [
'src' => $cPath ] ) );
396 foreach ( $ops
as $op ) {
397 if ( isset( $op[
'src'] ) ) {
400 if ( empty( $op[
'ignoreMissingSource'] )
401 || $this->
fileExists( [
'src' => $op[
'src'] ] )
403 $paths[] = $op[
'src'];
406 if ( isset( $op[
'srcs'] ) ) {
407 $paths = array_merge( $paths, $op[
'srcs'] );
409 if ( isset( $op[
'dst'] ) ) {
410 $paths[] = $op[
'dst'];
414 return array_values( array_unique( array_filter( $paths,
'FileBackend::isStoragePath' ) ) );
427 foreach ( $ops
as $op ) {
429 foreach ( [
'src',
'srcs',
'dst',
'dir' ]
as $par ) {
430 if ( isset( $newOp[$par] ) ) {
431 $newOp[$par] = $this->
substPaths( $newOp[$par], $backend );
462 '!^mwstore://' . preg_quote( $this->
name,
'!' ) .
'/!',
476 '!^mwstore://([^/]+)!',
487 foreach ( $ops
as $op ) {
488 if ( $op[
'op'] ===
'store' && !isset( $op[
'srcRef'] ) ) {
499 $realOps = $this->
substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
501 $status->merge( $masterStatus );
503 foreach ( $this->backends
as $index => $backend ) {
504 if ( $index === $this->masterIndex ) {
511 function()
use ( $backend, $realOps ) {
512 $backend->doQuickOperations( $realOps );
516 $status->merge( $backend->doQuickOperations( $realOps ) );
522 $status->success = $masterStatus->success;
523 $status->successCount = $masterStatus->successCount;
524 $status->failCount = $masterStatus->failCount;
553 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
555 $status->merge( $masterStatus );
557 foreach ( $this->backends
as $index => $backend ) {
558 if ( $index === $this->masterIndex ) {
563 if ( $this->asyncWrites ) {
565 function()
use ( $backend, $method, $realParams ) {
566 $backend->$method( $realParams );
570 $status->merge( $backend->$method( $realParams ) );
581 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
590 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
592 return $this->backends[$index]->fileExists( $realParams );
597 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
599 return $this->backends[$index]->getFileTimestamp( $realParams );
604 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
606 return $this->backends[$index]->getFileSize( $realParams );
611 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
613 return $this->backends[$index]->getFileStat( $realParams );
618 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
620 return $this->backends[$index]->getFileXAttributes( $realParams );
625 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
627 $contentsM = $this->backends[$index]->getFileContentsMulti( $realParams );
630 foreach ( $contentsM
as $path => $data ) {
639 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
641 return $this->backends[$index]->getFileSha1Base36( $realParams );
646 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
648 return $this->backends[$index]->getFileProps( $realParams );
653 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
655 return $this->backends[$index]->streamFile( $realParams );
660 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
662 $fsFilesM = $this->backends[$index]->getLocalReferenceMulti( $realParams );
665 foreach ( $fsFilesM
as $path => $fsFile ) {
674 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
676 $tempFilesM = $this->backends[$index]->getLocalCopyMulti( $realParams );
679 foreach ( $tempFilesM
as $path => $tempFile ) {
688 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
690 return $this->backends[$index]->getFileHttpUrl( $realParams );
694 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
700 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
706 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
716 foreach ( $this->backends
as $backend ) {
717 $realPaths = is_array( $paths ) ? $this->
substPaths( $paths, $backend ) :
null;
718 $backend->clearCache( $realPaths );
723 $realPaths = $this->
substPaths( $paths, $this->backends[$this->readIndex] );
729 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
731 return $this->backends[$index]->preloadFileStat( $realParams );
735 $realOps = $this->
substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
738 $paths = $this->backends[
$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );