Merge "Type hint against LinkTarget in WatchedItemStore"
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / objectcache / WANObjectCacheTest.php
index 017d745..9e4a5d7 100644 (file)
@@ -11,7 +11,7 @@ use Wikimedia\TestingAccessWrapper;
  * @covers WANObjectCache::getWarmupKeyMisses
  * @covers WANObjectCache::prefixCacheKeys
  * @covers WANObjectCache::getProcessCache
- * @covers WANObjectCache::getNonProcessCachedKeys
+ * @covers WANObjectCache::getNonProcessCachedMultiKeys
  * @covers WANObjectCache::getRawKeysForWarmup
  * @covers WANObjectCache::getInterimValue
  * @covers WANObjectCache::setInterimValue
@@ -47,18 +47,26 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
         * @param int $ttl
         */
        public function testSetAndGet( $value, $ttl ) {
+               $cache = $this->cache;
+
                $curTTL = null;
                $asOf = null;
-               $key = $this->cache->makeKey( 'x', wfRandomString() );
+               $key = $cache->makeKey( 'x', wfRandomString() );
 
-               $this->cache->get( $key, $curTTL, [], $asOf );
+               $cache->get( $key, $curTTL, [], $asOf );
                $this->assertNull( $curTTL, "Current TTL is null" );
                $this->assertNull( $asOf, "Current as-of-time is infinite" );
 
                $t = microtime( true );
-               $this->cache->set( $key, $value, $ttl );
 
-               $this->assertEquals( $value, $this->cache->get( $key, $curTTL, [], $asOf ) );
+               $cache->set( $key, $value, $cache::TTL_UNCACHEABLE );
+               $cache->get( $key, $curTTL, [], $asOf );
+               $this->assertNull( $curTTL, "Current TTL is null (TTL_UNCACHEABLE)" );
+               $this->assertNull( $asOf, "Current as-of-time is infinite (TTL_UNCACHEABLE)" );
+
+               $cache->set( $key, $value, $ttl );
+
+               $this->assertEquals( $value, $cache->get( $key, $curTTL, [], $asOf ) );
                if ( is_infinite( $ttl ) || $ttl == 0 ) {
                        $this->assertTrue( is_infinite( $curTTL ), "Current TTL is infinite" );
                } else {
@@ -120,79 +128,153 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertFalse( $this->cache->get( $key ), "Stale set() value ignored" );
        }
 
-       public function testProcessCache() {
+       /**
+        * @covers WANObjectCache::getWithSetCallback
+        */
+       public function testProcessCacheLruAndDelete() {
+               $cache = $this->cache;
                $mockWallClock = 1549343530.2053;
-               $this->cache->setMockTime( $mockWallClock );
+               $cache->setMockTime( $mockWallClock );
 
                $hit = 0;
-               $callback = function () use ( &$hit ) {
+               $fn = function () use ( &$hit ) {
                        ++$hit;
                        return 42;
                };
-               $keys = [ wfRandomString(), wfRandomString(), wfRandomString() ];
-               $groups = [ 'thiscache:1', 'thatcache:1', 'somecache:1' ];
+               $keysA = [ wfRandomString(), wfRandomString(), wfRandomString() ];
+               $keysB = [ wfRandomString(), wfRandomString(), wfRandomString() ];
+               $pcg = [ 'thiscache:1', 'thatcache:1', 'somecache:1' ];
 
-               foreach ( $keys as $i => $key ) {
-                       $this->cache->getWithSetCallback(
-                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               foreach ( $keysA as $i => $key ) {
+                       $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5, 'pcGroup' => $pcg[$i] ] );
                }
-               $this->assertEquals( 3, $hit );
+               $this->assertEquals( 3, $hit, "Values not cached yet" );
 
-               foreach ( $keys as $i => $key ) {
-                       $this->cache->getWithSetCallback(
-                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               foreach ( $keysA as $i => $key ) {
+                       // Should not evict from process cache
+                       $cache->delete( $key );
+                       $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5, 'pcGroup' => $pcg[$i] ] );
                }
-               $this->assertEquals( 3, $hit, "Values cached" );
+               $this->assertEquals( 3, $hit, "Values cached; not cleared by delete()" );
 
-               foreach ( $keys as $i => $key ) {
-                       $this->cache->getWithSetCallback(
-                               "$key-2", 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               foreach ( $keysB as $i => $key ) {
+                       $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5, 'pcGroup' => $pcg[$i] ] );
                }
-               $this->assertEquals( 6, $hit );
+               $this->assertEquals( 6, $hit, "New values not cached yet" );
 
-               foreach ( $keys as $i => $key ) {
-                       $this->cache->getWithSetCallback(
-                               "$key-2", 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+               foreach ( $keysB as $i => $key ) {
+                       $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5, 'pcGroup' => $pcg[$i] ] );
                }
                $this->assertEquals( 6, $hit, "New values cached" );
 
-               foreach ( $keys as $i => $key ) {
-                       // Should evict from process cache
-                       $this->cache->delete( $key );
+               foreach ( $keysA as $i => $key ) {
+                       $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5, 'pcGroup' => $pcg[$i] ] );
+               }
+               $this->assertEquals( 9, $hit, "Prior values evicted by new values" );
+       }
+
+       /**
+        * @covers WANObjectCache::getWithSetCallback
+        */
+       public function testProcessCacheInterimKeys() {
+               $cache = $this->cache;
+               $mockWallClock = 1549343530.2053;
+               $cache->setMockTime( $mockWallClock );
+
+               $hit = 0;
+               $fn = function () use ( &$hit ) {
+                       ++$hit;
+                       return 42;
+               };
+               $keysA = [ wfRandomString(), wfRandomString(), wfRandomString() ];
+               $pcg = [ 'thiscache:1', 'thatcache:1', 'somecache:1' ];
+
+               foreach ( $keysA as $i => $key ) {
+                       $cache->delete( $key ); // tombstone key
                        $mockWallClock += 0.001; // cached values will be newer than tombstone
-                       // Get into cache (specific process cache group)
-                       $this->cache->getWithSetCallback(
-                               $key, 100, $callback, [ 'pcTTL' => 5, 'pcGroup' => $groups[$i] ] );
+                       // Get into process cache (specific group) and interim cache
+                       $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5, 'pcGroup' => $pcg[$i] ] );
                }
-               $this->assertEquals( 9, $hit, "Values evicted by delete()" );
+               $this->assertEquals( 3, $hit );
 
-               // Get into cache (default process cache group)
-               $key = reset( $keys );
-               $this->cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
-               $this->assertEquals( 9, $hit, "Value recently interim-cached" );
+               // Get into process cache (default group)
+               $key = reset( $keysA );
+               $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5 ] );
+               $this->assertEquals( 3, $hit, "Value recently interim-cached" );
 
                $mockWallClock += 0.2; // interim key not brand new
-               $this->cache->clearProcessCache();
-               $this->cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
-               $this->assertEquals( 10, $hit, "Value calculated (interim key not recent and reset)" );
-               $this->cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
-               $this->assertEquals( 10, $hit, "Value process cached" );
+               $cache->clearProcessCache();
+               $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5 ] );
+               $this->assertEquals( 4, $hit, "Value calculated (interim key not recent and reset)" );
+               $cache->getWithSetCallback( $key, 100, $fn, [ 'pcTTL' => 5 ] );
+               $this->assertEquals( 4, $hit, "Value process cached" );
+       }
 
-               $mockWallClock += 0.2; // interim key not brand new
-               $outerCallback = function () use ( &$callback, $key ) {
-                       $v = $this->cache->getWithSetCallback( $key, 100, $callback, [ 'pcTTL' => 5 ] );
+       /**
+        * @covers WANObjectCache::getWithSetCallback
+        */
+       public function testProcessCacheNesting() {
+               $cache = $this->cache;
+               $mockWallClock = 1549343530.2053;
+               $cache->setMockTime( $mockWallClock );
+
+               $keyOuter = "outer-" . wfRandomString();
+               $keyInner = "inner-" . wfRandomString();
+
+               $innerHit = 0;
+               $innerFn = function () use ( &$innerHit ) {
+                       ++$innerHit;
+                       return 42;
+               };
+
+               $outerHit = 0;
+               $outerFn = function () use ( $keyInner, $innerFn, $cache, &$outerHit ) {
+                       ++$outerHit;
+                       $v = $cache->getWithSetCallback( $keyInner, 100, $innerFn, [ 'pcTTL' => 5 ] );
 
                        return 43 + $v;
                };
-               // 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" );
+
+               $cache->getWithSetCallback( $keyInner, 100, $innerFn, [ 'pcTTL' => 5 ] );
+               $cache->getWithSetCallback( $keyInner, 100, $innerFn, [ 'pcTTL' => 5 ] );
+
+               $this->assertEquals( 1, $innerHit, "Inner callback value cached" );
+               $cache->delete( $keyInner, $cache::HOLDOFF_NONE );
+               $mockWallClock += 1;
+
+               $cache->getWithSetCallback( $keyInner, 100, $innerFn, [ 'pcTTL' => 5 ] );
+               $this->assertEquals( 1, $innerHit, "Inner callback process cached" );
+
+               // Outer key misses and inner key process cache value is refused
+               $cache->getWithSetCallback( $keyOuter, 100, $outerFn );
+
+               $this->assertEquals( 1, $outerHit, "Outer callback value not yet cached" );
+               $this->assertEquals( 2, $innerHit, "Inner callback value process cache skipped" );
+
+               $cache->getWithSetCallback( $keyOuter, 100, $outerFn );
+
+               $this->assertEquals( 1, $outerHit, "Outer callback value cached" );
+
+               $cache->delete( $keyInner, $cache::HOLDOFF_NONE );
+               $cache->delete( $keyOuter, $cache::HOLDOFF_NONE );
+               $mockWallClock += 1;
+               $cache->clearProcessCache();
+               $cache->getWithSetCallback( $keyOuter, 100, $outerFn );
+
+               $this->assertEquals( 2, $outerHit, "Outer callback value not yet cached" );
+               $this->assertEquals( 3, $innerHit, "Inner callback value not yet cached" );
+
+               $cache->delete( $keyInner, $cache::HOLDOFF_NONE );
+               $mockWallClock += 1;
+               $cache->getWithSetCallback( $keyInner, 100, $innerFn, [ 'pcTTL' => 5 ] );
+
+               $this->assertEquals( 3, $innerHit, "Inner callback value process cached" );
        }
 
        /**
         * @dataProvider getWithSetCallback_provider
         * @covers WANObjectCache::getWithSetCallback()
-        * @covers WANObjectCache::doGetWithSetCallback()
+        * @covers WANObjectCache::fetchOrRegenerate()
         * @param array $extOpts
         * @param bool $versioned
         */
@@ -268,11 +350,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $curTTL = null;
                $v = $cache->get( $key, $curTTL, [ $cKey1, $cKey2 ] );
-               if ( $versioned ) {
-                       $this->assertEquals( $value, $v[$cache::VFLD_DATA], "Value returned" );
-               } else {
-                       $this->assertEquals( $value, $v, "Value returned" );
-               }
+               $this->assertEquals( $value, $v, "Value returned" );
                $this->assertLessThanOrEqual( 0, $curTTL, "Value has current TTL < 0 due to check keys" );
 
                $wasSet = 0;
@@ -378,7 +456,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
        /**
         * @dataProvider getWithSetCallback_provider
         * @covers WANObjectCache::getWithSetCallback()
-        * @covers WANObjectCache::doGetWithSetCallback()
+        * @covers WANObjectCache::fetchOrRegenerate()
         * @param array $extOpts
         * @param bool $versioned
         */
@@ -544,15 +622,6 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertEquals( 2, $wasSet, "Value re-calculated" );
        }
 
-       /**
-        * @covers WANObjectCache::getWithSetCallback()
-        * @covers WANObjectCache::doGetWithSetCallback()
-        */
-       public function testGetWithSetCallback_invalidCallback() {
-               $this->setExpectedException( InvalidArgumentException::class );
-               $this->cache->getWithSetCallback( 'key', 30, 'invalid callback' );
-       }
-
        /**
         * @dataProvider getMultiWithSetCallback_provider
         * @covers WANObjectCache::getMultiWithSetCallback
@@ -606,15 +675,16 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $value = "@efef$";
                $keyedIds = new ArrayIterator( [ $keyB => 'efef' ] );
                $v = $cache->getMultiWithSetCallback(
-                       $keyedIds, 30, $genFunc, [ 'lowTTL' => 0, 'lockTSE' => 5, ] + $extOpts );
+                       $keyedIds, 30, $genFunc, [ 'lowTTL' => 0, 'lockTSE' => 5 ] + $extOpts );
                $this->assertEquals( $value, $v[$keyB], "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value regenerated" );
-               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed yet in process cache" );
+               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in warmup cache" );
+
                $v = $cache->getMultiWithSetCallback(
-                       $keyedIds, 30, $genFunc, [ 'lowTTL' => 0, 'lockTSE' => 5, ] + $extOpts );
+                       $keyedIds, 30, $genFunc, [ 'lowTTL' => 0, 'lockTSE' => 5 ] + $extOpts );
                $this->assertEquals( $value, $v[$keyB], "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value not regenerated" );
-               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in process cache" );
+               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in warmup cache" );
 
                $mockWallClock += 1;
 
@@ -649,11 +719,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $curTTL = null;
                $v = $cache->get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
-               if ( $versioned ) {
-                       $this->assertEquals( $value, $v[$cache::VFLD_DATA], "Value returned" );
-               } else {
-                       $this->assertEquals( $value, $v, "Value returned" );
-               }
+               $this->assertEquals( $value, $v, "Value returned" );
                $this->assertLessThanOrEqual( 0, $curTTL, "Value has current TTL < 0 due to check keys" );
 
                $wasSet = 0;
@@ -701,8 +767,8 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $localBag = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'getMulti' ] )->getMock();
                $localBag->expects( $this->exactly( 1 ) )->method( 'getMulti' )->willReturn( [
-                       WANObjectCache::VALUE_KEY_PREFIX . 'k1' => 'val-id1',
-                       WANObjectCache::VALUE_KEY_PREFIX . 'k2' => 'val-id2'
+                       'WANCache:v:' . 'k1' => 'val-id1',
+                       'WANCache:v:' . 'k2' => 'val-id2'
                ] );
                $wanCache = new WANObjectCache( [ 'cache' => $localBag ] );
 
@@ -780,12 +846,13 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                        $keyedIds, 30, $genFunc, [ 'lowTTL' => 0 ] + $extOpts );
                $this->assertEquals( $value, $v[$keyB], "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value regenerated" );
-               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed yet in process cache" );
+               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in warmup cache" );
+
                $v = $cache->getMultiWithUnionSetCallback(
                        $keyedIds, 30, $genFunc, [ 'lowTTL' => 0 ] + $extOpts );
                $this->assertEquals( $value, $v[$keyB], "Value returned" );
                $this->assertEquals( 1, $wasSet, "Value not regenerated" );
-               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in process cache" );
+               $this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in warmup cache" );
 
                $mockWallClock += 1;
 
@@ -818,11 +885,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $curTTL = null;
                $v = $cache->get( $keyC, $curTTL, [ $cKey1, $cKey2 ] );
-               if ( $versioned ) {
-                       $this->assertEquals( $value, $v[$cache::VFLD_DATA], "Value returned" );
-               } else {
-                       $this->assertEquals( $value, $v, "Value returned" );
-               }
+               $this->assertEquals( $value, $v, "Value returned" );
                $this->assertLessThanOrEqual( 0, $curTTL, "Value has current TTL < 0 due to check keys" );
 
                $wasSet = 0;
@@ -880,7 +943,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
        /**
         * @covers WANObjectCache::getWithSetCallback()
-        * @covers WANObjectCache::doGetWithSetCallback()
+        * @covers WANObjectCache::fetchOrRegenerate()
         */
        public function testLockTSE() {
                $cache = $this->cache;
@@ -901,7 +964,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertEquals( 1, $calls, 'Value was populated' );
 
                // Acquire the mutex to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $checkKeys = [ wfRandomString() ]; // new check keys => force misses
                $ret = $cache->getWithSetCallback( $key, 30, $func,
@@ -924,7 +987,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
        /**
         * @covers WANObjectCache::getWithSetCallback()
-        * @covers WANObjectCache::doGetWithSetCallback()
+        * @covers WANObjectCache::fetchOrRegenerate()
         * @covers WANObjectCache::set()
         */
        public function testLockTSESlow() {
@@ -963,7 +1026,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $mockWallClock += 2; // low logical TTL expired
                // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $ret = $cache->getWithSetCallback( $key, 300, $func, [ 'lockTSE' => 5 ] );
                $this->assertEquals( $value, $ret );
@@ -971,7 +1034,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $mockWallClock += 301; // physical TTL expired
                // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $ret = $cache->getWithSetCallback( $key, 300, $func, [ 'lockTSE' => 5 ] );
                $this->assertEquals( $value, $ret );
@@ -1005,7 +1068,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
        /**
         * @covers WANObjectCache::getWithSetCallback()
-        * @covers WANObjectCache::doGetWithSetCallback()
+        * @covers WANObjectCache::fetchOrRegenerate()
         */
        public function testBusyValue() {
                $cache = $this->cache;
@@ -1029,7 +1092,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $mockWallClock += 0.2; // interim keys not brand new
 
                // Acquire a lock to verify that getWithSetCallback uses busyValue properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $checkKeys = [ wfRandomString() ]; // new check keys => force misses
                $ret = $cache->getWithSetCallback( $key, 30, $func,
@@ -1048,14 +1111,14 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertEquals( $busyValue, $ret, 'Callback was not used; used busy value' );
                $this->assertEquals( 2, $calls, 'Callback was not used; used busy value' );
 
-               $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
+               $this->internalCache->delete( 'WANCache:m:' . $key );
                $mockWallClock += 0.001; // cached values will be newer than tombstone
                $ret = $cache->getWithSetCallback( $key, 30, $func,
                        [ 'lockTSE' => 30, 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
                $this->assertEquals( $value, $ret, 'Callback was used; saved interim' );
                $this->assertEquals( 3, $calls, 'Callback was used; saved interim' );
 
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
                $ret = $cache->getWithSetCallback( $key, 30, $func,
                        [ 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
                $this->assertEquals( $value, $ret, 'Callback was not used; used interim' );
@@ -1083,7 +1146,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $cache->set( $key2, $value2, 10 );
 
                $curTTLs = [];
-               $this->assertEquals(
+               $this->assertSame(
                        [ $key1 => $value1, $key2 => $value2 ],
                        $cache->getMulti( [ $key1, $key2, $key3 ], $curTTLs ),
                        'Result array populated'
@@ -1099,7 +1162,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $mockWallClock += 1;
 
                $curTTLs = [];
-               $this->assertEquals(
+               $this->assertSame(
                        [ $key1 => $value1, $key2 => $value2 ],
                        $cache->getMulti( [ $key1, $key2, $key3 ], $curTTLs, [ $cKey1, $cKey2 ] ),
                        "Result array populated even with new check keys"
@@ -1145,7 +1208,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                // Fake initial check key to be set in the past. Otherwise we'd have to sleep for
                // several seconds during the test to assert the behaviour.
                foreach ( [ $checkAll, $check1, $check2 ] as $checkKey ) {
-                       $cache->touchCheckKey( $checkKey, WANObjectCache::HOLDOFF_NONE );
+                       $cache->touchCheckKey( $checkKey, WANObjectCache::HOLDOFF_TTL_NONE );
                }
 
                $mockWallClock += 0.100;
@@ -1160,7 +1223,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                        'key2' => $check2,
                        'key3' => $check3,
                ] );
-               $this->assertEquals(
+               $this->assertSame(
                        [ 'key1' => $value1, 'key2' => $value2 ],
                        $result,
                        'Initial values'
@@ -1180,7 +1243,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                        'key2' => $check2,
                        'key3' => $check3,
                ] );
-               $this->assertEquals(
+               $this->assertSame(
                        [ 'key1' => $value1, 'key2' => $value2 ],
                        $result,
                        'key1 expired by check1, but value still provided'
@@ -1267,7 +1330,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertLessThan( 0, $curTTL, "Deleted key is tombstoned and has current TTL < 0" );
 
                $this->cache->set( $key, $value );
-               $this->cache->delete( $key, WANObjectCache::HOLDOFF_NONE );
+               $this->cache->delete( $key, WANObjectCache::HOLDOFF_TTL_NONE );
 
                $curTTL = null;
                $v = $this->cache->get( $key, $curTTL );
@@ -1283,7 +1346,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
        /**
         * @dataProvider getWithSetCallback_versions_provider
         * @covers WANObjectCache::getWithSetCallback()
-        * @covers WANObjectCache::doGetWithSetCallback()
+        * @covers WANObjectCache::fetchOrRegenerate()
         * @param array $extOpts
         * @param bool $versioned
         */
@@ -1399,10 +1462,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 3, $wasCalled, 'Value regenerated (got mutex)' ); // sets interim
                // Lock up the mutex so interim cache is used
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 3, $wasCalled, 'Value interim cached (failed mutex)' );
-               $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
+               $this->internalCache->delete( 'WANCache:m:' . $key );
 
                $cache->useInterimHoldOffCaching( false );
 
@@ -1419,7 +1482,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 4, $wasCalled, 'Value still regenerated (got mutex)' );
                // Lock up the mutex so interim cache is used
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 5, $wasCalled, 'Value still regenerated (failed mutex)' );
        }
@@ -1485,16 +1548,16 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                // Two check keys are newer (given hold-off) than $key, another is older
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey2,
-                       WANObjectCache::PURGE_VAL_PREFIX . ( $priorTime - 3 )
+                       'WANCache:t:' . $tKey2,
+                       'PURGED:' . ( $priorTime - 3 )
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey2,
-                       WANObjectCache::PURGE_VAL_PREFIX . ( $priorTime - 5 )
+                       'WANCache:t:' . $tKey2,
+                       'PURGED:' . ( $priorTime - 5 )
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey1,
-                       WANObjectCache::PURGE_VAL_PREFIX . ( $priorTime - 30 )
+                       'WANCache:t:' . $tKey1,
+                       'PURGED:' . ( $priorTime - 30 )
                );
                $this->cache->set( $key, $value, 30 );
 
@@ -1521,30 +1584,30 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $badTime = microtime( true ) - 300;
 
                $this->internalCache->set(
-                       WANObjectCache::VALUE_KEY_PREFIX . $vKey1,
+                       'WANCache:v:' . $vKey1,
                        [
-                               WANObjectCache::FLD_VERSION => WANObjectCache::VERSION,
-                               WANObjectCache::FLD_VALUE => $value,
-                               WANObjectCache::FLD_TTL => 3600,
-                               WANObjectCache::FLD_TIME => $goodTime
+                               0 => 1,
+                               1 => $value,
+                               2 => 3600,
+                               3 => $goodTime
                        ]
                );
                $this->internalCache->set(
-                       WANObjectCache::VALUE_KEY_PREFIX . $vKey2,
+                       'WANCache:v:' . $vKey2,
                        [
-                               WANObjectCache::FLD_VERSION => WANObjectCache::VERSION,
-                               WANObjectCache::FLD_VALUE => $value,
-                               WANObjectCache::FLD_TTL => 3600,
-                               WANObjectCache::FLD_TIME => $badTime
+                               0 => 1,
+                               1 => $value,
+                               2 => 3600,
+                               3 => $badTime
                        ]
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey1,
-                       WANObjectCache::PURGE_VAL_PREFIX . $goodTime
+                       'WANCache:t:' . $tKey1,
+                       'PURGED:' . $goodTime
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey2,
-                       WANObjectCache::PURGE_VAL_PREFIX . $badTime
+                       'WANCache:t:' . $tKey2,
+                       'PURGED:' . $badTime
                );
 
                $this->assertEquals( $value, $this->cache->get( $vKey1 ) );
@@ -1569,10 +1632,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                        ->setMethods( [ 'get', 'changeTTL' ] )->getMock();
                $backend->expects( $this->once() )->method( 'get' )
                        ->willReturn( [
-                               WANObjectCache::FLD_VERSION => WANObjectCache::VERSION,
-                               WANObjectCache::FLD_VALUE => 'value',
-                               WANObjectCache::FLD_TTL => 3600,
-                               WANObjectCache::FLD_TIME => 300,
+                               0 => 1,
+                               1 => 'value',
+                               2 => 3600,
+                               3 => 300,
                        ] );
                $backend->expects( $this->once() )->method( 'changeTTL' )
                        ->willReturn( false );
@@ -1658,7 +1721,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                ] );
 
                $localBag->expects( $this->once() )->method( 'set' )
-                       ->with( "/*/mw-wan/" . $wanCache::VALUE_KEY_PREFIX . "test" );
+                       ->with( "/*/mw-wan/" . 'WANCache:v:' . "test" );
 
                $wanCache->delete( 'test' );
        }
@@ -1674,7 +1737,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                ] );
 
                $localBag->expects( $this->once() )->method( 'set' )
-                       ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" );
+                       ->with( "/*/mw-wan/" . 'WANCache:t:' . "test" );
 
                $wanCache->touchCheckKey( 'test' );
        }
@@ -1690,7 +1753,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                ] );
 
                $localBag->expects( $this->once() )->method( 'delete' )
-                       ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" );
+                       ->with( "/*/mw-wan/" . 'WANCache:t:' . "test" );
 
                $wanCache->resetCheckKey( 'test' );
        }
@@ -1802,6 +1865,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
         * @covers WANObjectCache::makeKey
         */
        public function testMakeKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $backend = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeKey' ] )->getMock();
                $backend->expects( $this->once() )->method( 'makeKey' )
@@ -1818,6 +1885,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
         * @covers WANObjectCache::makeGlobalKey
         */
        public function testMakeGlobalKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $backend = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeGlobalKey' ] )->getMock();
                $backend->expects( $this->once() )->method( 'makeGlobalKey' )
@@ -1850,6 +1921,137 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $this->assertEquals( $class, $wanCache->determineKeyClassForStats( $key ) );
        }
+
+       /**
+        * @covers WANObjectCache::makeMultiKeys
+        */
+       public function testMakeMultiKeys() {
+               $cache = $this->cache;
+
+               $ids = [ 1, 2, 3, 4, 4, 5, 6, 6, 7, 7 ];
+               $keyCallback = function ( $id, WANObjectCache $cache ) {
+                       return $cache->makeKey( 'key', $id );
+               };
+               $keyedIds = $cache->makeMultiKeys( $ids, $keyCallback );
+
+               $expected = [
+                       "local:key:1" => 1,
+                       "local:key:2" => 2,
+                       "local:key:3" => 3,
+                       "local:key:4" => 4,
+                       "local:key:5" => 5,
+                       "local:key:6" => 6,
+                       "local:key:7" => 7
+               ];
+               $this->assertSame( $expected, iterator_to_array( $keyedIds ) );
+
+               $ids = [ '1', '2', '3', '4', '4', '5', '6', '6', '7', '7' ];
+               $keyCallback = function ( $id, WANObjectCache $cache ) {
+                       return $cache->makeGlobalKey( 'key', $id, 'a', $id, 'b' );
+               };
+               $keyedIds = $cache->makeMultiKeys( $ids, $keyCallback );
+
+               $expected = [
+                       "global:key:1:a:1:b" => '1',
+                       "global:key:2:a:2:b" => '2',
+                       "global:key:3:a:3:b" => '3',
+                       "global:key:4:a:4:b" => '4',
+                       "global:key:5:a:5:b" => '5',
+                       "global:key:6:a:6:b" => '6',
+                       "global:key:7:a:7:b" => '7'
+               ];
+               $this->assertSame( $expected, iterator_to_array( $keyedIds ) );
+       }
+
+       /**
+        * @covers WANObjectCache::makeMultiKeys
+        */
+       public function testMakeMultiKeysIntString() {
+               $cache = $this->cache;
+               $ids = [ 1, 2, 3, 4, '4', 5, 6, 6, 7, '7' ];
+               $keyCallback = function ( $id, WANObjectCache $cache ) {
+                       return $cache->makeGlobalKey( 'key', $id, 'a', $id, 'b' );
+               };
+
+               $keyedIds = $cache->makeMultiKeys( $ids, $keyCallback );
+
+               $expected = [
+                       "global:key:1:a:1:b" => 1,
+                       "global:key:2:a:2:b" => 2,
+                       "global:key:3:a:3:b" => 3,
+                       "global:key:4:a:4:b" => 4,
+                       "global:key:5:a:5:b" => 5,
+                       "global:key:6:a:6:b" => 6,
+                       "global:key:7:a:7:b" => 7
+               ];
+               $this->assertSame( $expected, iterator_to_array( $keyedIds ) );
+       }
+
+       /**
+        * @covers WANObjectCache::makeMultiKeys
+        * @expectedException UnexpectedValueException
+        */
+       public function testMakeMultiKeysCollision() {
+               $ids = [ 1, 2, 3, 4, '4', 5, 6, 6, 7 ];
+
+               $this->cache->makeMultiKeys(
+                       $ids,
+                       function ( $id ) {
+                               return "keymod:" . $id % 3;
+                       }
+               );
+       }
+
+       /**
+        * @covers WANObjectCache::multiRemap
+        */
+       public function testMultiRemap() {
+               $a = [ 'a', 'b', 'c' ];
+               $res = [ 'keyA' => 1, 'keyB' => 2, 'keyC' => 3 ];
+
+               $this->assertEquals(
+                       [ 'a' => 1, 'b' => 2, 'c' => 3 ],
+                       $this->cache->multiRemap( $a, $res )
+               );
+
+               $a = [ 'a', 'b', 'c', 'c', 'd' ];
+               $res = [ 'keyA' => 1, 'keyB' => 2, 'keyC' => 3, 'keyD' => 4 ];
+
+               $this->assertEquals(
+                       [ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 ],
+                       $this->cache->multiRemap( $a, $res )
+               );
+       }
+
+       /**
+        * @covers WANObjectCache::hash256
+        */
+       public function testHash256() {
+               $bag = new HashBagOStuff();
+               $cache = new WANObjectCache( [ 'cache' => $bag, 'epoch' => 5 ] );
+               $this->assertEquals(
+                       'f402bce76bfa1136adc705d8d5719911ce1fe61f0ad82ddf79a15f3c4de6ec4c',
+                       $cache->hash256( 'x' )
+               );
+
+               $cache = new WANObjectCache( [ 'cache' => $bag, 'epoch' => 50 ] );
+               $this->assertEquals(
+                       'f79a126722f0a682c4c500509f1b61e836e56c4803f92edc89fc281da5caa54e',
+                       $cache->hash256( 'x' )
+               );
+
+               $cache = new WANObjectCache( [ 'cache' => $bag, 'secret' => 'garden' ] );
+               $this->assertEquals(
+                       '48cd57016ffe29981a1114c45e5daef327d30fc6206cb73edc3cb94b4d8fe093',
+                       $cache->hash256( 'x' )
+               );
+
+               $cache = new WANObjectCache( [ 'cache' => $bag, 'secret' => 'garden', 'epoch' => 3 ] );
+               $this->assertEquals(
+                       '48cd57016ffe29981a1114c45e5daef327d30fc6206cb73edc3cb94b4d8fe093',
+                       $cache->hash256( 'x' )
+               );
+       }
 }
 
 class NearExpiringWANObjectCache extends WANObjectCache {