Aaron Schulz has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/355602 )

Change subject: [WIP] objectcache: add getMultiWithUnionSetCallback() method
......................................................................

[WIP] objectcache: add getMultiWithUnionSetCallback() method

This supports callbacks that fetch all the missing values at once.

Change-Id: I74747cc06f97edc9163178180597e6651743b048
---
M includes/libs/objectcache/WANObjectCache.php
1 file changed, 145 insertions(+), 27 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/02/355602/1

diff --git a/includes/libs/objectcache/WANObjectCache.php 
b/includes/libs/objectcache/WANObjectCache.php
index 4b314c3..8f7d93b 100644
--- a/includes/libs/objectcache/WANObjectCache.php
+++ b/includes/libs/objectcache/WANObjectCache.php
@@ -1049,7 +1049,7 @@
        }
 
        /**
-        * Method to fetch/regenerate multiple cache keys at once
+        * Method to fetch multiple cache keys at once with regeneration
         *
         * This works the same as getWithSetCallback() except:
         *   - a) The $keys argument expects the result of 
WANObjectCache::makeMultiKeys()
@@ -1078,7 +1078,7 @@
         *         // Time-to-live (in seconds)
         *         $cache::TTL_DAY,
         *         // Function that derives the new key value
-        *         return function ( $id, $oldValue, &$ttl, array &$setOpts ) {
+        *         function ( $id, $oldValue, &$ttl, array &$setOpts ) {
         *             $dbr = wfGetDB( DB_REPLICA );
         *             // Account for any snapshot/replica DB lag
         *             $setOpts += Database::getCacheSetOptions( $dbr );
@@ -1110,38 +1110,124 @@
        ) {
                $checkKeys = isset( $opts['checkKeys'] ) ? $opts['checkKeys'] : 
[];
 
-               $keysWarmUp = [];
-               // Get all the value keys to fetch...
-               foreach ( $keyedIds as $key => $id ) {
-                       $keysWarmUp[] = self::VALUE_KEY_PREFIX . $key;
-               }
-               // Get all the check keys to fetch...
-               foreach ( $checkKeys as $i => $checkKeyOrKeys ) {
-                       if ( is_int( $i ) ) {
-                               // Single check key that applies to all value 
keys
-                               $keysWarmUp[] = self::TIME_KEY_PREFIX . 
$checkKeyOrKeys;
-                       } else {
-                               // List of check keys that apply to value key $i
-                               $keysWarmUp = array_merge(
-                                       $keysWarmUp,
-                                       self::prefixCacheKeys( $checkKeyOrKeys, 
self::TIME_KEY_PREFIX )
-                               );
-                       }
-               }
-
-               $this->keyFetchCount += count( $keysWarmUp );
-               $this->warmupCache = $this->cache->getMulti( $keysWarmUp );
-               $this->warmupCache += array_fill_keys( $keysWarmUp, false );
+               // Load required keys into process cache in one go
+               $this->warmupCache = $this->getRawKeysForWarmup( $keyedIds, 
$checkKeys );
 
                // Wrap $callback to match the getWithSetCallback() format 
while passing $id to $callback
-               $id = null;
-               $func = function ( $oldValue, &$ttl, array $setOpts, $oldAsOf ) 
use ( $callback, &$id ) {
+               $id = null; // current entity ID
+               $func = function ( $oldValue, &$ttl, &$setOpts, $oldAsOf ) use 
( $callback, &$id ) {
                        return $callback( $id, $oldValue, $ttl, $setOpts, 
$oldAsOf );
                };
 
                $values = [];
-               foreach ( $keyedIds as $key => $id ) {
+               foreach ( $keyedIds as $key => $id ) { // preserve order
                        $values[$key] = $this->getWithSetCallback( $key, $ttl, 
$func, $opts );
+               }
+
+               $this->warmupCache = [];
+
+               return $values;
+       }
+
+       /**
+        * Method to fetch/regenerate multiple cache keys at once
+        *
+        * This works the same as getWithSetCallback() except:
+        *   - a) The $keys argument expects the result of 
WANObjectCache::makeMultiKeys()
+        *   - b) The $callback argument expects a callback returning a map of 
(ID => new value)
+        *        for all entity IDs in $regenById and it takes the following 
arguments:
+        *          - $regenById: map of (entity ID => ("oldValue": mixed, 
"oldAsOf": float))
+        *          - &$ttls: a reference to the (entity ID => new TTL) map
+        *          - &$setOpts: a reference to options for set() which can be 
altered
+        *   - c) The return value is a map of (cache key => value) in the 
order of $keyedIds
+        *   - d) The "lockTSE" and "busyValue" options are ignored
+        *
+        * @see WANObjectCache::getWithSetCallback()
+        * @see WANObjectCache::getMultiWithSetCallback()
+        *
+        * Example usage:
+        * @code
+        *     $rows = $cache->getMultiWithUnionSetCallback(
+        *         // Map of cache keys to entity IDs
+        *         $cache->makeMultiKeys(
+        *             $this->fileVersionIds(),
+        *             function ( $id, WANObjectCache $cache ) {
+        *                 return $cache->makeKey( 'file-version', $id );
+        *             }
+        *         ),
+        *         // Time-to-live (in seconds)
+        *         $cache::TTL_DAY,
+        *         // Function that derives the new key value
+        *         function ( array $regenById, array &$ttls, array &$setOpts ) 
{
+        *             $dbr = wfGetDB( DB_REPLICA );
+        *             // Account for any snapshot/replica DB lag
+        *             $setOpts += Database::getCacheSetOptions( $dbr );
+        *
+        *             // Load the rows for these files
+        *             $rows = [];
+        *             $ids = array_keys( $regenById );
+        *             $res = $dbr->select( 'file', '*', [ 'id' => $ids ], 
__METHOD__ );
+        *             foreach ( $res as $row ) {
+        *                 $rows[$row->id] = $row;
+        *                 $mtime = wfTimestamp( TS_UNIX, $row->timestamp );
+        *                 $ttls[$row->id] = $this->adaptiveTTL( $mtime, 
$ttls[$row->id] );
+        *             }
+        *
+        *             return $rows;
+        *         },
+        *         ]
+        *     );
+        *     $files = array_map( [ __CLASS__, 'newFromRow' ], $rows );
+        * @endcode
+        *
+        * @param ArrayIterator $keyedIds Result of 
WANObjectCache::makeMultiKeys()
+        * @param integer $ttl Seconds to live for key updates
+        * @param callable $callback Callback the yields entity regeneration 
callbacks
+        * @param array $opts Options map
+        * @return array Map of (cache key => value) in the same order as 
$keyedIds
+        * @since 1.30
+        */
+       final public function getMultiWithUnionSetCallback(
+               ArrayIterator $keyedIds, $ttl, callable $callback, array $opts 
= []
+       ) {
+               $checkKeys = isset( $opts['checkKeys'] ) ? $opts['checkKeys'] : 
[];
+               unset( $opts['lockTSE'] ); // incompatible
+               unset( $opts['busyValue'] ); // incompatible
+
+               // Load required keys into process cache in one go
+               $this->warmupCache = $this->getRawKeysForWarmup( $keyedIds, 
$checkKeys );
+
+               // Current entity ID cursor for callback loop
+               $id = null;
+               // Regenration info by ID for keys that needed to renegerate,
+               // (e.g. if absent, stale, miss-versioned, 
preemptive/popularity refresh).
+               $regenById = [];
+
+               // Use a dummy callback just to populate $regenById
+               $func = function ( $oldValue, &$ttl, &$setOpts, $oldAsOf ) use 
( &$id, &$regenById ) {
+                       $regenById[$id] = [ 'oldValue' => $oldValue, 'oldAsOf' 
=> $oldAsOf ];
+
+                       return false; // uncacheable
+               };
+
+               // Run the cache-aside logic using warmupCache instead of 
persistent cache queries
+               $values = [];
+               foreach ( $keyedIds as $key => $id ) { // preserve order
+                       $values[$key] = $this->getWithSetCallback( $key, $ttl, 
$func, $opts );
+               }
+
+               if ( $regenById ) {
+                       // Initialize default TTLs and set options
+                       $ttls = array_fill_keys( array_keys( $regenById ), $ttl 
);
+                       $setOpts = [];
+                       // Run the callback to regenerate the values for all 
required IDs
+                       $valuesById = $callback( $regenById, $ttls, $setOpts );
+                       foreach ( $keyedIds as $key => $id ) {
+                               if ( array_key_exists( $id, $valuesById ) ) {
+                                       $values[$key] = $valuesById[$id]; // 
override dummy value
+                                       $this->set( $key, $valuesById[$id], 
$ttls[$id], $setOpts );
+                               }
+                       }
                }
 
                $this->warmupCache = [];
@@ -1606,4 +1692,36 @@
 
                return $this->processCaches[$group];
        }
+
+       /**
+        * @param ArrayIterator $keyedIds Result of 
WANObjectCache::makeMultiKeys()
+        * @param array $checkKeys
+        * @return array Map of (cache key => mixed)
+        */
+       private function getRawKeysForWarmup( ArrayIterator $keyedIds, array 
$checkKeys ) {
+               $keysWarmUp = [];
+               // Get all the value keys to fetch...
+               foreach ( $keyedIds as $key => $id ) {
+                       $keysWarmUp[] = self::VALUE_KEY_PREFIX . $key;
+               }
+               // Get all the check keys to fetch...
+               foreach ( $checkKeys as $i => $checkKeyOrKeys ) {
+                       if ( is_int( $i ) ) {
+                               // Single check key that applies to all value 
keys
+                               $keysWarmUp[] = self::TIME_KEY_PREFIX . 
$checkKeyOrKeys;
+                       } else {
+                               // List of check keys that apply to value key $i
+                               $keysWarmUp = array_merge(
+                                       $keysWarmUp,
+                                       self::prefixCacheKeys( $checkKeyOrKeys, 
self::TIME_KEY_PREFIX )
+                               );
+                       }
+               }
+
+               $this->keyFetchCount += count( $keysWarmUp );
+               $warmupCache = $this->cache->getMulti( $keysWarmUp );
+               $warmupCache += array_fill_keys( $keysWarmUp, false );
+
+               return $warmupCache;
+       }
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/355602
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I74747cc06f97edc9163178180597e6651743b048
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to