use Wikimedia\WaitConditionLoop;
/**
- * interface is intended to be more or less compatible with
- * the PHP memcached client.
+ * Class representing a cache/ephemeral data store
*
- * backends for local hash array and SQL table included:
- * @code
- * $bag = new HashBagOStuff();
- * $bag = new SqlBagOStuff(); # connect to db first
- * @endcode
+ * This interface is intended to be more or less compatible with the PHP memcached client.
+ *
+ * Instances of this class should be created with an intended access scope, such as:
+ * - a) A single PHP thread on a server (e.g. stored in a PHP variable)
+ * - b) A single application server (e.g. stored in APC or sqlite)
+ * - c) All application servers in datacenter (e.g. stored in memcached or mysql)
+ * - d) All application servers in all datacenters (e.g. stored via mcrouter or dynomite)
+ *
+ * Callers should use the proper factory methods that yield BagOStuff instances. Site admins
+ * should make sure the configuration for those factory methods matches their access scope.
+ * BagOStuff subclasses have widely varying levels of support for replication features.
+ *
+ * For any given instance, methods like lock(), unlock(), merge(), and set() with WRITE_SYNC
+ * should semantically operate over its entire access scope; any nodes/threads in that scope
+ * should serialize appropriately when using them. Likewise, a call to get() with READ_LATEST
+ * from one node in its access scope should reflect the prior changes of any other node its access
+ * scope. Any get() should reflect the changes of any prior set() with WRITE_SYNC.
*
* @ingroup Cache
*/
/** @var callable[] */
protected $busyCallbacks = [];
+ /** @var float|null */
+ private $wallClockOverride;
+
/** @var int[] Map of (ATTR_* class constant => QOS_* class constant) */
protected $attrMap = [];
/**
* Get an item with the given key
*
- * If the key includes a determistic input hash (e.g. the key can only have
+ * If the key includes a deterministic input hash (e.g. the key can only have
* the correct value) or complete staleness checks are handled by the caller
* (e.g. nothing relies on the TTL), then the READ_VERIFIED flag should be set.
* This lets tiered backends know they can safely upgrade a cached value to
return null;
}
- $lSince = microtime( true ); // lock timestamp
+ $lSince = $this->getCurrentTime(); // lock timestamp
return new ScopedCallback( function () use ( $key, $lSince, $expiry ) {
$latency = 0.050; // latency skew (err towards keeping lock present)
- $age = ( microtime( true ) - $lSince + $latency );
+ $age = ( $this->getCurrentTime() - $lSince + $latency );
if ( ( $age + $latency ) >= $expiry ) {
$this->logger->warning( "Lock for $key held too long ($age sec)." );
return; // expired; it's not "safe" to delete the key
*/
protected function convertExpiry( $exptime ) {
if ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) ) {
- return time() + $exptime;
+ return (int)$this->getCurrentTime() + $exptime;
} else {
return $exptime;
}
*/
protected function convertToRelative( $exptime ) {
if ( $exptime >= ( 10 * self::TTL_YEAR ) ) {
- $exptime -= time();
+ $exptime -= (int)$this->getCurrentTime();
if ( $exptime <= 0 ) {
$exptime = 1;
}
* @since 1.27
* @param string $keyspace
* @param array $args
- * @return string
+ * @return string Colon-delimited list of $keyspace followed by escaped components of $args
*/
public function makeKeyInternal( $keyspace, $args ) {
$key = $keyspace;
* Make a global cache key.
*
* @since 1.27
- * @param string $keys,... Key component
- * @return string
+ * @param string $class Key class
+ * @param string $component [optional] Key component (starting with a key collection name)
+ * @return string Colon-delimited list of $keyspace followed by escaped components of $args
*/
- public function makeGlobalKey() {
+ public function makeGlobalKey( $class, $component = null ) {
return $this->makeKeyInternal( 'global', func_get_args() );
}
* Make a cache key, scoped to this instance's keyspace.
*
* @since 1.27
- * @param string $keys,... Key component
- * @return string
+ * @param string $class Key class
+ * @param string $component [optional] Key component (starting with a key collection name)
+ * @return string Colon-delimited list of $keyspace followed by escaped components of $args
*/
- public function makeKey() {
+ public function makeKey( $class, $component = null ) {
return $this->makeKeyInternal( $this->keyspace, func_get_args() );
}
return $map;
}
+
+ /**
+ * @return float UNIX timestamp
+ * @codeCoverageIgnore
+ */
+ protected function getCurrentTime() {
+ return $this->wallClockOverride ?: microtime( true );
+ }
+
+ /**
+ * @param float|null &$time Mock UNIX timestamp for testing
+ * @codeCoverageIgnore
+ */
+ public function setMockTime( &$time ) {
+ $this->wallClockOverride =& $time;
+ }
}