jenkins-bot has submitted this change and it was merged.
Change subject: objectcache: Implement check keys per cache key in
WANObjectCache::getMulti()
......................................................................
objectcache: Implement check keys per cache key in WANObjectCache::getMulti()
To allow batch queries for multiple keys that themselves have different check
keys. Previously check keys always applied to all keys being retrieved.
Change-Id: I9e5ba198d79020ce05a802a510762e29fcfb2f1b
---
M includes/libs/objectcache/WANObjectCache.php
M tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
2 files changed, 133 insertions(+), 20 deletions(-)
Approvals:
Aaron Schulz: Looks good to me, approved
jenkins-bot: Verified
diff --git a/includes/libs/objectcache/WANObjectCache.php
b/includes/libs/objectcache/WANObjectCache.php
index 7e3fa4f..128999a 100644
--- a/includes/libs/objectcache/WANObjectCache.php
+++ b/includes/libs/objectcache/WANObjectCache.php
@@ -217,7 +217,8 @@
*
* @param array $keys List of cache keys
* @param array $curTTLs Map of (key => approximate TTL left) for
existing keys [returned]
- * @param array $checkKeys List of "check" keys to apply to all of $keys
+ * @param array $checkKeys List of check keys to apply to all $keys.
May also apply "check"
+ * keys to specific cache keys only by using cache keys as keys in the
$checkKeys array.
* @return array Map of (key => value) for keys that exist
*/
final public function getMulti(
@@ -228,26 +229,32 @@
$vPrefixLen = strlen( self::VALUE_KEY_PREFIX );
$valueKeys = self::prefixCacheKeys( $keys,
self::VALUE_KEY_PREFIX );
- $checkKeys = self::prefixCacheKeys( $checkKeys,
self::TIME_KEY_PREFIX );
+
+ $checksForAll = array();
+ $checksByKey = array();
+ $checkKeysFlat = array();
+ foreach ( $checkKeys as $i => $keys ) {
+ $prefixed = self::prefixCacheKeys( (array)$keys,
self::TIME_KEY_PREFIX );
+ $checkKeysFlat = array_merge( $checkKeysFlat, $prefixed
);
+ // Is this check keys for a specific cache key, or for
all keys being fetched?
+ if ( is_int( $i ) ) {
+ $checksForAll = array_merge( $checksForAll,
$prefixed );
+ } else {
+ $checksByKey[$i] = isset( $checksByKey[$i] )
+ ? array_merge( $checksByKey[$i],
$prefixed )
+ : $prefixed;
+ }
+ }
// Fetch all of the raw values
- $wrappedValues = $this->cache->getMulti( array_merge(
$valueKeys, $checkKeys ) );
+ $wrappedValues = $this->cache->getMulti( array_merge(
$valueKeys, $checkKeysFlat ) );
$now = microtime( true );
- // Get/initialize the timestamp of all the "check" keys
- $checkKeyTimes = array();
- foreach ( $checkKeys as $checkKey ) {
- $timestamp = isset( $wrappedValues[$checkKey] )
- ? self::parsePurgeValue(
$wrappedValues[$checkKey] )
- : false;
- if ( !is_float( $timestamp ) ) {
- // Key is not set or invalid; regenerate
- $this->cache->add( $checkKey,
- self::PURGE_VAL_PREFIX . $now,
self::CHECK_KEY_TTL );
- $timestamp = $now;
- }
-
- $checkKeyTimes[] = $timestamp;
+ // Collect timestamps from all "check" keys
+ $checkKeyTimesForAll = $this->processCheckKeys( $checksForAll,
$wrappedValues, $now );
+ $checkKeyTimesByKey = array();
+ foreach ( $checksByKey as $cacheKey => $checks ) {
+ $checkKeyTimesByKey[$cacheKey] =
$this->processCheckKeys( $checks, $wrappedValues, $now );
}
// Get the main cache value for each key and validate them
@@ -261,16 +268,20 @@
list( $value, $curTTL ) = $this->unwrap(
$wrappedValues[$vKey], $now );
if ( $value !== false ) {
$result[$key] = $value;
+
+ // Force dependant keys to be invalid for a
while after purging
+ // to reduce race conditions involving stale
data getting cached
+ $checkKeyTimes = $checkKeyTimesForAll;
+ if ( isset( $checkKeyTimesByKey[$key] ) ) {
+ $checkKeyTimes = array_merge(
$checkKeyTimes, $checkKeyTimesByKey[$key] );
+ }
foreach ( $checkKeyTimes as $checkKeyTime ) {
- // Force dependant keys to be invalid
for a while after purging
- // to reduce race conditions involving
stale data getting cached
$safeTimestamp = $checkKeyTime +
self::HOLDOFF_TTL;
if ( $safeTimestamp >=
$wrappedValues[$vKey][self::FLD_TIME] ) {
$curTTL = min( $curTTL,
$checkKeyTime - $now );
}
}
}
-
$curTTLs[$key] = $curTTL;
}
@@ -278,6 +289,29 @@
}
/**
+ * @since 1.27
+ * @param array $timeKeys List of prefixed time check keys
+ * @param array $wrappedValues
+ * @param float $now
+ * @return array List of timestamps
+ */
+ private function processCheckKeys( array $timeKeys, array
$wrappedValues, $now ) {
+ $times = array();
+ foreach ( $timeKeys as $timeKey ) {
+ $timestamp = isset( $wrappedValues[$timeKey] )
+ ? self::parsePurgeValue(
$wrappedValues[$timeKey] )
+ : false;
+ if ( !is_float( $timestamp ) ) {
+ // Key is not set or invalid; regenerate
+ $this->cache->add( $timeKey,
self::PURGE_VAL_PREFIX . $now, self::CHECK_KEY_TTL );
+ $timestamp = $now;
+ }
+ $times[] = $timestamp;
+ }
+ return $times;
+ }
+
+ /**
* Set the value of a key in cache
*
* Simply calling this method when source data changes is not valid
because
diff --git a/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
b/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
index d0ae8c9..b301f28 100644
--- a/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
+++ b/tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
@@ -292,6 +292,85 @@
}
/**
+ * @covers WANObjectCache::getMulti()
+ * @covers WANObjectCache::processCheckKeys()
+ */
+ public function testGetMultiCheckKeys() {
+ $cache = $this->cache;
+
+ $checkAll = wfRandomString();
+ $check1 = wfRandomString();
+ $check2 = wfRandomString();
+ $check3 = wfRandomString();
+ $value1 = wfRandomString();
+ $value2 = wfRandomString();
+
+ // 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 ( array( $checkAll, $check1, $check2 ) as $checkKey ) {
+ $this->internalCache->set( $cache::TIME_KEY_PREFIX .
$checkKey,
+ $cache::PURGE_VAL_PREFIX . microtime( true ) -
$cache::HOLDOFF_TTL, $cache::CHECK_KEY_TTL );
+ }
+
+ $cache->set( 'key1', $value1, 10 );
+ $cache->set( 'key2', $value2, 10 );
+
+ $curTTLs = array();
+ $result = $cache->getMulti( array( 'key1', 'key2', 'key3' ),
$curTTLs, array(
+ 'key1' => $check1,
+ $checkAll,
+ 'key2' => $check2,
+ 'key3' => $check3,
+ ) );
+ $this->assertEquals(
+ array( 'key1' => $value1, 'key2' => $value2 ),
+ $result,
+ 'Initial values'
+ );
+ $this->assertEquals(
+ array( 'key1' => 0, 'key2' => 0 ),
+ $curTTLs,
+ 'Initial ttls'
+ );
+
+ $cache->touchCheckKey( $check1 );
+ usleep( 100 );
+
+ $curTTLs = array();
+ $result = $cache->getMulti( array( 'key1', 'key2', 'key3' ),
$curTTLs, array(
+ 'key1' => $check1,
+ $checkAll,
+ 'key2' => $check2,
+ 'key3' => $check3,
+ ) );
+ $this->assertEquals(
+ array( 'key1' => $value1, 'key2' => $value2 ),
+ $result,
+ 'key1 expired by check1, but value still provided'
+ );
+ $this->assertLessThan( 0, $curTTLs['key1'], 'key1 TTL expired'
);
+ $this->assertEquals( 0, $curTTLs['key2'], 'key2 still valid' );
+
+ $cache->touchCheckKey( $checkAll );
+ usleep( 100 );
+
+ $curTTLs = array();
+ $result = $cache->getMulti( array( 'key1', 'key2', 'key3' ),
$curTTLs, array(
+ 'key1' => $check1,
+ $checkAll,
+ 'key2' => $check2,
+ 'key3' => $check3,
+ ) );
+ $this->assertEquals(
+ array( 'key1' => $value1, 'key2' => $value2 ),
+ $result,
+ 'All keys expired by checkAll, but value still provided'
+ );
+ $this->assertLessThan( 0, $curTTLs['key1'], 'key1 expired by
checkAll' );
+ $this->assertLessThan( 0, $curTTLs['key2'], 'key2 expired by
checkAll' );
+ }
+
+ /**
* @covers WANObjectCache::delete()
*/
public function testDelete() {
--
To view, visit https://gerrit.wikimedia.org/r/253459
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I9e5ba198d79020ce05a802a510762e29fcfb2f1b
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Krinkle <[email protected]>
Gerrit-Reviewer: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits