objectcache: add some WAN cache preemptive refresh tests
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / objectcache / WANObjectCacheTest.php
index 0b2df61..592f72a 100644 (file)
@@ -13,6 +13,8 @@ use Wikimedia\TestingAccessWrapper;
  * @covers WANObjectCache::getProcessCache
  * @covers WANObjectCache::getNonProcessCachedKeys
  * @covers WANObjectCache::getRawKeysForWarmup
+ * @covers WANObjectCache::getInterimValue
+ * @covers WANObjectCache::setInterimValue
  */
 class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
        /** @var WANObjectCache */
@@ -157,8 +159,9 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
                $this->assertEquals( 9, $hit, "Values evicted" );
 
                $key = reset( $keys );
-               // Get into cache
+               // Get into cache (default process cache group)
                $this->cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
+               $this->assertEquals( 10, $hit, "Value calculated" );
                $this->cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
                $this->assertEquals( 10, $hit, "Value cached" );
                $outerCallback = function () use ( &$callback, $key ) {
@@ -166,7 +169,8 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
 
                        return 43 + $v;
                };
-               $this->cache->getWithSetCallback( $key, 100, $outerCallback );
+               // Outer key misses and refuses inner key process cache value
+               $this->cache->getWithSetCallback( "$key-miss-outer", 100, $outerCallback );
                $this->assertEquals( 11, $hit, "Nested callback value process cache skipped" );
        }
 
@@ -271,6 +275,66 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
                ];
        }
 
+       public function testPreemtiveRefresh() {
+               $value = 'KatCafe';
+               $wasSet = 0;
+               $func = function ( $old, &$ttl, &$opts, $asOf ) use ( &$wasSet, $value )
+               {
+                       ++$wasSet;
+                       return $value;
+               };
+
+               $cache = new NearExpiringWANObjectCache( [
+                       'cache'   => new HashBagOStuff(),
+                       'pool'    => 'empty'
+               ] );
+
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'lowTTL' => 30 ];
+               $v = $cache->getWithSetCallback( $key, 20, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $v = $cache->getWithSetCallback( $key, 20, $func, $opts );
+               $this->assertEquals( 2, $wasSet, "Value re-calculated" );
+
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'lowTTL' => 1 ];
+               $v = $cache->getWithSetCallback( $key, 30, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $v = $cache->getWithSetCallback( $key, 30, $func, $opts );
+               $this->assertEquals( 1, $wasSet, "Value cached" );
+
+               $cache = new PopularityRefreshingWANObjectCache( [
+                       'cache'   => new HashBagOStuff(),
+                       'pool'    => 'empty'
+               ] );
+
+               $now = microtime( true ); // reference time
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'hotTTR' => 900 ];
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $cache->setTime( $now + 30 );
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( 1, $wasSet, "Value cached" );
+
+               $wasSet = 0;
+               $key = wfRandomString();
+               $opts = [ 'hotTTR' => 10 ];
+               $cache->setTime( $now );
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( $value, $v, "Value returned" );
+               $this->assertEquals( 1, $wasSet, "Value calculated" );
+               $cache->setTime( $now + 30 );
+               $v = $cache->getWithSetCallback( $key, 60, $func, $opts );
+               $this->assertEquals( 2, $wasSet, "Value re-calculated" );
+       }
+
        /**
         * @covers WANObjectCache::getWithSetCallback()
         * @covers WANObjectCache::doGetWithSetCallback()
@@ -1318,4 +1382,68 @@ class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
 
                $this->assertSame( 'special', $wanCache->makeGlobalKey( 'a', 'b' ) );
        }
+
+       public static function statsKeyProvider() {
+               return [
+                       [ 'domain:page:5', 'page' ],
+                       [ 'domain:main-key', 'main-key' ],
+                       [ 'domain:page:history', 'page' ],
+                       [ 'missingdomainkey', 'missingdomainkey' ]
+               ];
+       }
+
+       /**
+        * @dataProvider statsKeyProvider
+        * @covers WANObjectCache::determineKeyClass
+        */
+       public function testStatsKeyClass( $key, $class ) {
+               $wanCache = TestingAccessWrapper::newFromObject( new WANObjectCache( [
+                       'cache' => new HashBagOStuff,
+                       'pool' => 'testcache-hash',
+                       'relayer' => new EventRelayerNull( [] )
+               ] ) );
+
+               $this->assertEquals( $class, $wanCache->determineKeyClass( $key ) );
+       }
+}
+
+class TimeAdjustableHashBagOStuff extends HashBagOStuff {
+       private $timeOverride = 0;
+
+       public function setTime( $time ) {
+               $this->timeOverride = $time;
+       }
+
+       protected function getCurrentTime() {
+               return $this->timeOverride ?: parent::getCurrentTime();
+       }
+}
+
+class TimeAdjustableWANObjectCache extends WANObjectCache {
+       private $timeOverride = 0;
+
+       public function setTime( $time ) {
+               $this->timeOverride = $time;
+               if ( $this->cache instanceof TimeAdjustableHashBagOStuff ) {
+                       $this->cache->setTime( $time );
+               }
+       }
+
+       protected function getCurrentTime() {
+               return $this->timeOverride ?: parent::getCurrentTime();
+       }
+}
+
+class NearExpiringWANObjectCache extends TimeAdjustableWANObjectCache {
+       const CLOCK_SKEW = 1;
+
+       protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
+               return ( ( $curTTL + self::CLOCK_SKEW ) < $lowTTL );
+       }
+}
+
+class PopularityRefreshingWANObjectCache extends TimeAdjustableWANObjectCache {
+       protected function worthRefreshPopular( $asOf, $ageNew, $timeTillRefresh, $now ) {
+               return ( ( $now - $asOf ) > $timeTillRefresh );
+       }
 }