* @covers WANObjectCache::setInterimValue
*/
class WANObjectCacheTest extends PHPUnit_Framework_TestCase {
- /** @var WANObjectCache */
+ /** @var TimeAdjustableWANObjectCache */
private $cache;
/** @var BagOStuff */
private $internalCache;
protected function setUp() {
parent::setUp();
- $this->cache = new WANObjectCache( [
- 'cache' => new HashBagOStuff(),
+ $this->cache = new TimeAdjustableWANObjectCache( [
+ 'cache' => new TimeAdjustableHashBagOStuff(),
'pool' => 'testcache-hash',
'relayer' => new EventRelayerNull( [] )
] );
$this->assertGreaterThanOrEqual( 19, $curTTL, 'Current TTL between 19-20 (overriden)' );
$wasSet = 0;
- $v = $cache->getWithSetCallback( $key, 30, $func, [
- 'lowTTL' => 0,
- 'lockTSE' => 5,
- ] + $extOpts );
+ $v = $cache->getWithSetCallback(
+ $key, 30, $func, [ 'lowTTL' => 0, 'lockTSE' => 5 ] + $extOpts );
$this->assertEquals( $value, $v, "Value returned" );
$this->assertEquals( 0, $wasSet, "Value not regenerated" );
- $priorTime = microtime( true );
- usleep( 1 );
+ $priorTime = microtime( true ); // reference time
+ $cache->setTime( $priorTime );
+
+ $cache->addTime( 1 );
+
$wasSet = 0;
$v = $cache->getWithSetCallback(
$key, 30, $func, [ 'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
$t2 = $cache->getCheckKeyTime( $cKey2 );
$this->assertGreaterThanOrEqual( $priorTime, $t2, 'Check keys generated on miss' );
- $priorTime = microtime( true );
+ $priorTime = $cache->addTime( 0.01 );
+
$wasSet = 0;
$v = $cache->getWithSetCallback(
$key, 30, $func, [ 'checkKeys' => [ $cKey1, $cKey2 ] ] + $extOpts
$this->assertEquals( $value, $v, "Value still returned after deleted" );
$this->assertEquals( 1, $wasSet, "Value process cached while deleted" );
- $timeyCache = new TimeAdjustableWANObjectCache( [
- 'cache' => new TimeAdjustableHashBagOStuff(),
- 'pool' => 'empty'
- ] );
-
$oldValReceived = -1;
$oldAsOfReceived = -1;
$checkFunc = function ( $oldVal, &$ttl, array $setOpts, $oldAsOf )
$wasSet = 0;
$key = wfRandomString();
- $timeyCache->setTime( $now );
- $v = $timeyCache->getWithSetCallback(
+ $v = $cache->getWithSetCallback(
$key, 30, $checkFunc, [ 'staleTTL' => 50 ] + $extOpts );
$this->assertEquals( 'xxx1', $v, "Value returned" );
$this->assertEquals( false, $oldValReceived, "Callback got no stale value" );
$this->assertEquals( null, $oldAsOfReceived, "Callback got no stale value" );
- $timeyCache->setTime( $now + 40 );
- $v = $timeyCache->getWithSetCallback(
+ $cache->addTime( 40 );
+ $v = $cache->getWithSetCallback(
$key, 30, $checkFunc, [ 'staleTTL' => 50 ] + $extOpts );
$this->assertEquals( 'xxx2', $v, "Value still returned after expired" );
$this->assertEquals( 2, $wasSet, "Value recalculated while expired" );
$this->assertEquals( 'xxx1', $oldValReceived, "Callback got stale value" );
$this->assertNotEquals( null, $oldAsOfReceived, "Callback got stale value" );
- $timeyCache->setTime( $now + 300 );
- $v = $timeyCache->getWithSetCallback(
+ $cache->addTime( 260 );
+ $v = $cache->getWithSetCallback(
$key, 30, $checkFunc, [ 'staleTTL' => 50 ] + $extOpts );
$this->assertEquals( 'xxx3', $v, "Value still returned after expired" );
$this->assertEquals( 3, $wasSet, "Value recalculated while expired" );
$wasSet = 0;
$key = wfRandomString();
- $checkKey = $timeyCache->makeKey( 'template', 'X' );
- $timeyCache->setTime( $now - $timeyCache::HOLDOFF_TTL - 1 );
- $timeyCache->touchCheckKey( $checkKey ); // init check key
- $timeyCache->setTime( $now );
- $v = $timeyCache->getWithSetCallback(
+ $checkKey = $cache->makeKey( 'template', 'X' );
+ $cache->setTime( $now - $cache::HOLDOFF_TTL - 1 );
+ $cache->touchCheckKey( $checkKey ); // init check key
+ $cache->setTime( $now );
+ $v = $cache->getWithSetCallback(
$key,
- $timeyCache::TTL_WEEK,
+ $cache::TTL_INDEFINITE,
$checkFunc,
- [ 'graceTTL' => $timeyCache::TTL_DAY, 'checkKeys' => [ $checkKey ] ] + $extOpts
+ [ 'graceTTL' => $cache::TTL_WEEK, 'checkKeys' => [ $checkKey ] ] + $extOpts
);
$this->assertEquals( 'xxx1', $v, "Value returned" );
$this->assertEquals( 1, $wasSet, "Value computed" );
$this->assertEquals( false, $oldValReceived, "Callback got no stale value" );
$this->assertEquals( null, $oldAsOfReceived, "Callback got no stale value" );
- $timeyCache->setTime( $now + 300 ); // some time passes
- $timeyCache->touchCheckKey( $checkKey ); // make key stale
- $timeyCache->setTime( $now + 3600 ); // ~23 hours left of grace
- $v = $timeyCache->getWithSetCallback(
+ $cache->addTime( $cache::TTL_HOUR ); // some time passes
+ $v = $cache->getWithSetCallback(
+ $key,
+ $cache::TTL_INDEFINITE,
+ $checkFunc,
+ [ 'graceTTL' => $cache::TTL_WEEK, 'checkKeys' => [ $checkKey ] ] + $extOpts
+ );
+ $this->assertEquals( 'xxx1', $v, "Cached value returned" );
+ $this->assertEquals( 1, $wasSet, "Cached value returned" );
+
+ $cache->touchCheckKey( $checkKey ); // make key stale
+ $cache->addTime( 0.01 ); // ~1 week left of grace (barely stale to avoid refreshes)
+
+ $v = $cache->getWithSetCallback(
$key,
- $timeyCache::TTL_WEEK,
+ $cache::TTL_INDEFINITE,
$checkFunc,
- [ 'graceTTL' => $timeyCache::TTL_DAY, 'checkKeys' => [ $checkKey ] ] + $extOpts
+ [ 'graceTTL' => $cache::TTL_WEEK, 'checkKeys' => [ $checkKey ] ] + $extOpts
);
$this->assertEquals( 'xxx1', $v, "Value still returned after expired (in grace)" );
$this->assertEquals( 1, $wasSet, "Value still returned after expired (in grace)" );
- $timeyCache->setTime( $now + $timeyCache::TTL_DAY );
- $v = $timeyCache->getWithSetCallback(
+ // Change of refresh increase to unity as staleness approaches graceTTL
+
+ $cache->addTime( $cache::TTL_WEEK ); // 8 days of being stale
+ $v = $cache->getWithSetCallback(
$key,
- $timeyCache::TTL_WEEK,
+ $cache::TTL_INDEFINITE,
$checkFunc,
- [ 'graceTTL' => $timeyCache::TTL_DAY, 'checkKeys' => [ $checkKey ] ] + $extOpts
+ [ 'graceTTL' => $cache::TTL_WEEK, 'checkKeys' => [ $checkKey ] ] + $extOpts
);
$this->assertEquals( 'xxx2', $v, "Value was recomputed (past grace)" );
$this->assertEquals( 2, $wasSet, "Value was recomputed (past grace)" );
$this->assertEquals( 1, $wasSet, "Value not regenerated" );
$this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in process cache" );
- $priorTime = microtime( true );
- usleep( 1 );
+ $priorTime = microtime( true ); // reference time
+ $cache->setTime( $priorTime );
+
+ $cache->addTime( 1 );
+
$wasSet = 0;
$keyedIds = new ArrayIterator( [ $keyB => 'efef' ] );
$v = $cache->getMultiWithSetCallback(
$t2 = $cache->getCheckKeyTime( $cKey2 );
$this->assertGreaterThanOrEqual( $priorTime, $t2, 'Check keys generated on miss' );
- $priorTime = microtime( true );
+ $priorTime = $cache->addTime( 0.01 );
$value = "@43636$";
$wasSet = 0;
$keyedIds = new ArrayIterator( [ $keyC => 43636 ] );
$this->assertEquals( 1, $wasSet, "Value not regenerated" );
$this->assertEquals( 0, $cache->getWarmupKeyMisses(), "Keys warmed in process cache" );
- $priorTime = microtime( true );
- usleep( 1 );
+ $priorTime = microtime( true ); // reference time
+ $cache->setTime( $priorTime );
+
+ $cache->addTime( 1 );
+
$wasSet = 0;
$keyedIds = new ArrayIterator( [ $keyB => 'efef' ] );
$v = $cache->getMultiWithUnionSetCallback(
$t2 = $cache->getCheckKeyTime( $cKey2 );
$this->assertGreaterThanOrEqual( $priorTime, $t2, 'Check keys generated on miss' );
- $priorTime = microtime( true );
+ $priorTime = $cache->addTime( 0.01 );
$value = "@43636$";
$wasSet = 0;
$keyedIds = new ArrayIterator( [ $keyC => 43636 ] );
$cKey1 = wfRandomString();
$cKey2 = wfRandomString();
- $priorTime = microtime( true );
- usleep( 1 );
+ $priorTime = microtime( true ); // reference time
+ $cache->setTime( $priorTime );
+
+ $cache->addTime( 1 );
+
$curTTLs = [];
$this->assertEquals(
[ $key1 => $value1, $key2 => $value2 ],
$this->assertLessThanOrEqual( 0, $curTTLs[$key1], 'Key 1 has current TTL <= 0' );
$this->assertLessThanOrEqual( 0, $curTTLs[$key2], 'Key 2 has current TTL <= 0' );
- usleep( 1 );
+ $cache->addTime( 1 );
+
$curTTLs = [];
$this->assertEquals(
[ $key1 => $value1, $key2 => $value2 ],
$value1 = wfRandomString();
$value2 = wfRandomString();
+ $cache->setTime( microtime( true ) );
+
// 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 );
}
- usleep( 100 );
+
+ $cache->addTime( 0.100 );
$cache->set( 'key1', $value1, 10 );
$cache->set( 'key2', $value2, 10 );
$this->assertGreaterThanOrEqual( 9.5, $curTTLs['key2'], 'Initial ttls' );
$this->assertLessThanOrEqual( 10.5, $curTTLs['key2'], 'Initial ttls' );
+ $cache->addTime( 0.100 );
$cache->touchCheckKey( $check1 );
$curTTLs = [];
* @covers WANObjectCache::parsePurgeValue
*/
public function testTouchKeys() {
+ $cache = $this->cache;
$key = wfRandomString();
- $priorTime = microtime( true );
- usleep( 100 );
- $t0 = $this->cache->getCheckKeyTime( $key );
+ $priorTime = microtime( true ); // reference time
+ $cache->setTime( $priorTime );
+
+ $newTime = $cache->addTime( 0.100 );
+ $t0 = $cache->getCheckKeyTime( $key );
$this->assertGreaterThanOrEqual( $priorTime, $t0, 'Check key auto-created' );
- $priorTime = microtime( true );
- usleep( 100 );
- $this->cache->touchCheckKey( $key );
- $t1 = $this->cache->getCheckKeyTime( $key );
+ $priorTime = $newTime;
+ $newTime = $cache->addTime( 0.100 );
+ $cache->touchCheckKey( $key );
+ $t1 = $cache->getCheckKeyTime( $key );
$this->assertGreaterThanOrEqual( $priorTime, $t1, 'Check key created' );
- $t2 = $this->cache->getCheckKeyTime( $key );
+ $t2 = $cache->getCheckKeyTime( $key );
$this->assertEquals( $t1, $t2, 'Check key time did not change' );
- usleep( 100 );
- $this->cache->touchCheckKey( $key );
- $t3 = $this->cache->getCheckKeyTime( $key );
+ $cache->addTime( 0.100 );
+ $cache->touchCheckKey( $key );
+ $t3 = $cache->getCheckKeyTime( $key );
$this->assertGreaterThan( $t2, $t3, 'Check key time increased' );
- $t4 = $this->cache->getCheckKeyTime( $key );
+ $t4 = $cache->getCheckKeyTime( $key );
$this->assertEquals( $t3, $t4, 'Check key time did not change' );
- usleep( 100 );
- $this->cache->resetCheckKey( $key );
- $t5 = $this->cache->getCheckKeyTime( $key );
+ $cache->addTime( 0.100 );
+ $cache->resetCheckKey( $key );
+ $t5 = $cache->getCheckKeyTime( $key );
$this->assertGreaterThan( $t4, $t5, 'Check key time increased' );
- $t6 = $this->cache->getCheckKeyTime( $key );
+ $t6 = $cache->getCheckKeyTime( $key );
$this->assertEquals( $t5, $t6, 'Check key time did not change' );
}
}
}
+ public function addTime( $time ) {
+ $this->timeOverride += $time;
+ if ( $this->cache instanceof TimeAdjustableHashBagOStuff ) {
+ $this->cache->setTime( $this->timeOverride );
+ }
+
+ return $this->timeOverride;
+ }
+
protected function getCurrentTime() {
return $this->timeOverride ?: parent::getCurrentTime();
}
const CLOCK_SKEW = 1;
protected function worthRefreshExpiring( $curTTL, $lowTTL ) {
- return ( ( $curTTL + self::CLOCK_SKEW ) < $lowTTL );
+ return ( $curTTL > 0 && ( $curTTL + self::CLOCK_SKEW ) < $lowTTL );
}
}