* Added FileBackend process cache for fileExists(), getFileTimestamp(), and getLocalReference().
* Refactored getFileSha1Base36() into parent class and subclass functions.
* Removed some FileBackendMultiWrite comment duplication.
}
/**
- * @see FileBackend::fileExists()
+ * @see FileBackend::doFileExists()
*/
- public function fileExists( array $params ) {
+ protected function doFileExists( array $params ) {
list( $c, $source ) = $this->resolveStoragePathReal( $params['src'] );
if ( $source === null ) {
return false; // invalid storage path
}
/**
- * @see FileBackend::getFileTimestamp()
+ * @see FileBackend::doGetFileTimestamp()
*/
- public function getFileTimestamp( array $params ) {
+ public function doGetFileTimestamp( array $params ) {
list( $c, $source ) = $this->resolveStoragePathReal( $params['src'] );
if ( $source === null ) {
return false; // invalid storage path
* This should only be called from within FileBackendGroup.
*
* $config includes:
- * 'name' : The unique name of this backend
- * 'wikiId' : Prefix to container names that is unique to this wiki
- * 'lockManager' : Registered name of a file lock manager to use
+ * 'name' : The unique name of this backend.
+ * 'wikiId' : Prefix to container names that is unique to this wiki.
+ * 'lockManager' : Registered name of a file lock manager to use.
* 'readOnly' : Write operations are disallowed if this is a non-empty string.
* It should be an explanation for the backend being read-only.
*
return Status::newGood();
}
+ /**
+ * @see FileBackendBase::fileExists()
+ */
+ final public function fileExists( array $params ) {
+ $path = $params['src'];
+ if ( isset( $this->cache[$path]['exists'] ) ) {
+ return $this->cache[$path]['exists'];
+ }
+ $exists = $this->doFileExists( $params );
+ if ( $exists ) { // don't cache negatives
+ $this->trimCache(); // limit memory
+ $this->cache[$path]['exists'] = $exists;
+ }
+ return $exists;
+ }
+
+ /**
+ * @see FileBackend::fileExists()
+ */
+ abstract protected function doFileExists( array $params );
+
+ /**
+ * @see FileBackendBase::getFileTimestamp()
+ */
+ final public function getFileTimestamp( array $params ) {
+ $path = $params['src'];
+ if ( isset( $this->cache[$path]['timestamp'] ) ) {
+ return $this->cache[$path]['timestamp'];
+ }
+ $timestamp = $this->doGetFileTimestamp( $params );
+ if ( $timestamp ) { // don't cache negatives
+ $this->trimCache(); // limit memory
+ $this->cache[$path]['timestamp'] = $timestamp;
+ }
+ return $timestamp;
+ }
+
+ /**
+ * @see FileBackend::getFileTimestamp()
+ */
+ abstract protected function doGetFileTimestamp( array $params );
+
/**
* @see FileBackendBase::getFileContents()
*/
if ( isset( $this->cache[$path]['sha1'] ) ) {
return $this->cache[$path]['sha1'];
}
+ $hash = $this->doGetFileSha1Base36( $params );
+ if ( $hash ) { // don't cache negatives
+ $this->trimCache(); // limit memory
+ $this->cache[$path]['sha1'] = $hash;
+ }
+ return $hash;
+ }
+
+ /**
+ * @see FileBackend::getFileSha1Base36()
+ */
+ protected function doGetFileSha1Base36( array $params ) {
$fsFile = $this->getLocalReference( $params );
if ( !$fsFile ) {
return false;
} else {
- $sha1 = $fsFile->getSha1Base36();
- if ( $sha1 !== false ) { // don't cache negatives
- $this->trimCache(); // limit memory
- $this->cache[$path]['sha1'] = $sha1;
- }
- return $sha1;
+ return $fsFile->getSha1Base36();
}
}
* @see FileBackendBase::getLocalReference()
*/
public function getLocalReference( array $params ) {
- return $this->getLocalCopy( $params );
+ $path = $params['src'];
+ if ( isset( $this->cache[$path]['localRef'] ) ) {
+ return $this->cache[$path]['localRef'];
+ }
+ $tmpFile = $this->getLocalCopy( $params );
+ if ( $tmpFile ) { // don't cache negatives
+ $this->trimCache(); // limit memory
+ $this->cache[$path]['localRef'] = $tmpFile;
+ }
+ return $tmpFile;
}
/**
/**
* Construct a proxy backend that consists of several internal backends.
- * $config contains:
- * 'name' : The name of the proxy backend
- * 'lockManager' : Registered name of the file lock manager to use
+ * Additional $config params include:
* 'backends' : Array of backend config and multi-backend settings.
* Each value is the config used in the constructor of a
* FileBackend class, but with these additional settings:
protected $state = self::STATE_NEW; // integer
protected $failed = false; // boolean
protected $useBackups = true; // boolean
+ protected $useLatest = true; // boolean
protected $destSameAsSource = false; // boolean
protected $destAlreadyExists = false; // boolean
$this->useBackups = false;
}
+ /**
+ * Allow stale data for file reads and existence checks.
+ * If this is called, then disableBackups() should also be called
+ * unless the affected files are known to have not changed recently.
+ *
+ * @return void
+ */
+ final protected function allowStaleReads() {
+ $this->useLatest = false;
+ }
+
/**
* Attempt a series of file operations.
* Callers are responsible for handling file locking.
final public static function attemptBatch( array $performOps, array $opts ) {
$status = Status::newGood();
+ $allowStale = isset( $opts['allowStale'] ) && $opts['allowStale'];
$ignoreErrors = isset( $opts['ignoreErrors'] ) && $opts['ignoreErrors'];
$predicates = FileOp::newPredicates(); // account for previous op in prechecks
// Do pre-checks for each operation; abort on failure...
foreach ( $performOps as $index => $fileOp ) {
+ if ( $allowStale ) {
+ $fileOp->allowStaleReads(); // allow potentially stale reads
+ }
$status->merge( $fileOp->precheck( $predicates ) );
if ( !$status->isOK() ) { // operation failed?
if ( $ignoreErrors ) {
$status = Status::newGood();
if ( $this->useBackups ) {
// Check if a file already exists at the source...
- $params = array( 'src' => $this->params['src'] );
+ $params = array( 'src' => $this->params['src'], 'latest' => $this->useLatest );
if ( $this->backend->fileExists( $params ) ) {
// Create a temporary backup copy...
$this->tmpSourcePath = $this->backend->getLocalCopy( $params );
if ( $this->getParam( 'overwriteDest' ) ) {
if ( $this->useBackups ) {
// Create a temporary backup copy...
- $params = array( 'src' => $this->params['dst'] );
+ $params = array( 'src' => $this->params['dst'], 'latest' => $this->useLatest );
$this->tmpDestFile = $this->backend->getLocalCopy( $params );
if ( !$this->tmpDestFile ) {
$status->fatal( 'backend-fail-backup', $this->params['dst'] );
if ( FileBackend::isStoragePath( $path ) ) {
// For some backends (e.g. Swift, Azure) we can get
// standard hashes to use for this types of comparisons.
- $hash = $this->backend->getFileSha1Base36( array( 'src' => $path ) );
+ $params = array( 'src' => $path, 'latest' => $this->useLatest );
+ $hash = $this->backend->getFileSha1Base36( $params );
// Source file is on file system
} else {
wfSuppressWarnings();
if ( isset( $predicates['exists'][$source] ) ) {
return $predicates['exists'][$source]; // previous op assures this
} else {
- return $this->backend->fileExists( array( 'src' => $source ) );
+ $params = array( 'src' => $source, 'latest' => $this->useLatest );
+ return $this->backend->fileExists( $params );
}
}