X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Flibs%2Fobjectcache%2FWANObjectCache.php;h=dac34217869b1e860e2c029f6b0880ceacd3101d;hb=b8c5ec5999af9a79e090a5052b949f7c3ac9c471;hp=9fbad395b746183a3edec0117673430ad7b58dec;hpb=43a0144346da4e6edbbadee0b7d9def53357bc10;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php index 9fbad395b7..dac3421786 100644 --- a/includes/libs/objectcache/WANObjectCache.php +++ b/includes/libs/objectcache/WANObjectCache.php @@ -194,8 +194,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { /** Tiny positive float to use when using "minTime" to assert an inequality */ const TINY_POSTIVE = 0.000001; - /** Seconds of delay after get() where set() storms are a consideration with 'lockTSE' */ - const SET_DELAY_HIGH_SEC = 0.1; + /** Milliseconds of delay after get() where set() storms are a consideration with 'lockTSE' */ + const SET_DELAY_HIGH_MS = 50; /** Min millisecond set() backoff for keys in hold-off (far less than INTERIM_KEY_TTL) */ const RECENT_SET_LOW_MS = 50; /** Max millisecond set() backoff for keys in hold-off (far less than INTERIM_KEY_TTL) */ @@ -320,7 +320,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * - tombAsOf: UNIX timestamp of the tombstone or null if the key is not tombstoned * - lastCKPurge: UNIX timestamp of the highest check key or null if none provided * - * Othwerwise, $info will transform into the cached value timestamp. + * Otherwise, $info will transform into the cached value timestamp. * * @param string $key Cache key made from makeKey() or makeGlobalKey() * @param mixed|null &$curTTL Approximate TTL left on the key if present/tombstoned [returned] @@ -521,31 +521,34 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * @param int $ttl Seconds to live. Special values are: * - WANObjectCache::TTL_INDEFINITE: Cache forever (default) * @param array $opts Options map: - * - lag : Seconds of replica DB lag. Typically, this is either the replica DB lag + * - lag: seconds of replica DB lag. Typically, this is either the replica DB lag * before the data was read or, if applicable, the replica DB lag before * the snapshot-isolated transaction the data was read from started. * Use false to indicate that replication is not running. * Default: 0 seconds - * - since : UNIX timestamp of the data in $value. Typically, this is either + * - since: UNIX timestamp of the data in $value. Typically, this is either * the current time the data was read or (if applicable) the time when * the snapshot-isolated transaction the data was read from started. * Default: 0 seconds - * - pending : Whether this data is possibly from an uncommitted write transaction. + * - pending: whether this data is possibly from an uncommitted write transaction. * Generally, other threads should not see values from the future and * they certainly should not see ones that ended up getting rolled back. * Default: false - * - lockTSE : if excessive replication/snapshot lag is detected, then store the value + * - lockTSE: if excessive replication/snapshot lag is detected, then store the value * with this TTL and flag it as stale. This is only useful if the reads for this key * use getWithSetCallback() with "lockTSE" set. Note that if "staleTTL" is set * then it will still add on to this TTL in the excessive lag scenario. * Default: WANObjectCache::TSE_NONE - * - staleTTL : Seconds to keep the key around if it is stale. The get()/getMulti() + * - staleTTL: seconds to keep the key around if it is stale. The get()/getMulti() * methods return such stale values with a $curTTL of 0, and getWithSetCallback() * will call the regeneration callback in such cases, passing in the old value * and its as-of time to the callback. This is useful if adaptiveTTL() is used * on the old value's as-of time when it is verified as still being correct. * Default: WANObjectCache::STALE_TTL_NONE. + * - creating: optimize for the case where the key does not already exist. + * Default: false * @note Options added in 1.28: staleTTL + * @note Options added in 1.33: creating * @return bool Success */ final public function set( $key, $value, $ttl = self::TTL_INDEFINITE, array $opts = [] ) { @@ -553,6 +556,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { $lockTSE = $opts['lockTSE'] ?? self::TSE_NONE; $staleTTL = $opts['staleTTL'] ?? self::STALE_TTL_NONE; $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0; + $creating = $opts['creating'] ?? false; $lag = $opts['lag'] ?? 0; // Do not cache potentially uncommitted data as it might get rolled back @@ -597,35 +601,42 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] ); // Case C: medium length request with medium replication lag + } elseif ( $lockTSE >= 0 ) { + // Store value as *almost* stale to avoid cache and mutex stampedes + $logicalTTL = self::TTL_SECOND; + $this->logger->info( + 'Lowered set() TTL for {cachekey} due to high read lag.', + [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] + ); } else { - if ( $lockTSE >= 0 ) { - // Store value as *almost* stale to avoid cache and mutex stampedes - $logicalTTL = self::TTL_SECOND; - $this->logger->info( - 'Lowered set() TTL for {cachekey} due to high read lag.', - [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] - ); - } else { - $this->logger->info( - 'Rejected set() for {cachekey} due to high read lag.', - [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] - ); + $this->logger->info( + 'Rejected set() for {cachekey} due to high read lag.', + [ 'cachekey' => $key, 'lag' => $lag, 'age' => $age ] + ); - return true; // no-op the write for being unsafe - } + return true; // no-op the write for being unsafe } } // Wrap that value with time/TTL/version metadata $wrapped = $this->wrap( $value, $logicalTTL ?: $ttl, $now ); + $storeTTL = $ttl + $staleTTL; - $func = function ( $cache, $key, $cWrapped ) use ( $wrapped ) { - return ( is_string( $cWrapped ) ) - ? false // key is tombstoned; do nothing - : $wrapped; - }; + if ( $creating ) { + $ok = $this->cache->add( self::VALUE_KEY_PREFIX . $key, $wrapped, $storeTTL ); + } else { + $ok = $this->cache->merge( + self::VALUE_KEY_PREFIX . $key, + function ( $cache, $key, $cWrapped ) use ( $wrapped ) { + // A string value means that it is a tombstone; do nothing in that case + return ( is_string( $cWrapped ) ) ? false : $wrapped; + }, + $storeTTL, + 1 // 1 attempt + ); + } - return $this->cache->merge( self::VALUE_KEY_PREFIX . $key, $func, $ttl + $staleTTL, 1 ); + return $ok; } /** @@ -1124,7 +1135,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * stampede is worth avoiding. Note that if the key falls out of cache then concurrent * threads will all run the callback on cache miss until the value is saved in cache. * The only stampede protection in that case is from duplicate cache sets when the - * callback takes longer than WANObjectCache::SET_DELAY_HIGH_SEC seconds; consider + * callback takes longer than WANObjectCache::SET_DELAY_HIGH_MS milliseconds; consider * using "busyValue" if such stampedes are a problem. Note that the higher "lockTSE" is * set, the higher the worst-case staleness of returned values can be. Also note that * this option does not by itself handle the case of the key simply expiring on account @@ -1150,10 +1161,17 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * It is generally preferable to use a class constant when setting this value. * This has no effect unless pcTTL is used. * Default: WANObjectCache::PC_PRIMARY. - * - version: Integer version number. This allows for callers to make breaking changes to - * how values are stored while maintaining compatability and correct cache purges. New - * versions are stored alongside older versions concurrently. Avoid storing class objects - * however, as this reduces compatibility (due to serialization). + * - version: Integer version number. This lets callers make breaking changes to the format + * of cached values without causing problems for sites that use non-instantaneous code + * deployments. Old and new code will recognize incompatible versions and purges from + * both old and new code will been seen by each other. When this method encounters an + * incompatibly versioned value at the provided key, a "variant key" will be used for + * reading from and saving to cache. The variant key is specific to the key and version + * number provided to this method. If the variant key value is older than that of the + * provided key, or the provided key is non-existant, then the variant key will be seen + * as non-existant. Therefore, delete() calls invalidate the provided key's variant keys. + * The "checkKeys" and "touchedCallback" options still apply to variant keys as usual. + * Avoid storing class objects, as this reduces compatibility (due to serialization). * Default: null. * - minAsOf: Reject values if they were generated before this UNIX timestamp. * This is useful if the source of a key is suspected of having possibly changed @@ -1411,6 +1429,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { } } elseif ( !$useMutex || $hasLock ) { if ( $this->checkAndSetCooloff( $key, $kClass, $ago, $lockTSE, $hasLock ) ) { + $setOpts['creating'] = ( $curValue === false ); // Save the value unless a lock-winning thread is already expected to do that $setOpts['lockTSE'] = $lockTSE; $setOpts['staleTTL'] = $staleTTL; @@ -1457,7 +1476,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { // consistent hashing). if ( $lockTSE < 0 || $hasLock ) { return true; // either not a priori hot or thread has the lock - } elseif ( $elapsed <= self::SET_DELAY_HIGH_SEC ) { + } elseif ( $elapsed <= self::SET_DELAY_HIGH_MS * 1e3 ) { return true; // not enough time for threads to pile up } @@ -2306,7 +2325,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * @return string A collection name to describe this class of key */ protected function determineKeyClassForStats( $key ) { - $parts = explode( ':', $key ); + $parts = explode( ':', $key, 3 ); return $parts[1] ?? $parts[0]; // sanity }