Collapse some nested if statements
[lhc/web/wiklou.git] / includes / libs / objectcache / WANObjectCache.php
index 41a3e4e..8f0b539 100644 (file)
@@ -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]
@@ -519,40 +519,44 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param string $key Cache key
         * @param mixed $value
         * @param int $ttl Seconds to live. Special values are:
-        *   - WANObjectCache::TTL_INDEFINITE: Cache forever
+        *   - 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 = 0, array $opts = [] ) {
+       final public function set( $key, $value, $ttl = self::TTL_INDEFINITE, array $opts = [] ) {
                $now = $this->getCurrentTime();
                $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
                }