MediaWiki  1.27.1
FileBackendMultiWrite.php
Go to the documentation of this file.
1 <?php
46  protected $backends = [];
47 
49  protected $masterIndex = -1;
51  protected $readIndex = -1;
52 
54  protected $syncChecks = 0;
56  protected $autoResync = false;
57 
59  protected $asyncWrites = false;
60 
61  /* Possible internal backend consistency checks */
62  const CHECK_SIZE = 1;
63  const CHECK_TIME = 2;
64  const CHECK_SHA1 = 4;
65 
97  public function __construct( array $config ) {
98  parent::__construct( $config );
99  $this->syncChecks = isset( $config['syncChecks'] )
100  ? $config['syncChecks']
101  : self::CHECK_SIZE;
102  $this->autoResync = isset( $config['autoResync'] )
103  ? $config['autoResync']
104  : false;
105  $this->asyncWrites = isset( $config['replication'] ) && $config['replication'] === 'async';
106  // Construct backends here rather than via registration
107  // to keep these backends hidden from outside the proxy.
108  $namesUsed = [];
109  foreach ( $config['backends'] as $index => $config ) {
110  if ( isset( $config['template'] ) ) {
111  // Config is just a modified version of a registered backend's.
112  // This should only be used when that config is used only by this backend.
113  $config = $config + FileBackendGroup::singleton()->config( $config['template'] );
114  }
115  $name = $config['name'];
116  if ( isset( $namesUsed[$name] ) ) { // don't break FileOp predicates
117  throw new FileBackendError( "Two or more backends defined with the name $name." );
118  }
119  $namesUsed[$name] = 1;
120  // Alter certain sub-backend settings for sanity
121  unset( $config['readOnly'] ); // use proxy backend setting
122  unset( $config['fileJournal'] ); // use proxy backend journal
123  unset( $config['lockManager'] ); // lock under proxy backend
124  $config['wikiId'] = $this->wikiId; // use the proxy backend wiki ID
125  if ( !empty( $config['isMultiMaster'] ) ) {
126  if ( $this->masterIndex >= 0 ) {
127  throw new FileBackendError( 'More than one master backend defined.' );
128  }
129  $this->masterIndex = $index; // this is the "master"
130  $config['fileJournal'] = $this->fileJournal; // log under proxy backend
131  }
132  if ( !empty( $config['readAffinity'] ) ) {
133  $this->readIndex = $index; // prefer this for reads
134  }
135  // Create sub-backend object
136  if ( !isset( $config['class'] ) ) {
137  throw new FileBackendError( 'No class given for a backend config.' );
138  }
139  $class = $config['class'];
140  $this->backends[$index] = new $class( $config );
141  }
142  if ( $this->masterIndex < 0 ) { // need backends and must have a master
143  throw new FileBackendError( 'No master backend defined.' );
144  }
145  if ( $this->readIndex < 0 ) {
146  $this->readIndex = $this->masterIndex; // default
147  }
148  }
149 
150  final protected function doOperationsInternal( array $ops, array $opts ) {
152 
153  $mbe = $this->backends[$this->masterIndex]; // convenience
154 
155  // Try to lock those files for the scope of this function...
156  $scopeLock = null;
157  if ( empty( $opts['nonLocking'] ) ) {
158  // Try to lock those files for the scope of this function...
160  $scopeLock = $this->getScopedLocksForOps( $ops, $status );
161  if ( !$status->isOK() ) {
162  return $status; // abort
163  }
164  }
165  // Clear any cache entries (after locks acquired)
166  $this->clearCache();
167  $opts['preserveCache'] = true; // only locked files are cached
168  // Get the list of paths to read/write...
169  $relevantPaths = $this->fileStoragePathsForOps( $ops );
170  // Check if the paths are valid and accessible on all backends...
171  $status->merge( $this->accessibilityCheck( $relevantPaths ) );
172  if ( !$status->isOK() ) {
173  return $status; // abort
174  }
175  // Do a consistency check to see if the backends are consistent...
176  $syncStatus = $this->consistencyCheck( $relevantPaths );
177  if ( !$syncStatus->isOK() ) {
178  wfDebugLog( 'FileOperation', get_class( $this ) .
179  " failed sync check: " . FormatJson::encode( $relevantPaths ) );
180  // Try to resync the clone backends to the master on the spot...
181  if ( $this->autoResync === false
182  || !$this->resyncFiles( $relevantPaths, $this->autoResync )->isOK()
183  ) {
184  $status->merge( $syncStatus );
185 
186  return $status; // abort
187  }
188  }
189  // Actually attempt the operation batch on the master backend...
190  $realOps = $this->substOpBatchPaths( $ops, $mbe );
191  $masterStatus = $mbe->doOperations( $realOps, $opts );
192  $status->merge( $masterStatus );
193  // Propagate the operations to the clone backends if there were no unexpected errors
194  // and if there were either no expected errors or if the 'force' option was used.
195  // However, if nothing succeeded at all, then don't replicate any of the operations.
196  // If $ops only had one operation, this might avoid backend sync inconsistencies.
197  if ( $masterStatus->isOK() && $masterStatus->successCount > 0 ) {
198  foreach ( $this->backends as $index => $backend ) {
199  if ( $index === $this->masterIndex ) {
200  continue; // done already
201  }
202 
203  $realOps = $this->substOpBatchPaths( $ops, $backend );
204  if ( $this->asyncWrites && !$this->hasVolatileSources( $ops ) ) {
205  // Bind $scopeLock to the callback to preserve locks
207  function() use ( $backend, $realOps, $opts, $scopeLock, $relevantPaths ) {
208  wfDebugLog( 'FileOperationReplication',
209  "'{$backend->getName()}' async replication; paths: " .
210  FormatJson::encode( $relevantPaths ) );
211  $backend->doOperations( $realOps, $opts );
212  }
213  );
214  } else {
215  wfDebugLog( 'FileOperationReplication',
216  "'{$backend->getName()}' sync replication; paths: " .
217  FormatJson::encode( $relevantPaths ) );
218  $status->merge( $backend->doOperations( $realOps, $opts ) );
219  }
220  }
221  }
222  // Make 'success', 'successCount', and 'failCount' fields reflect
223  // the overall operation, rather than all the batches for each backend.
224  // Do this by only using success values from the master backend's batch.
225  $status->success = $masterStatus->success;
226  $status->successCount = $masterStatus->successCount;
227  $status->failCount = $masterStatus->failCount;
228 
229  return $status;
230  }
231 
238  public function consistencyCheck( array $paths ) {
240  if ( $this->syncChecks == 0 || count( $this->backends ) <= 1 ) {
241  return $status; // skip checks
242  }
243 
244  // Preload all of the stat info in as few round trips as possible...
245  foreach ( $this->backends as $backend ) {
246  $realPaths = $this->substPaths( $paths, $backend );
247  $backend->preloadFileStat( [ 'srcs' => $realPaths, 'latest' => true ] );
248  }
249 
250  $mBackend = $this->backends[$this->masterIndex];
251  foreach ( $paths as $path ) {
252  $params = [ 'src' => $path, 'latest' => true ];
253  $mParams = $this->substOpPaths( $params, $mBackend );
254  // Stat the file on the 'master' backend
255  $mStat = $mBackend->getFileStat( $mParams );
256  if ( $this->syncChecks & self::CHECK_SHA1 ) {
257  $mSha1 = $mBackend->getFileSha1Base36( $mParams );
258  } else {
259  $mSha1 = false;
260  }
261  // Check if all clone backends agree with the master...
262  foreach ( $this->backends as $index => $cBackend ) {
263  if ( $index === $this->masterIndex ) {
264  continue; // master
265  }
266  $cParams = $this->substOpPaths( $params, $cBackend );
267  $cStat = $cBackend->getFileStat( $cParams );
268  if ( $mStat ) { // file is in master
269  if ( !$cStat ) { // file should exist
270  $status->fatal( 'backend-fail-synced', $path );
271  continue;
272  }
273  if ( $this->syncChecks & self::CHECK_SIZE ) {
274  if ( $cStat['size'] != $mStat['size'] ) { // wrong size
275  $status->fatal( 'backend-fail-synced', $path );
276  continue;
277  }
278  }
279  if ( $this->syncChecks & self::CHECK_TIME ) {
280  $mTs = wfTimestamp( TS_UNIX, $mStat['mtime'] );
281  $cTs = wfTimestamp( TS_UNIX, $cStat['mtime'] );
282  if ( abs( $mTs - $cTs ) > 30 ) { // outdated file somewhere
283  $status->fatal( 'backend-fail-synced', $path );
284  continue;
285  }
286  }
287  if ( $this->syncChecks & self::CHECK_SHA1 ) {
288  if ( $cBackend->getFileSha1Base36( $cParams ) !== $mSha1 ) { // wrong SHA1
289  $status->fatal( 'backend-fail-synced', $path );
290  continue;
291  }
292  }
293  } else { // file is not in master
294  if ( $cStat ) { // file should not exist
295  $status->fatal( 'backend-fail-synced', $path );
296  }
297  }
298  }
299  }
300 
301  return $status;
302  }
303 
310  public function accessibilityCheck( array $paths ) {
312  if ( count( $this->backends ) <= 1 ) {
313  return $status; // skip checks
314  }
315 
316  foreach ( $paths as $path ) {
317  foreach ( $this->backends as $backend ) {
318  $realPath = $this->substPaths( $path, $backend );
319  if ( !$backend->isPathUsableInternal( $realPath ) ) {
320  $status->fatal( 'backend-fail-usable', $path );
321  }
322  }
323  }
324 
325  return $status;
326  }
327 
336  public function resyncFiles( array $paths, $resyncMode = true ) {
338 
339  $mBackend = $this->backends[$this->masterIndex];
340  foreach ( $paths as $path ) {
341  $mPath = $this->substPaths( $path, $mBackend );
342  $mSha1 = $mBackend->getFileSha1Base36( [ 'src' => $mPath, 'latest' => true ] );
343  $mStat = $mBackend->getFileStat( [ 'src' => $mPath, 'latest' => true ] );
344  if ( $mStat === null || ( $mSha1 !== false && !$mStat ) ) { // sanity
345  $status->fatal( 'backend-fail-internal', $this->name );
346  wfDebugLog( 'FileOperation', __METHOD__
347  . ': File is not available on the master backend' );
348  continue; // file is not available on the master backend...
349  }
350  // Check of all clone backends agree with the master...
351  foreach ( $this->backends as $index => $cBackend ) {
352  if ( $index === $this->masterIndex ) {
353  continue; // master
354  }
355  $cPath = $this->substPaths( $path, $cBackend );
356  $cSha1 = $cBackend->getFileSha1Base36( [ 'src' => $cPath, 'latest' => true ] );
357  $cStat = $cBackend->getFileStat( [ 'src' => $cPath, 'latest' => true ] );
358  if ( $cStat === null || ( $cSha1 !== false && !$cStat ) ) { // sanity
359  $status->fatal( 'backend-fail-internal', $cBackend->getName() );
360  wfDebugLog( 'FileOperation', __METHOD__ .
361  ': File is not available on the clone backend' );
362  continue; // file is not available on the clone backend...
363  }
364  if ( $mSha1 === $cSha1 ) {
365  // already synced; nothing to do
366  } elseif ( $mSha1 !== false ) { // file is in master
367  if ( $resyncMode === 'conservative'
368  && $cStat && $cStat['mtime'] > $mStat['mtime']
369  ) {
370  $status->fatal( 'backend-fail-synced', $path );
371  continue; // don't rollback data
372  }
373  $fsFile = $mBackend->getLocalReference(
374  [ 'src' => $mPath, 'latest' => true ] );
375  $status->merge( $cBackend->quickStore(
376  [ 'src' => $fsFile->getPath(), 'dst' => $cPath ]
377  ) );
378  } elseif ( $mStat === false ) { // file is not in master
379  if ( $resyncMode === 'conservative' ) {
380  $status->fatal( 'backend-fail-synced', $path );
381  continue; // don't delete data
382  }
383  $status->merge( $cBackend->quickDelete( [ 'src' => $cPath ] ) );
384  }
385  }
386  }
387 
388  if ( !$status->isOK() ) {
389  wfDebugLog( 'FileOperation', get_class( $this ) .
390  " failed to resync: " . FormatJson::encode( $paths ) );
391  }
392 
393  return $status;
394  }
395 
402  protected function fileStoragePathsForOps( array $ops ) {
403  $paths = [];
404  foreach ( $ops as $op ) {
405  if ( isset( $op['src'] ) ) {
406  // For things like copy/move/delete with "ignoreMissingSource" and there
407  // is no source file, nothing should happen and there should be no errors.
408  if ( empty( $op['ignoreMissingSource'] )
409  || $this->fileExists( [ 'src' => $op['src'] ] )
410  ) {
411  $paths[] = $op['src'];
412  }
413  }
414  if ( isset( $op['srcs'] ) ) {
415  $paths = array_merge( $paths, $op['srcs'] );
416  }
417  if ( isset( $op['dst'] ) ) {
418  $paths[] = $op['dst'];
419  }
420  }
421 
422  return array_values( array_unique( array_filter( $paths, 'FileBackend::isStoragePath' ) ) );
423  }
424 
433  protected function substOpBatchPaths( array $ops, FileBackendStore $backend ) {
434  $newOps = []; // operations
435  foreach ( $ops as $op ) {
436  $newOp = $op; // operation
437  foreach ( [ 'src', 'srcs', 'dst', 'dir' ] as $par ) {
438  if ( isset( $newOp[$par] ) ) { // string or array
439  $newOp[$par] = $this->substPaths( $newOp[$par], $backend );
440  }
441  }
442  $newOps[] = $newOp;
443  }
444 
445  return $newOps;
446  }
447 
455  protected function substOpPaths( array $ops, FileBackendStore $backend ) {
456  $newOps = $this->substOpBatchPaths( [ $ops ], $backend );
457 
458  return $newOps[0];
459  }
460 
468  protected function substPaths( $paths, FileBackendStore $backend ) {
469  return preg_replace(
470  '!^mwstore://' . preg_quote( $this->name, '!' ) . '/!',
471  StringUtils::escapeRegexReplacement( "mwstore://{$backend->getName()}/" ),
472  $paths // string or array
473  );
474  }
475 
482  protected function unsubstPaths( $paths ) {
483  return preg_replace(
484  '!^mwstore://([^/]+)!',
485  StringUtils::escapeRegexReplacement( "mwstore://{$this->name}" ),
486  $paths // string or array
487  );
488  }
489 
494  protected function hasVolatileSources( array $ops ) {
495  foreach ( $ops as $op ) {
496  if ( $op['op'] === 'store' && !isset( $op['srcRef'] ) ) {
497  return true; // source file might be deleted anytime after do*Operations()
498  }
499  }
500 
501  return false;
502  }
503 
504  protected function doQuickOperationsInternal( array $ops ) {
506  // Do the operations on the master backend; setting Status fields...
507  $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
508  $masterStatus = $this->backends[$this->masterIndex]->doQuickOperations( $realOps );
509  $status->merge( $masterStatus );
510  // Propagate the operations to the clone backends...
511  foreach ( $this->backends as $index => $backend ) {
512  if ( $index === $this->masterIndex ) {
513  continue; // done already
514  }
515 
516  $realOps = $this->substOpBatchPaths( $ops, $backend );
517  if ( $this->asyncWrites && !$this->hasVolatileSources( $ops ) ) {
519  function() use ( $backend, $realOps ) {
520  $backend->doQuickOperations( $realOps );
521  }
522  );
523  } else {
524  $status->merge( $backend->doQuickOperations( $realOps ) );
525  }
526  }
527  // Make 'success', 'successCount', and 'failCount' fields reflect
528  // the overall operation, rather than all the batches for each backend.
529  // Do this by only using success values from the master backend's batch.
530  $status->success = $masterStatus->success;
531  $status->successCount = $masterStatus->successCount;
532  $status->failCount = $masterStatus->failCount;
533 
534  return $status;
535  }
536 
537  protected function doPrepare( array $params ) {
538  return $this->doDirectoryOp( 'prepare', $params );
539  }
540 
541  protected function doSecure( array $params ) {
542  return $this->doDirectoryOp( 'secure', $params );
543  }
544 
545  protected function doPublish( array $params ) {
546  return $this->doDirectoryOp( 'publish', $params );
547  }
548 
549  protected function doClean( array $params ) {
550  return $this->doDirectoryOp( 'clean', $params );
551  }
552 
558  protected function doDirectoryOp( $method, array $params ) {
560 
561  $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
562  $masterStatus = $this->backends[$this->masterIndex]->$method( $realParams );
563  $status->merge( $masterStatus );
564 
565  foreach ( $this->backends as $index => $backend ) {
566  if ( $index === $this->masterIndex ) {
567  continue; // already done
568  }
569 
570  $realParams = $this->substOpPaths( $params, $backend );
571  if ( $this->asyncWrites ) {
573  function() use ( $backend, $method, $realParams ) {
574  $backend->$method( $realParams );
575  }
576  );
577  } else {
578  $status->merge( $backend->$method( $realParams ) );
579  }
580  }
581 
582  return $status;
583  }
584 
585  public function concatenate( array $params ) {
586  // We are writing to an FS file, so we don't need to do this per-backend
587  $index = $this->getReadIndexFromParams( $params );
588  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
589 
590  return $this->backends[$index]->concatenate( $realParams );
591  }
592 
593  public function fileExists( array $params ) {
594  $index = $this->getReadIndexFromParams( $params );
595  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
596 
597  return $this->backends[$index]->fileExists( $realParams );
598  }
599 
600  public function getFileTimestamp( array $params ) {
601  $index = $this->getReadIndexFromParams( $params );
602  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
603 
604  return $this->backends[$index]->getFileTimestamp( $realParams );
605  }
606 
607  public function getFileSize( array $params ) {
608  $index = $this->getReadIndexFromParams( $params );
609  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
610 
611  return $this->backends[$index]->getFileSize( $realParams );
612  }
613 
614  public function getFileStat( array $params ) {
615  $index = $this->getReadIndexFromParams( $params );
616  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
617 
618  return $this->backends[$index]->getFileStat( $realParams );
619  }
620 
621  public function getFileXAttributes( array $params ) {
622  $index = $this->getReadIndexFromParams( $params );
623  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
624 
625  return $this->backends[$index]->getFileXAttributes( $realParams );
626  }
627 
628  public function getFileContentsMulti( array $params ) {
629  $index = $this->getReadIndexFromParams( $params );
630  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
631 
632  $contentsM = $this->backends[$index]->getFileContentsMulti( $realParams );
633 
634  $contents = []; // (path => FSFile) mapping using the proxy backend's name
635  foreach ( $contentsM as $path => $data ) {
636  $contents[$this->unsubstPaths( $path )] = $data;
637  }
638 
639  return $contents;
640  }
641 
642  public function getFileSha1Base36( array $params ) {
643  $index = $this->getReadIndexFromParams( $params );
644  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
645 
646  return $this->backends[$index]->getFileSha1Base36( $realParams );
647  }
648 
649  public function getFileProps( array $params ) {
650  $index = $this->getReadIndexFromParams( $params );
651  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
652 
653  return $this->backends[$index]->getFileProps( $realParams );
654  }
655 
656  public function streamFile( array $params ) {
657  $index = $this->getReadIndexFromParams( $params );
658  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
659 
660  return $this->backends[$index]->streamFile( $realParams );
661  }
662 
663  public function getLocalReferenceMulti( array $params ) {
664  $index = $this->getReadIndexFromParams( $params );
665  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
666 
667  $fsFilesM = $this->backends[$index]->getLocalReferenceMulti( $realParams );
668 
669  $fsFiles = []; // (path => FSFile) mapping using the proxy backend's name
670  foreach ( $fsFilesM as $path => $fsFile ) {
671  $fsFiles[$this->unsubstPaths( $path )] = $fsFile;
672  }
673 
674  return $fsFiles;
675  }
676 
677  public function getLocalCopyMulti( array $params ) {
678  $index = $this->getReadIndexFromParams( $params );
679  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
680 
681  $tempFilesM = $this->backends[$index]->getLocalCopyMulti( $realParams );
682 
683  $tempFiles = []; // (path => TempFSFile) mapping using the proxy backend's name
684  foreach ( $tempFilesM as $path => $tempFile ) {
685  $tempFiles[$this->unsubstPaths( $path )] = $tempFile;
686  }
687 
688  return $tempFiles;
689  }
690 
691  public function getFileHttpUrl( array $params ) {
692  $index = $this->getReadIndexFromParams( $params );
693  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
694 
695  return $this->backends[$index]->getFileHttpUrl( $realParams );
696  }
697 
698  public function directoryExists( array $params ) {
699  $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
700 
701  return $this->backends[$this->masterIndex]->directoryExists( $realParams );
702  }
703 
704  public function getDirectoryList( array $params ) {
705  $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
706 
707  return $this->backends[$this->masterIndex]->getDirectoryList( $realParams );
708  }
709 
710  public function getFileList( array $params ) {
711  $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
712 
713  return $this->backends[$this->masterIndex]->getFileList( $realParams );
714  }
715 
716  public function getFeatures() {
717  return $this->backends[$this->masterIndex]->getFeatures();
718  }
719 
720  public function clearCache( array $paths = null ) {
721  foreach ( $this->backends as $backend ) {
722  $realPaths = is_array( $paths ) ? $this->substPaths( $paths, $backend ) : null;
723  $backend->clearCache( $realPaths );
724  }
725  }
726 
727  public function preloadCache( array $paths ) {
728  $realPaths = $this->substPaths( $paths, $this->backends[$this->readIndex] );
729  $this->backends[$this->readIndex]->preloadCache( $realPaths );
730  }
731 
732  public function preloadFileStat( array $params ) {
733  $index = $this->getReadIndexFromParams( $params );
734  $realParams = $this->substOpPaths( $params, $this->backends[$index] );
735 
736  return $this->backends[$index]->preloadFileStat( $realParams );
737  }
738 
739  public function getScopedLocksForOps( array $ops, Status $status ) {
740  $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
741  $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $realOps );
742  // Get the paths to lock from the master backend
743  $paths = $this->backends[$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );
744  // Get the paths under the proxy backend's name
745  $pbPaths = [
748  ];
749 
750  // Actually acquire the locks
751  return $this->getScopedFileLocks( $pbPaths, 'mixed', $status );
752  }
753 
758  protected function getReadIndexFromParams( array $params ) {
759  return !empty( $params['latest'] ) ? $this->masterIndex : $this->readIndex;
760  }
761 }
FileJournal $fileJournal
the array() calling protocol came about after MediaWiki 1.4rc1.
FileBackendStore[] $backends
Prioritized list of FileBackendStore objects.
substOpPaths(array $ops, FileBackendStore $backend)
Same as substOpBatchPaths() but for a single operation.
substOpBatchPaths(array $ops, FileBackendStore $backend)
Substitute the backend name in storage path parameters for a set of operations with that of a given i...
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
getScopedLocksForOps(array $ops, Status $status)
accessibilityCheck(array $paths)
Check that a set of file paths are usable across all internal backends.
clearCache(array $paths=null)
static escapeRegexReplacement($string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter...
fileStoragePathsForOps(array $ops)
Get a list of file storage paths to read or write for a list of operations.
static addCallableUpdate($callable, $type=self::POSTSEND)
Add a callable update.
wfTimestamp($outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
consistencyCheck(array $paths)
Check that a set of files are consistent across all internal backends.
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1798
wfDebugLog($logGroup, $text, $dest= 'all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not...
const LOCK_UW
Definition: LockManager.php:61
File backend exception for checked exceptions (e.g.
const LOCK_EX
Definition: LockManager.php:62
substPaths($paths, FileBackendStore $backend)
Substitute the backend of storage paths with an internal backend's name.
int $readIndex
Index of read affinity backend.
getLocalReferenceMulti(array $params)
static encode($value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:127
$params
doOperationsInternal(array $ops, array $opts)
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
getReadIndexFromParams(array $params)
string $name
Unique backend name.
Definition: FileBackend.php:87
Base class for all backends using particular storage medium.
resyncFiles(array $paths, $resyncMode=true)
Check that a set of files are consistent across all internal backends and re-synchronize those files ...
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at name
Definition: design.txt:12
Proxy backend that mirrors writes to several internal backends.
Base class for all file backend classes (including multi-write backends).
Definition: FileBackend.php:85
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:1004
__construct(array $config)
Construct a proxy backend that consists of several internal backends.
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
string $wikiId
Unique wiki name.
Definition: FileBackend.php:90
int $masterIndex
Index of master backend.
doDirectoryOp($method, array $params)
unsubstPaths($paths)
Substitute the backend of internal storage paths with the proxy backend's name.
static newGood($value=null)
Factory function for good results.
Definition: Status.php:101
getScopedFileLocks(array $paths, $type, Status $status, $timeout=0)
Lock the files at the given storage paths in the backend.