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

Reply via email to