/** Seconds to keep dependency purge keys around */
const CHECK_KEY_TTL = self::TTL_YEAR;
+ /** Seconds to keep interim value keys for tombstoned keys around */
+ const INTERIM_KEY_TTL = 1;
+
/** Seconds to keep lock keys around */
const LOCK_TTL = 10;
/** Default remaining TTL at which to consider pre-emptive regeneration */
* The "check" key essentially represents a last-modified time of an entity.
* When the key is touched, the timestamp will be updated to the current time.
* Keys using the "check" key via get(), getMulti(), or getWithSetCallback() will
- * be invalidated. The timestamp of "check" is treated as being HOLDOFF_TTL seconds
- * in the future by get*() methods in order to avoid race conditions where keys are
- * updated with stale values (e.g. from a DB replica DB).
+ * be invalidated. This approach is useful if many keys depend on a single entity.
*
- * This method is typically useful for keys with hardcoded names or in some cases
- * dynamically generated names, provided the number of such keys is modest. It sets a
- * high TTL on the "check" key, making it possible to know the timestamp of the last
- * change to the corresponding entities in most cases.
+ * The timestamp of the "check" key is treated as being HOLDOFF_TTL seconds in the
+ * future by get*() methods in order to avoid race conditions where keys are updated
+ * with stale values (e.g. from a lagged replica DB). A high TTL is set on the "check"
+ * key, making it possible to know the timestamp of the last change to the corresponding
+ * entities in most cases. This might use more cache space than resetCheckKey().
*
* When a few important keys get a large number of hits, a high cache time is usually
* desired as well as "lockTSE" logic. The resetCheckKey() method is less appropriate
*
* @param string $key Cache key made from makeKey() or makeGlobalKey()
* @param int $ttl Seconds to live for key updates. Special values are:
- * - WANObjectCache::TTL_INDEFINITE: Cache forever
- * - WANObjectCache::TTL_UNCACHEABLE: Do not cache at all
+ * - WANObjectCache::TTL_INDEFINITE: Cache forever (subject to LRU-style evictions)
+ * - WANObjectCache::TTL_UNCACHEABLE: Do not cache (if the key exists, it is not deleted)
* @param callable $callback Value generation function
* @param array $opts Options map:
* - checkKeys: List of "check" keys. The key at $key will be seen as invalid when either
- * touchCheckKey() or resetCheckKey() is called on any of these keys.
+ * touchCheckKey() or resetCheckKey() is called on any of the keys in this list. This
+ * is useful if thousands or millions of keys depend on the same entity. The entity can
+ * simply have its "check" key updated whenever the entity is modified.
* Default: [].
* - graceTTL: Consider reusing expired values instead of refreshing them if they expired
* less than this many seconds ago. The odds of a refresh becomes more likely over time,
* - hotTTR: Expected time-till-refresh (TTR) in seconds for keys that average ~1 hit per
* second (e.g. 1Hz). Keys with a hit rate higher than 1Hz will refresh sooner than this
* TTR and vise versa. Such refreshes won't happen until keys are "ageNew" seconds old.
- * The TTR is useful at reducing the impact of missed cache purges, since the effect of
- * a heavily referenced key being stale is worse than that of a rarely referenced key.
- * Unlike simply lowering $ttl, seldomly used keys are largely unaffected by this option,
- * which makes it possible to have a high hit rate for the "long-tail" of less-used keys.
+ * This uses randomization to avoid triggering cache stampedes. The TTR is useful at
+ * reducing the impact of missed cache purges, since the effect of a heavily referenced
+ * key being stale is worse than that of a rarely referenced key. Unlike simply lowering
+ * $ttl, seldomly used keys are largely unaffected by this option, which makes it
+ * possible to have a high hit rate for the "long-tail" of less-used keys.
* Default: WANObjectCache::HOT_TTR.
* - lowTTL: Consider pre-emptive updates when the current TTL (seconds) of the key is less
* than this. It becomes more likely over time, becoming certain once the key is expired.
+ * This helps avoid cache stampedes that might be triggered due to the key expiring.
* Default: WANObjectCache::LOW_TTL.
* - ageNew: Consider popularity refreshes only once a key reaches this age in seconds.
* Default: WANObjectCache::AGE_NEW.
use ( $callback, $version ) {
if ( is_array( $oldValue )
&& array_key_exists( self::VFLD_DATA, $oldValue )
+ && array_key_exists( self::VFLD_VERSION, $oldValue )
+ && $oldValue[self::VFLD_VERSION] === $version
) {
$oldData = $oldValue[self::VFLD_DATA];
} else {
// VFLD_DATA is not set if an old, unversioned, key is present
$oldData = false;
+ $oldAsOf = null;
}
return [
$isTombstone = ( $curTTL !== null && $value === false );
if ( $isTombstone && $lockTSE <= 0 ) {
// Use the INTERIM value for tombstoned keys to reduce regeneration load
- $lockTSE = 1;
+ $lockTSE = self::INTERIM_KEY_TTL;
}
// Assume a key is hot if requested soon after invalidation
$isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE );
return $minTTL; // no last-modified time provided
}
- $age = time() - $mtime;
+ $age = $this->getCurrentTime() - $mtime;
return (int)min( $maxTTL, max( $minTTL, $factor * $age ) );
}
$ageStale = abs( $curTTL ); // seconds of staleness
$curGTTL = ( $graceTTL - $ageStale ); // current grace-time-to-live
+ if ( $curGTTL <= 0 ) {
+ return false; // already out of grace period
+ }
// Chance of using a stale value is the complement of the chance of refreshing it
return !$this->worthRefreshExpiring( $curGTTL, $graceTTL );