*/
public function __construct( array $config ) {
parent::__construct( $config );
- $this->memCache = new EmptyBagOStuff(); // disabled by default
- $this->cheapCache = new ProcessCacheLRU( 300 );
+ $this->memCache = new EmptyBagOStuff(); // disabled by default
+ $this->cheapCache = new ProcessCacheLRU( 300 );
$this->expensiveCache = new ProcessCacheLRU( 5 );
}
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
+ * - dstExists : Whether a file exists at the destination (optimization).
+ * Callers can use "false" if no existing file is being changed.
*
* @param $params Array
* @return Status
} else {
$status = $this->doCreateInternal( $params );
$this->clearCache( array( $params['dst'] ) );
- $this->deleteFileCache( $params['dst'] ); // persistent cache
+ if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ }
}
wfProfileOut( __METHOD__ . '-' . $this->name );
wfProfileOut( __METHOD__ );
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
+ * - dstExists : Whether a file exists at the destination (optimization).
+ * Callers can use "false" if no existing file is being changed.
*
* @param $params Array
* @return Status
} else {
$status = $this->doStoreInternal( $params );
$this->clearCache( array( $params['dst'] ) );
- $this->deleteFileCache( $params['dst'] ); // persistent cache
+ if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ }
}
wfProfileOut( __METHOD__ . '-' . $this->name );
wfProfileOut( __METHOD__ );
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
+ * - dstExists : Whether a file exists at the destination (optimization).
+ * Callers can use "false" if no existing file is being changed.
*
* @param $params Array
* @return Status
wfProfileIn( __METHOD__ . '-' . $this->name );
$status = $this->doCopyInternal( $params );
$this->clearCache( array( $params['dst'] ) );
- $this->deleteFileCache( $params['dst'] ); // persistent cache
+ if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ }
wfProfileOut( __METHOD__ . '-' . $this->name );
wfProfileOut( __METHOD__ );
return $status;
* - async : Status will be returned immediately if supported.
* If the status is OK, then its value field will be
* set to a FileBackendStoreOpHandle object.
+ * - dstExists : Whether a file exists at the destination (optimization).
+ * Callers can use "false" if no existing file is being changed.
*
* @param $params Array
* @return Status
$status = $this->doMoveInternal( $params );
$this->clearCache( array( $params['src'], $params['dst'] ) );
$this->deleteFileCache( $params['src'] ); // persistent cache
- $this->deleteFileCache( $params['dst'] ); // persistent cache
+ if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
+ $this->deleteFileCache( $params['dst'] ); // persistent cache
+ }
wfProfileOut( __METHOD__ . '-' . $this->name );
wfProfileOut( __METHOD__ );
return $status;
*/
protected function doConcatenate( array $params ) {
$status = Status::newGood();
-
$tmpPath = $params['dst']; // convenience
unset( $params['latest'] ); // sanity
- $callback = isset( $params['callback'] )
- ? $params['callback']
- : function( Status $status, $segment ) {};
// Check that the specified temp file is valid...
wfSuppressWarnings();
wfRestoreWarnings();
if ( !$ok ) { // not present or not empty
$status->fatal( 'backend-fail-opentemp', $tmpPath );
- $callback( $status, null ); // update progress
return $status;
}
$fsFile = $this->getLocalReference( array( 'src' => $path ) );
if ( !$fsFile ) { // retry failed?
$status->fatal( 'backend-fail-read', $path );
- $callback( $status, null ); // update progress
return $status;
}
}
$tmpHandle = fopen( $tmpPath, 'ab' );
if ( $tmpHandle === false ) {
$status->fatal( 'backend-fail-opentemp', $tmpPath );
- $callback( $status, null ); // update progress
return $status;
}
- $segment = 0; // segment number
// Build up the temp file using the source chunks (in order)...
foreach ( $fsFiles as $virtualSource => $fsFile ) {
- ++$segment; // first segment is "1"
// Get a handle to the local FS version
$sourceHandle = fopen( $fsFile->getPath(), 'rb' );
if ( $sourceHandle === false ) {
fclose( $tmpHandle );
$status->fatal( 'backend-fail-read', $virtualSource );
- $callback( $status, null ); // update progress
return $status;
}
// Append chunk to file (pass chunk size to avoid magic quotes)
fclose( $sourceHandle );
fclose( $tmpHandle );
$status->fatal( 'backend-fail-writetemp', $tmpPath );
- $callback( $status , null );
return $status;
}
fclose( $sourceHandle );
- $callback( $status, $segment ); // update progress (chunk success)
}
if ( !fclose( $tmpHandle ) ) {
$status->fatal( 'backend-fail-closetemp', $tmpPath );
- $callback( $status, null ); // update progress
return $status;
}
clearstatcache(); // temp file changed
- $callback( $status, null ); // update progress (full success)
return $status;
}
if ( $this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
$stat = $this->cheapCache->get( $path, 'stat' );
// If we want the latest data, check that this cached
- // value was in fact fetched with the latest available data
- // (the process cache is ignored if it contains a negative).
- if ( !$latest || ( is_array( $stat ) && $stat['latest'] ) ) {
- wfProfileOut( __METHOD__ . '-' . $this->name );
- wfProfileOut( __METHOD__ );
- return $stat;
+ // value was in fact fetched with the latest available data.
+ if ( is_array( $stat ) ) {
+ if ( !$latest || $stat['latest'] ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $stat;
+ }
+ } elseif ( in_array( $stat, array( 'NOT_EXIST', 'NOT_EXIST_LATEST' ) ) ) {
+ if ( !$latest || $stat === 'NOT_EXIST_LATEST' ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
}
}
wfProfileIn( __METHOD__ . '-miss' );
array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
}
} elseif ( $stat === false ) { // file does not exist
- $this->cheapCache->set( $path, 'stat', false );
+ $this->cheapCache->set( $path, 'stat', $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
wfDebug( __METHOD__ . ": File $path does not exist.\n" );
} else { // an error occurred
wfDebug( __METHOD__ . ": Could not stat file $path.\n" );
$hash = $this->doGetFileSha1Base36( $params );
wfProfileOut( __METHOD__ . '-miss-' . $this->name );
wfProfileOut( __METHOD__ . '-miss' );
- if ( $hash ) { // don't cache negatives
- $this->cheapCache->set( $path, 'sha1',
- array( 'hash' => $hash, 'latest' => $latest ) );
- }
+ $this->cheapCache->set( $path, 'sha1', array( 'hash' => $hash, 'latest' => $latest ) );
wfProfileOut( __METHOD__ . '-' . $this->name );
wfProfileOut( __METHOD__ );
return $hash;
/**
* Delete the cached stat info for a file path.
* The cache key is salted for a while to prevent race conditions.
+ * Since negatives (404s) are not cached, this does not need to be called when
+ * a file is created at a path were there was none before.
*
* @param $path string Storage path
*/