From 3a252efa12253e77a07a75da25bcf558c5bcd209 Mon Sep 17 00:00:00 2001 From: Ori Livneh Date: Sat, 8 Aug 2015 21:59:47 -0700 Subject: [PATCH] MessageCache: use APC for local caching, rather than files In addition to eliminating disk IO in a hot path, using APC spares us from having to serialize and unserialize cache arrays. Since we're not serializing, though, we don't have a string representation to hash, so use a random string instead. (The code already treats the association of hash string to cache as purely symbolic, so this is not problematic.) Whereas the hash was previously stored as the first 32 bytes of each cache file, we now store it as an array key instead (like VERSION and EXPIRY were already). Because this changes the structure of cached data, we have to bump MSG_CACHE_VERSION. While we're here, make MessageCache::getLocalCache() and MessageCache::saveToLocal() protected, make their signatures more consistent with other methods in this class. While they were (implicitly) public before, there are absolutely no external callers in Core or extensions[0][1], so we can skip the standard deprecation process. [0]: https://github.com/search?q=%40wikimedia+getlocalcache&type=Code&utf8=%E2%9C%93 [1]: https://github.com/search?utf8=%E2%9C%93&q=%40wikimedia+savetolocal&type=Code Change-Id: I020617d2df2a8f0f243b85f3383dc7b16f15aaad --- RELEASE-NOTES-1.26 | 2 + includes/cache/MessageCache.php | 89 ++++++++++----------------------- 2 files changed, 28 insertions(+), 63 deletions(-) diff --git a/RELEASE-NOTES-1.26 b/RELEASE-NOTES-1.26 index b4702728fb..914bf15fa0 100644 --- a/RELEASE-NOTES-1.26 +++ b/RELEASE-NOTES-1.26 @@ -53,6 +53,8 @@ production. the $wgMainCacheType settings. * Callers needing fast light-weight data stores use $wgMainStash to select the store type from $wgObjectCaches. The default is the local database. +* Interface message overrides in the MediaWiki namespace will now be cached in + memcached and APC (if available), rather than memcached and local files. ==== External libraries ==== * Update es5-shim from v4.0.0 to v4.1.5. diff --git a/includes/cache/MessageCache.php b/includes/cache/MessageCache.php index 67a74614c6..8fe0bf9019 100644 --- a/includes/cache/MessageCache.php +++ b/includes/cache/MessageCache.php @@ -25,7 +25,7 @@ * MediaWiki message cache structure version. * Bump this whenever the message cache format has changed. */ -define( 'MSG_CACHE_VERSION', 1 ); +define( 'MSG_CACHE_VERSION', 2 ); /** * Memcached timeout when loading a key. @@ -58,6 +58,7 @@ class MessageCache { * second level is message key and the values are either message * content prefixed with space, or !NONEXISTENT for negative * caching. + * @var array $mCache */ protected $mCache; @@ -154,6 +155,8 @@ class MessageCache { * @param int $expiry Lifetime for cache. @see $mExpiry. */ function __construct( $memCached, $useDB, $expiry ) { + global $wgUseLocalMessageCache; + if ( !$memCached ) { $memCached = wfGetCache( CACHE_NONE ); } @@ -162,6 +165,12 @@ class MessageCache { $this->mDisable = !$useDB; $this->mExpiry = $expiry; + if ( $wgUseLocalMessageCache ) { + $this->localCache = ObjectCache::newAccelerator( array(), CACHE_NONE ); + } else { + $this->localCache = wfGetCache( CACHE_NONE ); + } + $this->wanCache = ObjectCache::getMainWANInstance(); } @@ -180,70 +189,25 @@ class MessageCache { } /** - * Try to load the cache from a local file. + * Try to load the cache from APC. * - * @param string $hash The hash of contents, to check validity. * @param string $code Optional language code, see documenation of load(). - * @return array The cache array + * @return array|bool The cache array, or false if not in cache. */ - function getLocalCache( $hash, $code ) { - global $wgCacheDirectory; - - $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; - - # Check file existence - MediaWiki\suppressWarnings(); - $file = fopen( $filename, 'r' ); - MediaWiki\restoreWarnings(); - if ( !$file ) { - return false; // No cache file - } - - // Check to see if the file has the hash specified - $localHash = fread( $file, 32 ); - if ( $hash === $localHash ) { - // All good, get the rest of it - $serialized = ''; - while ( !feof( $file ) ) { - $serialized .= fread( $file, 100000 ); - } - fclose( $file ); - - return unserialize( $serialized ); - } else { - fclose( $file ); - - return false; // Wrong hash - } + protected function getLocalCache( $code ) { + $cacheKey = wfMemcKey( __CLASS__, $code ); + return $this->localCache->get( $cacheKey ); } /** - * Save the cache to a local file. - * @param string $serialized - * @param string $hash + * Save the cache to APC. + * * @param string $code + * @param array $cache The cache array */ - function saveToLocal( $serialized, $hash, $code ) { - global $wgCacheDirectory; - - $filename = "$wgCacheDirectory/messages-" . wfWikiID() . "-$code"; - wfMkdirParents( $wgCacheDirectory, null, __METHOD__ ); // might fail - - MediaWiki\suppressWarnings(); - $file = fopen( $filename, 'w' ); - MediaWiki\restoreWarnings(); - - if ( !$file ) { - wfDebug( "Unable to open local cache file for writing\n" ); - - return; - } - - fwrite( $file, $hash . $serialized ); - fclose( $file ); - MediaWiki\suppressWarnings(); - chmod( $filename, 0666 ); - MediaWiki\restoreWarnings(); + protected function saveToLocalCache( $code, $cache ) { + $cacheKey = wfMemcKey( __CLASS__, $code ); + $this->localCache->set( $cacheKey, $cache ); } /** @@ -305,8 +269,8 @@ class MessageCache { if ( $wgUseLocalMessageCache ) { list( $hash, $hashExpired ) = $this->getValidationHash( $code ); if ( $hash ) { - $cache = $this->getLocalCache( $hash, $code ); - if ( !$cache ) { + $cache = $this->getLocalCache( $code ); + if ( !$cache || !isset( $cache['HASH'] ) || $cache['HASH'] !== $hash ) { $where[] = 'local cache is empty or has the wrong hash'; } elseif ( $this->isCacheExpired( $cache ) ) { $where[] = 'local cache is expired'; @@ -559,6 +523,7 @@ class MessageCache { } $cache['VERSION'] = MSG_CACHE_VERSION; + $cache['HASH'] = wfRandomString( 8 ); $cache['EXPIRY'] = wfTimestamp( TS_MW, time() + $this->mExpiry ); return $cache; @@ -668,10 +633,8 @@ class MessageCache { # Save to local cache if ( $wgUseLocalMessageCache ) { - $serialized = serialize( $cache ); - $hash = md5( $serialized ); - $this->setValidationHash( $code, $hash ); - $this->saveToLocal( $serialized, $hash, $code ); + $this->setValidationHash( $code, $cache['HASH'] ); + $this->saveToLocalCache( $code, $cache ); } return $success; -- 2.20.1