X-Git-Url: https://git.cyclocoop.org/%28%28?a=blobdiff_plain;f=includes%2Flibs%2Fobjectcache%2FWANObjectCache.php;h=5d9557a1d65be93ada48405caad48b9e4939dfe9;hb=6a92393760f56b100773b20e8a6449032e05f6fe;hp=7f554560853888ed940fd52405cd68c8fda8f37c;hpb=340f3e9c57df2846d403f969fcc4889a457176ac;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php old mode 100755 new mode 100644 index 7f55456085..5d9557a1d6 --- a/includes/libs/objectcache/WANObjectCache.php +++ b/includes/libs/objectcache/WANObjectCache.php @@ -74,6 +74,11 @@ class WANObjectCache { /** Seconds to keep lock keys around */ const LOCK_TTL = 5; + /** Idiom for set()/getWithSetCallback() TTL */ + const TTL_NONE = 0; + /** Idiom for getWithSetCallback() callbacks to avoid calling set() */ + const TTL_UNCACHEABLE = -1; + /** Cache format version number */ const VERSION = 1; @@ -108,6 +113,17 @@ class WANObjectCache { $this->relayer = $params['relayer']; } + /** + * @return WANObjectCache Cache that wraps EmptyBagOStuff + */ + public static function newEmpty() { + return new self( array( + 'cache' => new EmptyBagOStuff(), + 'pool' => 'empty', + 'relayer' => new EventRelayerNull( array() ) + ) ); + } + /** * Fetch the value of a key from cache * @@ -263,6 +279,8 @@ class WANObjectCache { /** * Fetch the value of a timestamp "check" key * + * Note that "check" keys won't collide with other regular keys + * * @param string $key * @return float|bool TS_UNIX timestamp of the key; false if not present */ @@ -283,6 +301,8 @@ class WANObjectCache { * avoid race conditions where dependent keys get updated with a * stale value (e.g. from a DB slave). * + * Note that "check" keys won't collide with other regular keys + * * @see WANObjectCache::get() * * @param string $key Cache key @@ -300,10 +320,13 @@ class WANObjectCache { /** * Method to fetch/regenerate cache keys * - * On cache miss, the key will be set to the callback result. + * On cache miss, the key will be set to the callback result, + * unless the callback returns false. The arguments supplied are: + * (current value or false, &$ttl) * The callback function returns the new value given the current - * value (false if not present). If false is returned, then nothing - * will be saved to cache. + * value (false if not present). Preemptive re-caching and $checkKeys + * can result in a non-false current value. The TTL of the new value + * can be set dynamically by altering $ttl in the callback (by reference). * * Usually, callbacks ignore the current value, but it can be used * to maintain "most recent X" values that come from time or sequence @@ -326,7 +349,7 @@ class WANObjectCache { * @code * $key = wfMemcKey( 'cat-recent-actions', $catId ); * // Function that derives the new key value given the old value - * $callback = function( $cValue ) { ... }; + * $callback = function( $cValue, &$ttl ) { ... }; * // Get the key value from cache or from source on cache miss; * // try to only let one cluster thread manage doing cache updates * $opts = array( 'lockTSE' => 5, 'lowTTL' => 10 ); @@ -355,16 +378,20 @@ class WANObjectCache { * * @param string $key Cache key * @param callable $callback Value generation function - * @param integer $ttl Seconds to live when the key is updated [0=forever] + * @param integer $ttl Seconds to live for key updates. Special values are: + * - WANObjectCache::TTL_NONE : cache forever + * - WANObjectCache::TTL_UNCACHEABLE : do not cache at all * @param array $checkKeys List of "check" keys * @param array $opts Options map: * - lowTTL : consider pre-emptive updates when the current TTL (sec) * of the key is less than this. It becomes more likely * over time, becoming a certainty once the key is expired. - * - lockTSE : if the key is tombstoned or expired less (by $checkKeys) + * - lockTSE : if the key is tombstoned or expired (by $checkKeys) less * than this many seconds ago, then try to have a single * thread handle cache regeneration at any given time. * Other threads will try to use stale values if possible. + * If, on miss, the time since expiration is low, the assumption + * is that the key is hot and that a stampede is worth avoiding. * - tempTTL : when 'lockTSE' is set, this determines the TTL of the temp * key used to cache values while a key is tombstoned. * This avoids excessive regeneration of hot keys on delete() but @@ -388,16 +415,12 @@ class WANObjectCache { return $value; } - if ( !is_callable( $callback ) ) { - throw new InvalidArgumentException( "Invalid cache miss callback provided." ); - } - + $isTombstone = ( $curTTL !== null && $value === false ); // Assume a key is hot if requested soon after invalidation $isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) <= $lockTSE ); - $isTombstone = ( $curTTL !== null && $value === false ); $locked = false; - if ( $isHot || $isTombstone ) { + if ( $isHot ) { // Acquire a cluster-local non-blocking lock if ( $this->cache->lock( $key, 0, self::LOCK_TTL ) ) { // Lock acquired; this thread should update the key @@ -405,22 +428,28 @@ class WANObjectCache { } elseif ( $value !== false ) { // If it cannot be acquired; then the stale value can be used return $value; - } else { - // Either another thread has the lock or the lock failed. - // Use the stash value, which is likely from the prior thread. - $value = $this->cache->get( self::STASH_KEY_PREFIX . $key ); - // Regenerate on timeout or if the other thread failed - if ( $value !== false ) { - return $value; - } } } + if ( !$locked && ( $isTombstone || $isHot ) ) { + // Use the stash value for tombstoned keys to reduce regeneration load. + // For hot keys, either another thread has the lock or the lock failed; + // use the stash value from the last thread that regenerated it. + $value = $this->cache->get( self::STASH_KEY_PREFIX . $key ); + if ( $value !== false ) { + return $value; + } + } + + if ( !is_callable( $callback ) ) { + throw new InvalidArgumentException( "Invalid cache miss callback provided." ); + } + // Generate the new value from the callback... - $value = call_user_func( $callback, $cValue ); + $value = call_user_func_array( $callback, array( $cValue, &$ttl ) ); // When delete() is called, writes are write-holed by the tombstone, // so use a special stash key to pass the new value around threads. - if ( $value !== false && ( $isHot || $isTombstone ) ) { + if ( $value !== false && ( $isHot || $isTombstone ) && $ttl >= 0 ) { $this->cache->set( self::STASH_KEY_PREFIX . $key, $value, $tempTTL ); } @@ -428,7 +457,7 @@ class WANObjectCache { $this->cache->unlock( $key ); } - if ( $value !== false ) { + if ( $value !== false && $ttl >= 0 ) { // Update the cache; this will fail if the key is tombstoned $this->set( $key, $value, $ttl ); }