* Add the ability to expose key BagOStuff attributes like whether the
emulation SQL cache is being used. Several callers use hacks to detect
this and can be updated later.
* Fallback to a safe empty WAN cache in the CACHE_DB case. This fixes
a regression from
f4bf52e8438.
* Also add this protection to the server cache, which could break
similarly before too.
* Also fix CACHE_ANYTHING by avoid the recursion risk from
fc1d4d796024 by just checking the disabled service map.
Bug: T123829
Bug: T141804
Change-Id: I17ee26138f69e01ec1aaddb55ab27caa4d542193
return $service;
}
+ /**
+ * @param string $name
+ * @return bool Whether the service is disabled
+ * @since 1.28
+ */
+ public function isServiceDisabled( $name ) {
+ return isset( $this->disabled[$name] );
+ }
}
}
}
- $this->srvCache = ObjectCache::getLocalServerInstance();
- $this->wanCache = ObjectCache::getMainWANInstance();
+ // Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
+ // @TODO: inject these in via LBFactory at some point
+ $cache = ObjectCache::getLocalServerInstance();
+ if ( $cache->getQoS( $cache::ATTR_EMULATION ) > $cache::QOS_EMULATION_SQL ) {
+ $this->srvCache = $cache;
+ } else {
+ $this->srvCache = new EmptyBagOStuff();
+ }
+ $wCache = ObjectCache::getMainWANInstance();
+ if ( $wCache->getQoS( $wCache::ATTR_EMULATION ) > $wCache::QOS_EMULATION_SQL ) {
+ $this->wanCache = $wCache;
+ } else {
+ $this->wanCache = WANObjectCache::newEmpty();
+ }
if ( isset( $params['trxProfiler'] ) ) {
$this->trxProfiler = $params['trxProfiler'];
/** @var array[] Lock tracking */
protected $locks = [];
- /** @var integer */
+ /** @var integer ERR_* class constant */
protected $lastError = self::ERR_NONE;
/** @var string */
/** @var bool */
private $dupeTrackScheduled = false;
+ /** @var integer[] Map of (ATTR_* class constant => QOS_* class constant) */
+ protected $attrMap = [];
+
/** Possible values for getLastError() */
const ERR_NONE = 0; // no error
const ERR_NO_RESPONSE = 1; // no response
public function makeKey() {
return $this->makeKeyInternal( $this->keyspace, func_get_args() );
}
+
+ /**
+ * @param integer $flag ATTR_* class constant
+ * @return integer QOS_* class constant
+ * @since 1.28
+ */
+ public function getQoS( $flag ) {
+ return isset( $this->attrMap[$flag] ) ? $this->attrMap[$flag] : self::QOS_UNKNOWN;
+ }
+
+ /**
+ * Merge the flag maps of one or more BagOStuff objects into a "lowest common denominator" map
+ *
+ * @param BagOStuff[] $bags
+ * @return integer[] Resulting flag map (class ATTR_* constant => class QOS_* constant)
+ */
+ protected function mergeFlagMaps( array $bags ) {
+ $map = [];
+ foreach ( $bags as $bag ) {
+ foreach ( $bag->attrMap as $attr => $rank ) {
+ if ( isset( $map[$attr] ) ) {
+ $map[$attr] = min( $map[$attr], $rank );
+ } else {
+ $map[$attr] = $rank;
+ }
+ }
+ }
+
+ return $map;
+ }
}
* @param array $params Parameters for HashBagOStuff
*/
function __construct( BagOStuff $backend, $params = [] ) {
- $this->backend = $backend;
parent::__construct( $params );
+
+ $this->backend = $backend;
+ $this->attrMap = $backend->attrMap;
}
protected function doGet( $key, $flags = 0 ) {
const TTL_PROC_LONG = 30; // loose cache time that can survive slow web requests
const TTL_INDEFINITE = 0;
+
+ // Attribute and QoS constants; higher QOS values with the same prefix rank higher...
+ // Medium attributes constants related to emulation or media type
+ const ATTR_EMULATION = 1;
+ const QOS_EMULATION_SQL = 1;
+ // Generic "unknown" value that is useful for comparisons (e.g. always good enough)
+ const QOS_UNKNOWN = INF;
}
$this->caches[] = ObjectFactory::getObjectFromSpec( $cacheInfo );
}
}
+ $this->mergeFlagMaps( $this->caches );
$this->asyncWrites = (
isset( $params['replication'] ) &&
$this->readStore = ( $params['readFactory'] instanceof BagOStuff )
? $params['readFactory']
: ObjectFactory::getObjectFromSpec( $params['readFactory'] );
+ $this->attrMap = $this->mergeFlagMaps( [ $this->readStore, $this->writeStore ] );
}
public function setDebug( $debug ) {
/**
* Get the "last error" registered; clearLastError() should be called manually
- * @return int ERR_* constant for the "last error" registry
+ * @return int ERR_* class constant for the "last error" registry
*/
final public function getLastError() {
if ( $this->lastRelayError ) {
$this->procCache->clear();
}
+ /**
+ * @param integer $flag ATTR_* class constant
+ * @return integer QOS_* class constant
+ * @since 1.28
+ */
+ public function getQoS( $flag ) {
+ return $this->cache->getQoS( $flag );
+ }
+
/**
* Do the actual async bus purge of a key
*
}
}
- try {
- // Make sure we actually have a DB backend before falling back to CACHE_DB
- MediaWikiServices::getInstance()->getDBLoadBalancer();
- $candidate = CACHE_DB;
- } catch ( ServiceDisabledException $e ) {
+ if ( MediaWikiServices::getInstance()->isServiceDisabled( 'DBLoadBalancer' ) ) {
// The LoadBalancer is disabled, probably because
// MediaWikiServices::disableStorageBackend was called.
$candidate = CACHE_NONE;
+ } else {
+ $candidate = CACHE_DB;
}
return self::getInstance( $candidate );
*/
public function __construct( $params ) {
parent::__construct( $params );
+
+ $this->attrMap[self::ATTR_EMULATION] = self::QOS_EMULATION_SQL;
+
if ( isset( $params['servers'] ) ) {
$this->serverInfos = [];
$this->serverTags = [];