http://www.mediawiki.org/wiki/Special:Code/MediaWiki/73645
Revision: 73645
Author: tparscal
Date: 2010-09-23 21:23:51 +0000 (Thu, 23 Sep 2010)
Log Message:
-----------
Improved the performance of ResourceLoader by pre-loading module information in
a batch query. This was mostly code written by catrope and patched in / gotten
working by me.
Modified Paths:
--------------
trunk/phase3/includes/ResourceLoader.php
trunk/phase3/includes/ResourceLoaderModule.php
Modified: trunk/phase3/includes/ResourceLoader.php
===================================================================
--- trunk/phase3/includes/ResourceLoader.php 2010-09-23 21:13:10 UTC (rev
73644)
+++ trunk/phase3/includes/ResourceLoader.php 2010-09-23 21:23:51 UTC (rev
73645)
@@ -33,7 +33,7 @@
/* Protected Static Methods */
- /*
+ /**
* Registers core modules and runs registration hooks
*/
protected static function initialize() {
@@ -50,6 +50,55 @@
wfProfileOut( __METHOD__ );
}
}
+
+ protected static function preloadModuleInfo( $modules,
ResourceLoaderContext $context ) {
+ $dbr = wfGetDb( DB_SLAVE );
+ $skin = $context->getSkin();
+ $lang = $context->getLanguage();
+
+ // Get file dependency information
+ $res = $dbr->select( 'module_deps', array( 'md_module',
'md_deps' ), array(
+ 'md_module' => $modules,
+ 'md_skin' => $context->getSkin()
+ ), __METHOD__
+ );
+
+ $modulesWithDeps = array();
+ foreach ( $res as $row ) {
+ self::$modules[$row->md_module]->setFileDependencies(
$skin,
+ FormatJson::decode( $row->md_deps, true )
+ );
+ $modulesWithDeps[] = $row->md_module;
+ }
+ // Register the absence of a dependencies row too
+ foreach ( array_diff( $modules, $modulesWithDeps ) as $name ) {
+ self::$modules[$name]->setFileDependencies( $skin,
array() );
+ }
+
+ // Get message blob mtimes. Only do this for modules with
messages
+ $modulesWithMessages = array();
+ $modulesWithoutMessages = array();
+ foreach ( $modules as $name ) {
+ if ( count( self::$modules[$name]->getMessages() ) ) {
+ $modulesWithMessages[] = $name;
+ } else {
+ $modulesWithoutMessages[] = $name;
+ }
+ }
+ if ( count( $modulesWithMessages ) ) {
+ $res = $dbr->select( 'msg_resource', array(
'mr_resource', 'mr_timestamp' ), array(
+ 'mr_resource' => $modulesWithMessages,
+ 'mr_lang' => $lang
+ ), __METHOD__
+ );
+ foreach ( $res as $row ) {
+
self::$modules[$row->mr_resource]->setMsgBlobMtime( $lang, $row->mr_timestamp );
+ }
+ }
+ foreach ( $modulesWithoutMessages as $name ) {
+ self::$modules[$name]->setMsgBlobMtime( $lang, 0 );
+ }
+ }
/**
* Runs text through a filter, caching the filtered result for future
calls
@@ -279,6 +328,9 @@
$smaxage =
$wgResourceLoaderMaxage['versioned']['server'];
}
+ // Preload information needed to the mtime calculation below
+ self::preloadModuleInfo( $modules, $context );
+
// To send Last-Modified and support If-Modified-Since, we need
to detect
// the last modified time
wfProfileIn( __METHOD__.'-getModifiedTime' );
@@ -307,7 +359,7 @@
// Pre-fetch blobs
$blobs = $context->shouldIncludeMessages() ?
- MessageBlobStore::get( $modules, $context->getLanguage() ) :
array();
+ MessageBlobStore::get( $modules,
$context->getLanguage() ) : array();
// Generate output
foreach ( $modules as $name ) {
Modified: trunk/phase3/includes/ResourceLoaderModule.php
===================================================================
--- trunk/phase3/includes/ResourceLoaderModule.php 2010-09-23 21:13:10 UTC
(rev 73644)
+++ trunk/phase3/includes/ResourceLoaderModule.php 2010-09-23 21:23:51 UTC
(rev 73645)
@@ -27,6 +27,11 @@
/* Protected Members */
protected $name = null;
+
+ // In-object cache for file dependencies
+ protected $fileDeps = array();
+ // In-object cache for message blob mtime
+ protected $msgBlobMtime = array();
/* Methods */
@@ -131,7 +136,73 @@
// Stub, override expected
return array();
}
+
+ /**
+ * Get the files this module depends on indirectly for a given skin.
+ * Currently these are only image files referenced by the module's CSS.
+ *
+ * @param $skin String: skin name
+ * @return array of files
+ */
+ public function getFileDependencies( $skin ) {
+ // Try in-object cache first
+ if ( isset( $this->fileDeps[$skin] ) ) {
+ return $this->fileDeps[$skin];
+ }
+ $dbr = wfGetDB( DB_SLAVE );
+ $deps = $dbr->selectField( 'module_deps', 'md_deps', array(
+ 'md_module' => $this->getName(),
+ 'md_skin' => $skin,
+ ), __METHOD__
+ );
+ if ( !is_null( $deps ) ) {
+ $this->fileDeps[$skin] = (array) FormatJson::decode(
$deps, true );
+ }
+ return $this->fileDeps[$skin];
+ }
+
+ /**
+ * Set preloaded file dependency information. Used so we can load this
+ * information for all modules at once.
+ * @param $skin string Skin name
+ * @param $deps array Array of file names
+ */
+ public function setFileDependencies( $skin, $deps ) {
+ $this->fileDeps[$skin] = $deps;
+ }
+
+ /**
+ * Get the last modification timestamp of the message blob for this
+ * module in a given language.
+ * @param $lang string Language code
+ * @return int UNIX timestamp, or 0 if no blob found
+ */
+ public function getMsgBlobMtime( $lang ) {
+ if ( !count( $this->getMessages() ) )
+ return 0;
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $msgBlobMtime = $dbr->selectField( 'msg_resource',
'mr_timestamp', array(
+ 'mr_resource' => $this->getName(),
+ 'mr_lang' => $lang
+ ), __METHOD__
+ );
+ $this->msgBlobMtime[$lang] = $msgBlobMtime ? wfTimestamp(
TS_UNIX, $msgBlobMtime ) : 0;
+ return $this->msgBlobMtime[$lang];
+ }
+
+ /**
+ * Set a preloaded message blob last modification timestamp. Used so we
+ * can load this information for all modules at once.
+ * @param $lang string Language code
+ * @param $mtime int UNIX timestamp or 0 if there is no such blob
+ */
+ public function setMsgBlobMtime( $lang, $mtime ) {
+ $this->msgBlobMtime[$lang] = $mtime;
+ }
+
+
/* Abstract Methods */
/**
@@ -483,24 +554,11 @@
$this->loaders,
$this->getFileDependencies( $context->getSkin() )
);
+
wfProfileIn( __METHOD__.'-filemtime' );
$filesMtime = max( array_map( 'filemtime', array_map( array(
__CLASS__, 'remapFilename' ), $files ) ) );
wfProfileOut( __METHOD__.'-filemtime' );
- // Only get the message timestamp if there are messages in the
module
- $msgBlobMtime = 0;
- if ( count( $this->messages ) ) {
- // Get the mtime of the message blob
- // TODO: This timestamp is queried a lot and queried
separately for each module.
- // Maybe it should be put in memcached?
- $dbr = wfGetDB( DB_SLAVE );
- $msgBlobMtime = $dbr->selectField( 'msg_resource',
'mr_timestamp', array(
- 'mr_resource' => $this->getName(),
- 'mr_lang' => $context->getLanguage()
- ), __METHOD__
- );
- $msgBlobMtime = $msgBlobMtime ? wfTimestamp( TS_UNIX,
$msgBlobMtime ) : 0;
- }
- $this->modifiedTime[$context->getHash()] = max( $filesMtime,
$msgBlobMtime );
+ $this->modifiedTime[$context->getHash()] = max( $filesMtime,
$this->getMsgBlobMtime( $context->getLanguage() ) );
wfProfileOut( __METHOD__ );
return $this->modifiedTime[$context->getHash()];
}
@@ -590,43 +648,6 @@
}
/**
- * Get the files this module depends on indirectly for a given skin.
- * Currently these are only image files referenced by the module's CSS.
- *
- * @param $skin String: skin name
- * @return array of files
- */
- protected function getFileDependencies( $skin ) {
- // Try in-object cache first
- if ( isset( $this->fileDeps[$skin] ) ) {
- return $this->fileDeps[$skin];
- }
-
- // Now try memcached
- global $wgMemc;
-
- $key = wfMemcKey( 'resourceloader', 'module_deps',
$this->getName(), $skin );
- $deps = $wgMemc->get( $key );
-
- if ( !$deps ) {
- $dbr = wfGetDB( DB_SLAVE );
- $deps = $dbr->selectField( 'module_deps', 'md_deps',
array(
- 'md_module' => $this->getName(),
- 'md_skin' => $skin,
- ), __METHOD__
- );
- if ( !$deps ) {
- $deps = '[]'; // Empty array so we can do
negative caching
- }
- $wgMemc->set( $key, $deps );
- }
-
- $this->fileDeps = FormatJson::decode( $deps, true );
-
- return $this->fileDeps;
- }
-
- /**
* Get the contents of a set of files and concatenate them, with
* newlines in between. Each file is used only once.
*
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs