97 parent::__construct( $config );
98 $this->syncChecks = isset( $config[
'syncChecks'] )
99 ? $config[
'syncChecks']
101 $this->autoResync = isset( $config[
'autoResync'] )
102 ? $config[
'autoResync']
104 $this->asyncWrites = isset( $config[
'replication'] ) && $config[
'replication'] ===
'async';
108 foreach ( $config[
'backends']
as $index => $config ) {
109 $name = $config[
'name'];
110 if ( isset( $namesUsed[
$name] ) ) {
111 throw new LogicException(
"Two or more backends defined with the name $name." );
113 $namesUsed[
$name] = 1;
115 unset( $config[
'readOnly'] );
116 unset( $config[
'fileJournal'] );
117 unset( $config[
'lockManager'] );
119 if ( !empty( $config[
'isMultiMaster'] ) ) {
120 if ( $this->masterIndex >= 0 ) {
121 throw new LogicException(
'More than one master backend defined.' );
123 $this->masterIndex = $index;
126 if ( !empty( $config[
'readAffinity'] ) ) {
127 $this->readIndex = $index;
130 if ( !isset( $config[
'class'] ) ) {
131 throw new InvalidArgumentException(
'No class given for a backend config.' );
133 $class = $config[
'class'];
134 $this->backends[$index] =
new $class( $config );
136 if ( $this->masterIndex < 0 ) {
137 throw new LogicException(
'No master backend defined.' );
139 if ( $this->readIndex < 0 ) {
151 if ( empty( $opts[
'nonLocking'] ) ) {
161 $opts[
'preserveCache'] =
true;
171 if ( !$syncStatus->isOK() ) {
175 if ( $this->autoResync ===
false
176 || !$this->
resyncFiles( $relevantPaths, $this->autoResync )->isOK()
185 $masterStatus = $mbe->doOperations( $realOps, $opts );
186 $status->merge( $masterStatus );
191 if ( $masterStatus->isOK() && $masterStatus->successCount > 0 ) {
192 foreach ( $this->backends
as $index => $backend ) {
193 if ( $index === $this->masterIndex ) {
201 function ()
use ( $backend, $realOps, $opts, $scopeLock, $relevantPaths ) {
203 "'{$backend->getName()}' async replication; paths: " .
205 $backend->doOperations( $realOps, $opts );
210 "'{$backend->getName()}' sync replication; paths: " .
212 $status->merge( $backend->doOperations( $realOps, $opts ) );
219 $status->success = $masterStatus->success;
220 $status->successCount = $masterStatus->successCount;
221 $status->failCount = $masterStatus->failCount;
234 if ( $this->syncChecks == 0 ||
count( $this->backends ) <= 1 ) {
239 foreach ( $this->backends
as $backend ) {
240 $realPaths = $this->
substPaths( $paths, $backend );
241 $backend->preloadFileStat( [
'srcs' => $realPaths,
'latest' =>
true ] );
249 $mStat = $mBackend->getFileStat( $mParams );
250 if ( $this->syncChecks & self::CHECK_SHA1 ) {
251 $mSha1 = $mBackend->getFileSha1Base36( $mParams );
256 foreach ( $this->backends
as $index => $cBackend ) {
257 if ( $index === $this->masterIndex ) {
261 $cStat = $cBackend->getFileStat( $cParams );
267 if ( $this->syncChecks & self::CHECK_SIZE ) {
268 if ( $cStat[
'size'] != $mStat[
'size'] ) {
273 if ( $this->syncChecks & self::CHECK_TIME ) {
276 if ( abs( $mTs - $cTs ) > 30 ) {
281 if ( $this->syncChecks & self::CHECK_SHA1 ) {
282 if ( $cBackend->getFileSha1Base36( $cParams ) !== $mSha1 ) {
306 if (
count( $this->backends ) <= 1 ) {
311 foreach ( $this->backends
as $backend ) {
312 $realPath = $this->
substPaths( $path, $backend );
313 if ( !$backend->isPathUsableInternal( $realPath ) ) {
335 $mPath = $this->
substPaths( $path, $mBackend );
336 $mSha1 = $mBackend->getFileSha1Base36( [
'src' => $mPath,
'latest' =>
true ] );
337 $mStat = $mBackend->getFileStat( [
'src' => $mPath,
'latest' =>
true ] );
338 if ( $mStat ===
null || ( $mSha1 !==
false && !$mStat ) ) {
339 $status->fatal(
'backend-fail-internal', $this->
name );
341 .
': File is not available on the master backend' );
345 foreach ( $this->backends
as $index => $cBackend ) {
346 if ( $index === $this->masterIndex ) {
349 $cPath = $this->
substPaths( $path, $cBackend );
350 $cSha1 = $cBackend->getFileSha1Base36( [
'src' => $cPath,
'latest' =>
true ] );
351 $cStat = $cBackend->getFileStat( [
'src' => $cPath,
'latest' =>
true ] );
352 if ( $cStat ===
null || ( $cSha1 !==
false && !$cStat ) ) {
353 $status->fatal(
'backend-fail-internal', $cBackend->getName() );
355 ': File is not available on the clone backend' );
358 if ( $mSha1 === $cSha1 ) {
360 } elseif ( $mSha1 !==
false ) {
361 if ( $resyncMode ===
'conservative'
362 && $cStat && $cStat[
'mtime'] > $mStat[
'mtime']
367 $fsFile = $mBackend->getLocalReference(
368 [
'src' => $mPath,
'latest' =>
true ] );
369 $status->merge( $cBackend->quickStore(
370 [
'src' => $fsFile->getPath(),
'dst' => $cPath ]
372 } elseif ( $mStat ===
false ) {
373 if ( $resyncMode ===
'conservative' ) {
377 $status->merge( $cBackend->quickDelete( [
'src' => $cPath ] ) );
398 foreach ( $ops
as $op ) {
399 if ( isset( $op[
'src'] ) ) {
402 if ( empty( $op[
'ignoreMissingSource'] )
403 || $this->
fileExists( [
'src' => $op[
'src'] ] )
405 $paths[] = $op[
'src'];
408 if ( isset( $op[
'srcs'] ) ) {
409 $paths = array_merge( $paths, $op[
'srcs'] );
411 if ( isset( $op[
'dst'] ) ) {
412 $paths[] = $op[
'dst'];
416 return array_values( array_unique( array_filter( $paths,
'FileBackend::isStoragePath' ) ) );
429 foreach ( $ops
as $op ) {
431 foreach ( [
'src',
'srcs',
'dst',
'dir' ]
as $par ) {
432 if ( isset( $newOp[$par] ) ) {
433 $newOp[$par] = $this->
substPaths( $newOp[$par], $backend );
464 '!^mwstore://' . preg_quote( $this->
name,
'!' ) .
'/!',
478 '!^mwstore://([^/]+)!',
489 foreach ( $ops
as $op ) {
490 if ( $op[
'op'] ===
'store' && !isset( $op[
'srcRef'] ) ) {
501 $realOps = $this->
substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
503 $status->merge( $masterStatus );
505 foreach ( $this->backends
as $index => $backend ) {
506 if ( $index === $this->masterIndex ) {
513 function ()
use ( $backend, $realOps ) {
514 $backend->doQuickOperations( $realOps );
518 $status->merge( $backend->doQuickOperations( $realOps ) );
524 $status->success = $masterStatus->success;
525 $status->successCount = $masterStatus->successCount;
526 $status->failCount = $masterStatus->failCount;
555 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
557 $status->merge( $masterStatus );
559 foreach ( $this->backends
as $index => $backend ) {
560 if ( $index === $this->masterIndex ) {
565 if ( $this->asyncWrites ) {
567 function ()
use ( $backend, $method, $realParams ) {
568 $backend->$method( $realParams );
572 $status->merge( $backend->$method( $realParams ) );
583 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
592 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
594 return $this->backends[$index]->fileExists( $realParams );
599 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
601 return $this->backends[$index]->getFileTimestamp( $realParams );
606 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
608 return $this->backends[$index]->getFileSize( $realParams );
613 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
615 return $this->backends[$index]->getFileStat( $realParams );
620 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
622 return $this->backends[$index]->getFileXAttributes( $realParams );
627 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
629 $contentsM = $this->backends[$index]->getFileContentsMulti( $realParams );
632 foreach ( $contentsM
as $path => $data ) {
641 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
643 return $this->backends[$index]->getFileSha1Base36( $realParams );
648 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
650 return $this->backends[$index]->getFileProps( $realParams );
655 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
657 return $this->backends[$index]->streamFile( $realParams );
662 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
664 $fsFilesM = $this->backends[$index]->getLocalReferenceMulti( $realParams );
667 foreach ( $fsFilesM
as $path => $fsFile ) {
676 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
678 $tempFilesM = $this->backends[$index]->getLocalCopyMulti( $realParams );
681 foreach ( $tempFilesM
as $path => $tempFile ) {
690 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
692 return $this->backends[$index]->getFileHttpUrl( $realParams );
696 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
702 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
708 $realParams = $this->
substOpPaths( $params, $this->backends[$this->masterIndex] );
718 foreach ( $this->backends
as $backend ) {
719 $realPaths = is_array( $paths ) ? $this->
substPaths( $paths, $backend ) :
null;
720 $backend->clearCache( $realPaths );
725 $realPaths = $this->
substPaths( $paths, $this->backends[$this->readIndex] );
731 $realParams = $this->
substOpPaths( $params, $this->backends[$index] );
733 return $this->backends[$index]->preloadFileStat( $realParams );
737 $realOps = $this->
substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
740 $paths = $this->backends[
$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );