* @param array $lbConf Config for LBFactory::__construct()
* @param ServiceOptions $options
* @param ConfiguredReadOnlyMode $readOnlyMode
- * @param BagOStuff $srvCace
+ * @param BagOStuff $srvCache
* @param BagOStuff $mainStash
* @param WANObjectCache $wanCache
* @return array
array $lbConf,
ServiceOptions $options,
ConfiguredReadOnlyMode $readOnlyMode,
- BagOStuff $srvCace,
+ BagOStuff $srvCache,
BagOStuff $mainStash,
WANObjectCache $wanCache
) {
$options->get( 'DBprefix' )
);
- $lbConf = self::injectObjectCaches( $lbConf, $srvCace, $mainStash, $wanCache );
+ $lbConf = self::injectObjectCaches( $lbConf, $srvCache, $mainStash, $wanCache );
return $lbConf;
}
private static function injectObjectCaches(
array $lbConf, BagOStuff $sCache, BagOStuff $mStash, WANObjectCache $wCache
) {
+ // Fallback if APC style caching is not an option
+ if ( $sCache instanceof EmptyBagOStuff ) {
+ $sCache = new HashBagOStuff( [ 'maxKeys' => 100 ] );
+ }
+
// Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
if ( $sCache->getQoS( $sCache::ATTR_EMULATION ) > $sCache::QOS_EMULATION_SQL ) {
$lbConf['srvCache'] = $sCache;
} elseif ( !$hadError ) {
return false; // file does not exist
} else {
- return null; // failure
+ return self::UNKNOWN; // failure
}
}
$exists = is_dir( $dir );
$hadError = $this->untrapWarnings();
- return $hadError ? null : $exists;
+ return $hadError ? self::UNKNOWN : $exists;
}
/**
} elseif ( !is_readable( $dir ) ) {
$this->logger->warning( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
- return null; // bad permissions?
+ return self::UNKNOWN; // bad permissions?
}
return new FSFileBackendDirList( $dir, $params );
} elseif ( !is_readable( $dir ) ) {
$this->logger->warning( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
- return null; // bad permissions?
+ return self::UNKNOWN; // bad permissions?
}
return new FSFileBackendFileList( $dir, $params );
const ATTR_METADATA = 2; // files can be stored with metadata key/values
const ATTR_UNICODE_PATHS = 4; // files can have Unicode paths (not just ASCII)
+ /** @var null Idiom for "could not determine due to I/O errors" */
+ const UNKNOWN = null;
+
/**
* Create a new backend instance from configuration.
* This should only be called from within FileBackendGroup.
}
/**
- * Get the unique backend name.
+ * Get the unique backend name
+ *
* We may have multiple different backends of the same type.
* For example, we can have two Swift backends using different proxies.
*
/**
* Alias to getDomainId()
+ *
* @return string
* @since 1.20
* @deprecated Since 1.34 Use getDomainId()
abstract public function getFileHttpUrl( array $params );
/**
- * Check if a directory exists at a given storage path.
- * Backends using key/value stores will check if the path is a
- * virtual directory, meaning there are files under the given directory.
+ * Check if a directory exists at a given storage path
+ *
+ * For backends using key/value stores, a directory is said to exist whenever
+ * there exist any files with paths using the given directory path as a prefix
+ * followed by a forward slash. For example, if there is a file called
+ * "mwstore://backend/container/dir/path.svg" then directories are said to exist
+ * at "mwstore://backend/container" and "mwstore://backend/container/dir". These
+ * can be thought of as "virtual" directories.
+ *
+ * Backends that directly use a filesystem layer might enumerate empty directories.
+ * The clean() method should always be used when files are deleted or moved if this
+ * is a concern. This is a trade-off to avoid write amplication/contention on file
+ * changes or read amplification when calling this method.
*
* Storage backends with eventual consistency might return stale data.
*
+ * @see FileBackend::clean()
+ *
* @param array $params Parameters include:
* - dir : storage directory
- * @return bool|null Returns null on failure
+ * @return bool|null Whether a directory exists or null on failure
* @since 1.20
*/
abstract public function directoryExists( array $params );
/**
- * Get an iterator to list *all* directories under a storage directory.
+ * Get an iterator to list *all* directories under a storage directory
+ *
* If the directory is of the form "mwstore://backend/container",
* then all directories in the container will be listed.
* If the directory is of form "mwstore://backend/container/dir",
*
* Failures during iteration can result in FileBackendError exceptions (since 1.22).
*
+ * @see FileBackend::directoryExists()
+ *
* @param array $params Parameters include:
* - dir : storage directory
* - topOnly : only return direct child dirs of the directory
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null Directory list enumerator null on failure
* @since 1.20
*/
abstract public function getDirectoryList( array $params );
*
* Failures during iteration can result in FileBackendError exceptions (since 1.22).
*
+ * @see FileBackend::directoryExists()
+ *
* @param array $params Parameters include:
* - dir : storage directory
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null Directory list enumerator or null on failure
* @since 1.20
*/
final public function getTopDirectoryList( array $params ) {
}
/**
- * Get an iterator to list *all* stored files under a storage directory.
- * If the directory is of the form "mwstore://backend/container",
- * then all files in the container will be listed.
- * If the directory is of form "mwstore://backend/container/dir",
- * then all files under that directory will be listed.
- * Results will be storage paths relative to the given directory.
+ * Get an iterator to list *all* stored files under a storage directory
+ *
+ * If the directory is of the form "mwstore://backend/container", then all
+ * files in the container will be listed. If the directory is of form
+ * "mwstore://backend/container/dir", then all files under that directory will
+ * be listed. Results will be storage paths relative to the given directory.
*
* Storage backends with eventual consistency might return stale data.
*
* - dir : storage directory
* - topOnly : only return direct child files of the directory (since 1.20)
* - adviseStat : set to true if stat requests will be made on the files (since 1.22)
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null File list enumerator or null on failure
*/
abstract public function getFileList( array $params );
* @param array $params Parameters include:
* - dir : storage directory
* - adviseStat : set to true if stat requests will be made on the files (since 1.22)
- * @return Traversable|array|null Returns null on failure
+ * @return Traversable|array|null File list enumerator or null on failure
* @since 1.20
*/
final public function getTopFileList( array $params ) {
* @param array $params Parameters include:
* - srcs : list of source storage paths
* - latest : use the latest available data
- * @return bool All requests proceeded without I/O errors (since 1.24)
+ * @return bool Whether all requests proceeded without I/O errors (since 1.24)
* @since 1.23
*/
abstract public function preloadFileStat( array $params );
*
* @param string $type One of (attachment, inline)
* @param string $filename Suggested file name (should not contain slashes)
- * @throws FileBackendError
+ * @throws InvalidArgumentException
* @return string
* @since 1.20
*/
$ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
$stat = $this->getFileStat( $params );
- return ( $stat === null ) ? null : (bool)$stat; // null => failure
+ return ( $stat === self::UNKNOWN ) ? self::UNKNOWN : (bool)$stat;
}
final public function getFileTimestamp( array $params ) {
// cache entries from mass object listings that do not include the SHA-1. In that
// case, loading the persistent stat cache will likely yield the SHA-1.
if (
- $stat === null ||
+ $stat === self::UNKNOWN ||
( $requireSHA1 && is_array( $stat ) && !isset( $stat['sha1'] ) )
) {
$this->primeFileCache( [ $path ] ); // check persistent cache
$res = true;
break; // found one!
} elseif ( $exists === null ) { // error?
- $res = null; // if we don't find anything, it is indeterminate
+ $res = self::UNKNOWN; // if we don't find anything, it is indeterminate
}
}
final public function getDirectoryList( array $params ) {
list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
if ( $dir === null ) { // invalid storage path
- return null;
+ return self::UNKNOWN;
}
if ( $shard !== null ) {
// File listing is confined to a single container/shard
final public function getFileList( array $params ) {
list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
if ( $dir === null ) { // invalid storage path
- return null;
+ return self::UNKNOWN;
}
if ( $shard !== null ) {
// File listing is confined to a single container/shard
protected function doGetFileStat( array $params ) {
$src = $this->resolveHashKey( $params['src'] );
if ( $src === null ) {
- return null;
+ return false; // invalid path
}
if ( isset( $this->files[$src] ) ) {
$stat = $this->getContainerStat( $fullCont );
if ( is_array( $stat ) ) {
return $status; // already there
- } elseif ( $stat === null ) {
+ } elseif ( $stat === self::UNKNOWN ) {
$status->fatal( 'backend-fail-internal', $this->name );
$this->logger->error( __METHOD__ . ': cannot get container stat' );
return ( count( $status->value ) ) > 0;
}
- return null; // error
+ return self::UNKNOWN; // error
}
/**
if ( !$this->containerStatCache->hasField( $container, 'stat' ) ) {
$auth = $this->getAuthentication();
if ( !$auth ) {
- return null;
+ return self::UNKNOWN;
}
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run( [
$this->onError( null, __METHOD__,
[ 'cont' => $container ], $rerr, $rcode, $rdesc );
- return null;
+ return self::UNKNOWN;
}
}
$stats[$path] = false;
continue; // invalid storage path
} elseif ( !$auth ) {
- $stats[$path] = null;
+ $stats[$path] = self::UNKNOWN;
continue;
}
$stats[$path] = false;
continue; // ok, nothing to do
} elseif ( !is_array( $cstat ) ) {
- $stats[$path] = null;
+ $stats[$path] = self::UNKNOWN;
continue;
}
} elseif ( $rcode === 404 ) {
$stat = false;
} else {
- $stat = null;
+ $stat = self::UNKNOWN;
$this->onError( null, __METHOD__, $params, $rerr, $rcode, $rdesc );
}
$stats[$path] = $stat;
*/
public static function detectLocalServerCache() {
if ( function_exists( 'apcu_fetch' ) ) {
- return 'apcu';
+ // Make sure the APCu methods actually store anything
+ if ( PHP_SAPI !== 'cli' || ini_get( 'apc.enable_cli' ) ) {
+ return 'apcu';
+ }
} elseif ( function_exists( 'apc_fetch' ) ) {
- return 'apc';
+ // Make sure the APC methods actually store anything
+ if ( PHP_SAPI !== 'cli' || ini_get( 'apc.enable_cli' ) ) {
+ return 'apc';
+ }
} elseif ( function_exists( 'wincache_ucache_get' ) ) {
return 'wincache';
}
+
return CACHE_NONE;
}
}