From: Ilmari Karonen Date: Sun, 14 Dec 2008 05:47:48 +0000 (+0000) Subject: Add a new FileCache class to wrap RepoGroup::findFile() and findFiles(), and make... X-Git-Tag: 1.31.0-rc.0~43981 X-Git-Url: http://git.cyclocoop.org/%7B%24admin_url%7Dmembres/cotisations/gestion/rappel_supprimer.php?a=commitdiff_plain;h=8b306154235b56bcbc74c03b6dabcbcbefbafb64;p=lhc%2Fweb%2Fwiklou.git Add a new FileCache class to wrap RepoGroup::findFile() and findFiles(), and make wfFindFile() use it by default. This should improve performance for pages that refer to the same image several times, but the real benefit is that it allows batch file existence checks, à la LinkBatch, by collecting a set of titles (or DB keys) and calling FileCache::findFiles() on them to prefill the cache. XXX: The code seems to more or less work, but it obviously needs more testing, regarding both stability and memory usage. In particular, I have not tested file uploads yet -- there may be consistency issues there. --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 866e81f106..feca50a742 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -229,6 +229,7 @@ The following extensions are migrated into MediaWiki 1.14: your language) instead of Wikipedia. * (bug 16635) The "view and edit watchlist" page (Special:Watchlist/edit) now includes a table of contents +* File objects returned by wfFindFile() are now cached by default === Bug fixes in 1.14 === diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index f6eb5d8a2d..ce1912ea65 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -365,6 +365,7 @@ $wgAutoloadLocalClasses = array( # includes/filerepo 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php', 'File' => 'includes/filerepo/File.php', + 'FileCache' => 'includes/filerepo/FileCache.php', 'FileRepo' => 'includes/filerepo/FileRepo.php', 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php', 'ForeignAPIFile' => 'includes/filerepo/ForeignAPIFile.php', diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 44304409dc..d450836ae0 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -2719,10 +2719,15 @@ function &wfGetLBFactory() { * current version. An image object will be returned which * was created at the specified time. * @param mixed $flags FileRepo::FIND_ flags + * @param boolean $bypass Bypass the file cache even if it could be used * @return File, or false if the file does not exist */ -function wfFindFile( $title, $time = false, $flags = 0 ) { - return RepoGroup::singleton()->findFile( $title, $time, $flags ); +function wfFindFile( $title, $time = false, $flags = 0, $bypass = false ) { + if( !$time && !$flags && !$bypass ) { + return FileCache::singleton()->findFile( $title ); + } else { + return RepoGroup::singleton()->findFile( $title, $time, $flags ); + } } /** diff --git a/includes/filerepo/FileCache.php b/includes/filerepo/FileCache.php new file mode 100644 index 0000000000..7840d1a3b4 --- /dev/null +++ b/includes/filerepo/FileCache.php @@ -0,0 +1,156 @@ +repoGroup = $repoGroup; + } + + + /** + * Add some files to the cache. This is a fairly low-level function, + * which most users should not need to call. Note that any existing + * entries for the same keys will not be replaced. Call clearFiles() + * first if you need that. + * @param array $files array of File objects, indexed by DB key + */ + function addFiles( $files ) { + wfDebug( "FileCache adding ".count( $files )." files\n" ); + $this->cache += $files; + } + + /** + * Remove some files from the cache, so that their existence will be + * rechecked. This is a fairly low-level function, which most users + * should not need to call. + * @param array $remove array indexed by DB keys to remove (the values are ignored) + */ + function clearFiles( $remove ) { + wfDebug( "FileCache clearing data for ".count( $remove )." files\n" ); + $this->cache = array_diff_keys( $this->cache, $remove ); + $this->notFound = array_diff_keys( $this->notFound, $remove ); + } + + /** + * Mark some DB keys as nonexistent. This is a fairly low-level + * function, which most users should not need to call. + * @param array $dbkeys array of DB keys + */ + function markNotFound( $dbkeys ) { + wfDebug( "FileCache marking ".count( $dbkeys )." files as not found\n" ); + $this->notFound += array_fill_keys( $dbkeys, true ); + } + + + /** + * Search the cache for a file. + * @param mixed $title Title object or string + * @return File object or false if it is not found + * @todo Implement searching for old file versions(?) + */ + function findFile( $title ) { + if( !( $title instanceof Title ) ) { + $title = Title::makeTitleSafe( NS_FILE, $title ); + } + if( !$title ) { + return false; // invalid title? + } + + $dbkey = $title->getDBkey(); + if( array_key_exists( $dbkey, $this->cache ) ) { + wfDebug( "FileCache HIT for $dbkey\n" ); + return $this->cache[$dbkey]; + } + if( array_key_exists( $dbkey, $this->notFound ) ) { + wfDebug( "FileCache negative HIT for $dbkey\n" ); + return false; + } + + // Not in cache, fall back to a direct query + $file = $this->repoGroup->findFile( $title ); + if( $file ) { + wfDebug( "FileCache MISS for $dbkey\n" ); + $this->cache[$dbkey] = $file; + } else { + wfDebug( "FileCache negative MISS for $dbkey\n" ); + $this->notFound[$dbkey] = true; + } + return $file; + } + + /** + * Search the cache for multiple files. + * @param array $titles Title objects or strings to search for + * @return array of File objects, indexed by DB key + */ + function findFiles( $titles ) { + $titleObjs = array(); + foreach ( $titles as $title ) { + if ( !( $title instanceof Title ) ) { + $title = Title::makeTitleSafe( NS_FILE, $title ); + } + if ( $title ) { + $titleObjs[$title->getDBkey()] = $title; + } + } + + $result = array_intersect_key( $this->cache, $titleObjs ); + + $unsure = array_diff_key( $titleObjs, $result, $this->notFound ); + if( $unsure ) { + wfDebug( "FileCache MISS for ".count( $unsure )." files out of ".count( $titleObjs )."...\n" ); + // XXX: We assume the array returned by findFiles() is + // indexed by DBkey; this appears to be true, but should + // be explicitly documented. + $found = $this->repoGroup->findFiles( $unsure ); + $result += $found; + $this->addFiles( $found ); + $this->markNotFound( array_keys( array_diff_key( $unsure, $found ) ) ); + } + + wfDebug( "FileCache found ".count( $result )." files out of ".count( $titleObjs )."\n" ); + return $result; + } +}