From: Timo Tijhof Date: Fri, 30 Aug 2013 00:31:37 +0000 (-0700) Subject: resourceloader: Support hashes as cache invalidation trigger X-Git-Tag: 1.31.0-rc.0~18585 X-Git-Url: http://git.cyclocoop.org/%24image?a=commitdiff_plain;h=339ca89b18fc5f3c24633cd4b105fef70d85c2ee;p=lhc%2Fweb%2Fwiklou.git resourceloader: Support hashes as cache invalidation trigger For now ResourceLoader will still internally deal with timestamps, especially because they are easier to max() on the client side (on the server we could make a hash of different hashes, on the client that's a bit annoying). However ResourceLoaderModule#getHashMtime will abstract the logic we already use in ResourceLoaderLanguageDataModule and have encouraged others to use, which is to put the timestamp and hash in Memcached, and use that to observer the hash change and update the timestamp when it does. Updated ResourceLoaderLanguageDataModule to make use of this. Change-Id: Ib051ef41ba239084671c30fd275b8d94099d5d52 --- diff --git a/RELEASE-NOTES-1.22 b/RELEASE-NOTES-1.22 index 2a2070b016..171152ac6f 100644 --- a/RELEASE-NOTES-1.22 +++ b/RELEASE-NOTES-1.22 @@ -218,8 +218,8 @@ production. and Special:AllMyUploads respectively. * IPv6 addresses in X-Forwarded-For headers are now normalised before checking against allowed proxy lists. -* Add deferrable update support for callback/closure -* Add TitleMove hook before page renames +* Add deferrable update support for callback/closure. +* Add TitleMove hook before page renames. * Revision deletion backend code is moved out of SpecialRevisiondelete * Add a variable (wgRedactedFunctionArguments) to redact the values sent as certain function parameters from exception stack traces. @@ -237,6 +237,8 @@ production. for more details regarding custom functions. ** $wgResourceLoaderLESSImportPaths is an array of file system paths. Files referenced in LESS '@import' statements are looked up here first. +* ResourceLoader supports hashes as module cache invalidation trigger (instead + of or in addition to timestamps). === Bug fixes in 1.22 === * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one diff --git a/includes/resourceloader/ResourceLoaderLanguageDataModule.php b/includes/resourceloader/ResourceLoaderLanguageDataModule.php index e840300d72..fa0fbf85b0 100644 --- a/includes/resourceloader/ResourceLoaderLanguageDataModule.php +++ b/includes/resourceloader/ResourceLoaderLanguageDataModule.php @@ -75,9 +75,10 @@ class ResourceLoaderLanguageDataModule extends ResourceLoaderModule { return $this->language->separatorTransformTable(); } - /** - * Get all the dynamic data for the content language to an array + * Get all the dynamic data for the content language to an array. + * + * NOTE: Before calling this you HAVE to make sure $this->language is set. * * @return array */ @@ -105,26 +106,20 @@ class ResourceLoaderLanguageDataModule extends ResourceLoaderModule { /** * @param $context ResourceLoaderContext - * @return array|int|Mixed + * @return int: UNIX timestamp */ public function getModifiedTime( ResourceLoaderContext $context ) { - $this->language = Language::factory( $context->getLanguage() ); - $cache = wfGetCache( CACHE_ANYTHING ); - $key = wfMemcKey( 'resourceloader', 'langdatamodule', 'changeinfo' ); + return max( 1, $this->getHashMtime( $context ) ); + } - $data = $this->getData(); - $hash = md5( serialize( $data ) ); + /** + * @param $context ResourceLoaderContext + * @return string: Hash + */ + public function getModifiedHash( ResourceLoaderContext $context ) { + $this->language = Language::factory( $context->getLanguage() ); - $result = $cache->get( $key ); - if ( is_array( $result ) && $result['hash'] === $hash ) { - return $result['timestamp']; - } - $timestamp = wfTimestamp(); - $cache->set( $key, array( - 'hash' => $hash, - 'timestamp' => $timestamp, - ) ); - return $timestamp; + return md5( serialize( $this->getData() ) ); } /** diff --git a/includes/resourceloader/ResourceLoaderModule.php b/includes/resourceloader/ResourceLoaderModule.php index 298f1fef23..11264fc87c 100644 --- a/includes/resourceloader/ResourceLoaderModule.php +++ b/includes/resourceloader/ResourceLoaderModule.php @@ -382,14 +382,59 @@ abstract class ResourceLoaderModule { * If you want this to happen, you'll need to call getMsgBlobMtime() * yourself and take its result into consideration. * - * @param ResourceLoaderContext $context - * @return int: UNIX timestamp + * NOTE: The mtime of the module's hash is NOT automatically included. + * If your module provides a getModifiedHash() method, you'll need to call getHashMtime() + * yourself and take its result into consideration. + * + * @param ResourceLoaderContext $context Context object + * @return integer UNIX timestamp */ public function getModifiedTime( ResourceLoaderContext $context ) { // 0 would mean now return 1; } + /** + * Helper method for calculating when the module's hash (if it has one) changed. + * + * @param ResourceLoaderContext $context + * @return integer: UNIX timestamp or 0 if there is no hash provided + */ + public function getHashMtime( ResourceLoaderContext $context ) { + $hash = $this->getModifiedHash( $context ); + if ( !is_string( $hash ) ) { + return 0; + } + + $cache = wfGetCache( CACHE_ANYTHING ); + $key = wfMemcKey( 'resourceloader', 'modulemodifiedhash', $this->getName() ); + + $data = $cache->get( $key ); + if ( is_array( $data ) && $data['hash'] === $hash ) { + // Hash is still the same, re-use the timestamp of when we first saw this hash. + return $data['timestamp']; + } + + $timestamp = wfTimestamp(); + $cache->set( $key, array( + 'hash' => $hash, + 'timestamp' => $timestamp, + ) ); + + return $timestamp; + } + + /** + * Get the last modification timestamp of the message blob for this + * module in a given language. + * + * @param ResourceLoaderContext $context + * @return string|null: Hash + */ + public function getModifiedHash( ResourceLoaderContext $context ) { + return null; + } + /** * Check whether this module is known to be empty. If a child class * has an easy and cheap way to determine that this module is