From 8b306154235b56bcbc74c03b6dabcbcbefbafb64 Mon Sep 17 00:00:00 2001 From: Ilmari Karonen Date: Sun, 14 Dec 2008 05:47:48 +0000 Subject: [PATCH] =?utf8?q?Add=20a=20new=20FileCache=20class=20to=20wrap=20?= =?utf8?q?RepoGroup::findFile()=20and=20findFiles(),=20and=20make=20wfFind?= =?utf8?q?File()=20use=20it=20by=20default.=20=20This=20should=20improve?= =?utf8?q?=20performance=20for=20pages=20that=20refer=20to=20the=20same=20?= =?utf8?q?image=20several=20times,=20but=20the=20real=20benefit=20is=20tha?= =?utf8?q?t=20it=20allows=20batch=20file=20existence=20checks,=20=C3=A0=20?= =?utf8?q?la=20LinkBatch,=20by=20collecting=20a=20set=20of=20titles=20(or?= =?utf8?q?=20DB=20keys)=20and=20calling=20FileCache::findFiles()=20on=20th?= =?utf8?q?em=20to=20prefill=20the=20cache.=20XXX:=20The=20code=20seems=20t?= =?utf8?q?o=20more=20or=20less=20work,=20but=20it=20obviously=20needs=20mo?= =?utf8?q?re=20testing,=20regarding=20both=20stability=20and=20memory=20us?= =?utf8?q?age.=20=20In=20particular,=20I=20have=20not=20tested=20file=20up?= =?utf8?q?loads=20yet=20--=20there=20may=20be=20consistency=20issues=20the?= =?utf8?q?re.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- RELEASE-NOTES | 1 + includes/AutoLoader.php | 1 + includes/GlobalFunctions.php | 9 +- includes/filerepo/FileCache.php | 156 ++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 includes/filerepo/FileCache.php 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; + } +} -- 2.20.1