objectcache: Add WANObjectCache::resetCheckKey() method
authorAaron Schulz <aschulz@wikimedia.org>
Thu, 28 May 2015 17:04:19 +0000 (10:04 -0700)
committerKrinkle <krinklemail@gmail.com>
Mon, 13 Jul 2015 21:25:20 +0000 (21:25 +0000)
Change-Id: I6f8b97c1f4511534e1ab2656f472adee491f9d9f

includes/libs/objectcache/WANObjectCache.php
tests/phpunit/includes/objectcache/WANObjectCacheTest.php

index c32efb9..e8837b3 100644 (file)
@@ -300,8 +300,10 @@ class WANObjectCache {
 
                $time = self::parsePurgeValue( $this->cache->get( $key ) );
                if ( $time === false ) {
-                       $time = microtime( true );
+                       // Casting assures identical floats for the next getCheckKeyTime() calls
+                       $time = (string)microtime( true );
                        $this->cache->add( $key, self::PURGE_VAL_PREFIX . $time, self::CHECK_KEY_TTL );
+                       $time = (float)$time;
                }
 
                return $time;
@@ -320,6 +322,13 @@ class WANObjectCache {
         * avoid race conditions where dependent keys get updated with a
         * stale value (e.g. from a DB slave).
         *
+        * This is typically useful for keys with static names or some cases
+        * dynamically generated names where a low number of combinations exist.
+        * 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 in such cases since the "time since expiry"
+        * cannot be inferred.
+        *
         * Note that "check" keys won't collide with other regular keys
         *
         * @see WANObjectCache::get()
@@ -336,6 +345,39 @@ class WANObjectCache {
                return $this->relayPurge( $key, self::CHECK_KEY_TTL ) && $ok;
        }
 
+       /**
+        * Delete a "check" key from all clusters, invalidating keys that use it
+        *
+        * This is similar to touchCheckKey() in that keys using it via
+        * getWithSetCallback() will be invalidated. The differences are:
+        *   a) The timestamp will be deleted from all caches and lazily
+        *      re-initialized when accessed (rather than set everywhere)
+        *   b) Thus, dependent keys will be known to be invalid, but not
+        *      for how long (they are treated as "just" purged), which
+        *      effects any lockTSE logic in getWithSetCallback()
+        * The advantage is that this does not place high TTL keys on every cache
+        * server, making it better for code that will cache many different keys
+        * and either does not use lockTSE or uses a low enough TTL anyway.
+        *
+        * This is typically useful for keys with dynamically generated names
+        * where a high number of combinations exist.
+        *
+        * Note that "check" keys won't collide with other regular keys
+        *
+        * @see WANObjectCache::touchCheckKey()
+        * @see WANObjectCache::get()
+        *
+        * @param string $key Cache key
+        * @return bool True if the item was purged or not found, false on failure
+        */
+       final public function resetCheckKey( $key ) {
+               $key = self::TIME_KEY_PREFIX . $key;
+               // Update the local cluster immediately
+               $ok = $this->cache->delete( $key );
+               // Publish the purge to all clusters
+               return $this->relayDelete( $key ) && $ok;
+       }
+
        /**
         * Method to fetch/regenerate cache keys
         *
@@ -545,6 +587,26 @@ class WANObjectCache {
                return $ok;
        }
 
+       /**
+        * Do the actual async bus delete of a key
+        *
+        * @param string $key Cache key
+        * @return bool Success
+        */
+       protected function relayDelete( $key ) {
+               $event = $this->cache->modifySimpleRelayEvent( array(
+                       'cmd' => 'delete',
+                       'key' => $key,
+               ) );
+
+               $ok = $this->relayer->notify( "{$this->pool}:purge", $event );
+               if ( !$ok ) {
+                       $this->lastRelayError = self::ERR_RELAY;
+               }
+
+               return $ok;
+       }
+
        /**
         * Check if a key should be regenerated (using random probability)
         *
index 10f64be..9b4eca7 100644 (file)
@@ -224,6 +224,7 @@ class WANObjectCacheTest extends MediaWikiTestCase {
 
        /**
         * @covers WANObjectCache::touchCheckKey()
+        * @covers WANObjectCache::resetCheckKey()
         * @covers WANObjectCache::getCheckKeyTime()
         */
        public function testTouchKeys() {
@@ -250,5 +251,13 @@ class WANObjectCacheTest extends MediaWikiTestCase {
 
                $t4 = $this->cache->getCheckKeyTime( $key );
                $this->assertEquals( $t3, $t4, 'Check key time did not change' );
+
+               usleep( 1 );
+               $this->cache->resetCheckKey( $key );
+               $t5 = $this->cache->getCheckKeyTime( $key );
+               $this->assertGreaterThan( $t4, $t5, 'Check key time increased' );
+
+               $t6 = $this->cache->getCheckKeyTime( $key );
+               $this->assertEquals( $t5, $t6, 'Check key time did not change' );
        }
 }