Aaron Schulz has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/239442

Change subject: Improvements to WANObjectCache::getWithSetCallback()
......................................................................

Improvements to WANObjectCache::getWithSetCallback()

* If lockTSE is set, make sure it applies during during
  the entire duration of a key being tombstoned. This lets
  regenerations happen during that whole time, which lowers
  the chance of seeing stale data if the DBs are lagged.
* If lockTSE is not set, do not apply tempTTL (for tomstoned keys).
  If traffic is high, a stale value would usually be "stashed" and
  used for TEMP_TTL seconds. If the lag was only 1 second, then this
  is suboptimal.
* Enable lockTSE by default, since additional overhead is only
  incurred a few seconds around invalidations, and it can prevent
  DB overhead by avoiding stampedes.

Change-Id: I3b01f0ec67935a238b30e02e42004fd3b2dcfb9d
---
M includes/libs/objectcache/WANObjectCache.php
1 file changed, 21 insertions(+), 13 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/42/239442/1

diff --git a/includes/libs/objectcache/WANObjectCache.php 
b/includes/libs/objectcache/WANObjectCache.php
index 130caeb..85a15e9 100644
--- a/includes/libs/objectcache/WANObjectCache.php
+++ b/includes/libs/objectcache/WANObjectCache.php
@@ -75,6 +75,8 @@
        const LOCK_TTL = 5;
        /** Default remaining TTL at which to consider pre-emptive regeneration 
*/
        const LOW_TTL = 10;
+       /** Default time-since-expiry on a miss that makes a key "hot" */
+       const LOCK_TSE = 2;
        /** Default TTL for temporarily caching tombstoned keys */
        const TEMP_TTL = 5;
 
@@ -465,6 +467,7 @@
         *   - 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.
+        *               [Default: WANObjectCache::LOW_TTL seconds]
         *   - 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.
@@ -472,16 +475,18 @@
         *               If, on miss, the time since expiration is low, the 
assumption
         *               is that the key is hot and that a stampede is worth 
avoiding.
         *               Setting this above WANObjectCache::HOLDOFF_TTL makes 
no difference.
+        *               [Default: WANObjectCache::LOCK_TSE seconds]
         *   - tempTTL : TTL of the temp key used to cache values while a key 
is tombstoned.
         *               This avoids excessive regeneration of hot keys on 
delete() but may
         *               result in stale values.
+        *               [Default: WANObjectCache::TEMP_TTL seconds]
         * @return mixed Value to use for the key
         */
        final public function getWithSetCallback(
                $key, $callback, $ttl, array $checkKeys = array(), array $opts 
= array()
        ) {
                $lowTTL = isset( $opts['lowTTL'] ) ? $opts['lowTTL'] : min( 
self::LOW_TTL, $ttl );
-               $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : -1;
+               $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : 
self::LOCK_TSE;
                $tempTTL = isset( $opts['tempTTL'] ) ? $opts['tempTTL'] : 
self::TEMP_TTL;
 
                // Get the current key value
@@ -498,9 +503,14 @@
                $isTombstone = ( $curTTL !== null && $value === false );
                // Assume a key is hot if requested soon after invalidation
                $isHot = ( $curTTL !== null && $curTTL <= 0 && abs( $curTTL ) 
<= $lockTSE );
+               // Decide whether a single thread should handle regenerations.
+               // This avoids stampedes when $checkKeys are bumped and when 
preemptive
+               // renegerations take too long. It also reduces regenerations 
while $key
+               // is tombstoned. This balances cache freshness with avoiding 
DB load.
+               $useMutex = ( $isHot || ( $isTombstone && $lockTSE > 0 ) );
 
                $lockAcquired = false;
-               if ( $isHot ) {
+               if ( $useMutex ) {
                        // Acquire a cluster-local non-blocking lock
                        if ( $this->cache->lock( $key, 0, self::LOCK_TTL ) ) {
                                // Lock acquired; this thread should update the 
key
@@ -508,16 +518,14 @@
                        } elseif ( $value !== false ) {
                                // If it cannot be acquired; then the stale 
value can be used
                                return $value;
-                       }
-               }
-
-               if ( !$lockAcquired && ( $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;
+                       } else {
+                               // 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;
+                               }
                        }
                }
 
@@ -529,7 +537,7 @@
                $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 ) && $ttl >= 
0 ) {
+               if ( $useMutex && $value !== false && $ttl >= 0 ) {
                        $this->cache->set( self::STASH_KEY_PREFIX . $key, 
$value, $tempTTL );
                }
 

-- 
To view, visit https://gerrit.wikimedia.org/r/239442
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I3b01f0ec67935a238b30e02e42004fd3b2dcfb9d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to