From: Aaron Schulz Date: Wed, 11 Jul 2018 21:29:51 +0000 (+0100) Subject: objectcache: add "epoch" parameter to WANObjectCache X-Git-Tag: 1.34.0-rc.0~4517^2 X-Git-Url: http://git.heureux-cyclage.org/?a=commitdiff_plain;h=4af7fbd76a50c1c578f2af2c9a779cf1db35c3cd;p=lhc%2Fweb%2Fwiklou.git objectcache: add "epoch" parameter to WANObjectCache This can be used to discard all values before a certain point in time, such as periods of severe network problems or misconfiguration that resulted in missed purges. Change-Id: I612db8f91a5960b912e9f35645a3d3872df47460 --- diff --git a/includes/libs/objectcache/WANObjectCache.php b/includes/libs/objectcache/WANObjectCache.php index e30e061815..ed791284f3 100644 --- a/includes/libs/objectcache/WANObjectCache.php +++ b/includes/libs/objectcache/WANObjectCache.php @@ -107,6 +107,8 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { protected $useInterimHoldOffCaching = true; /** @var callable|null Function that takes a WAN cache callback and runs it later */ protected $asyncHandler; + /** @var float Unix timestamp of the oldest possible valid values */ + protected $epoch; /** @var int ERR_* constant for the "last error" registry */ protected $lastRelayError = self::ERR_NONE; @@ -223,6 +225,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { * - mcrouterAware: set as true if mcrouter is the backing store proxy and mcrouter * is configured to interpret /// key prefixes as routes. This * requires that "region" and "cluster" are both set above. [optional] + * - epoch: lowest UNIX timestamp a value/tombstone must have to be valid. [optional] */ public function __construct( array $params ) { $this->cache = $params['cache']; @@ -231,6 +234,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { $this->region = $params['region'] ?? 'main'; $this->cluster = $params['cluster'] ?? 'wan-main'; $this->mcrouterAware = !empty( $params['mcrouterAware'] ); + $this->epoch = $params['epoch'] ?? 1.0; $this->setLogger( $params['logger'] ?? new NullLogger() ); $this->stats = $params['stats'] ?? new NullStatsdDataFactory(); @@ -2040,6 +2044,11 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { $curTTL = INF; } + if ( $wrapped[self::FLD_TIME] < $this->epoch ) { + // Values this old are ignored + return [ false, null ]; + } + return [ $wrapped[self::FLD_VALUE], $curTTL ]; } @@ -2068,7 +2077,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { } /** - * @param string $value Wrapped value like "PURGED::" + * @param string|array|bool $value Possible string of the form "PURGED::" * @return array|bool Array containing a UNIX timestamp (float) and holdoff period (integer), * or false if value isn't a valid purge value */ @@ -2076,16 +2085,24 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface { if ( !is_string( $value ) ) { return false; } + $segments = explode( ':', $value, 3 ); if ( !isset( $segments[0] ) || !isset( $segments[1] ) || "{$segments[0]}:" !== self::PURGE_VAL_PREFIX ) { return false; } + if ( !isset( $segments[2] ) ) { // Back-compat with old purge values without holdoff $segments[2] = self::HOLDOFF_TTL; } + + if ( $segments[1] < $this->epoch ) { + // Values this old are ignored + return false; + } + return [ self::FLD_TIME => (float)$segments[1], self::FLD_HOLDOFF => (int)$segments[2], diff --git a/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php b/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php index 662bb9617c..22aa6671bb 100644 --- a/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php +++ b/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php @@ -1569,6 +1569,44 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase { $wanCache->resetCheckKey( 'test' ); } + public function testEpoch() { + $bag = new HashBagOStuff(); + $cache = new WANObjectCache( [ 'cache' => $bag, 'pool' => 'testcache-hash' ] ); + $key = $cache->makeGlobalKey( 'The whole of the Law' ); + + $now = microtime( true ); + $cache->setMockTime( $now ); + + $cache->set( $key, 'Do what thou Wilt' ); + $cache->touchCheckKey( $key ); + + $then = $now; + $now += 30; + $this->assertEquals( 'Do what thou Wilt', $cache->get( $key ) ); + $this->assertEquals( $then, $cache->getCheckKeyTime( $key ), 'Check key init', 0.01 ); + + $cache = new WANObjectCache( [ + 'cache' => $bag, + 'pool' => 'testcache-hash', + 'epoch' => $now - 3600 + ] ); + $cache->setMockTime( $now ); + + $this->assertEquals( 'Do what thou Wilt', $cache->get( $key ) ); + $this->assertEquals( $then, $cache->getCheckKeyTime( $key ), 'Check key kept', 0.01 ); + + $now += 30; + $cache = new WANObjectCache( [ + 'cache' => $bag, + 'pool' => 'testcache-hash', + 'epoch' => $now + 3600 + ] ); + $cache->setMockTime( $now ); + + $this->assertFalse( $cache->get( $key ), 'Key rejected due to epoch' ); + $this->assertEquals( $now, $cache->getCheckKeyTime( $key ), 'Check key reset', 0.01 ); + } + /** * @dataProvider provideAdaptiveTTL * @covers WANObjectCache::adaptiveTTL()