Cenarium has uploaded a new change for review. https://gerrit.wikimedia.org/r/218265
Change subject: Overhaul caching of tag usage statistics ...................................................................... Overhaul caching of tag usage statistics Caching is overhauled so that we can have a cached list of tags applied at least once for T27909. A short term, reactive cache is used for the statistics displayed at Special:Tags, which is purged every time tags are updated, unless the tag has more hits than a number specified in config (so that tags like mobile edit don't cause cache purges every few second). A longer term, stable cache is introduced for drop down menus, which is purged only when a tag is added for the first time or when deleting tags. Change-Id: I1b6838b975917fadc2c60998cce26ab490f22a31 --- M includes/DefaultSettings.php M includes/changetags/ChangeTags.php M includes/changetags/ChangeTagsContext.php 3 files changed, 96 insertions(+), 9 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/65/218265/1 diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 24c910f..8cbe4c7 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -6162,6 +6162,13 @@ $wgTagUsageCacheDuration = 60*60*24; /** + * Set this to a positive integer and tags with more than this many hits + * will not trigger a cache purge when applied. This means they won't be + * updated, but also less db queries and faster loading of Special:Tags. + */ +$wgTagMaxHitcountUpdate = 0; + +/** * Expiry to use for the caching of tags registered by extensions * 24 hours by default */ diff --git a/includes/changetags/ChangeTags.php b/includes/changetags/ChangeTags.php index e5b7e14..271600e 100644 --- a/includes/changetags/ChangeTags.php +++ b/includes/changetags/ChangeTags.php @@ -238,7 +238,7 @@ $dbw->delete( 'change_tag', $conds, __METHOD__ ); } } - ChangeTagsContext::purgeTagUsageCache(); + ChangeTagsContext::clearCachesAfterUpdate( $tagsToAdd, $tagsToRemove ); return array( $tagsToAdd, $tagsToRemove, $prevTags ); } @@ -631,7 +631,7 @@ ) { global $wgUseTagFilter; - $tagList = ChangeTagsContext::tagStats(); + $tagList = ChangeTagsContext::cachedTagStats(); // check config and if the list of tags is not empty if ( !$wgUseTagFilter || !count( $tagList ) ) { return $fullForm ? '' : array(); diff --git a/includes/changetags/ChangeTagsContext.php b/includes/changetags/ChangeTagsContext.php index d5c8ee3..5d5c898 100644 --- a/includes/changetags/ChangeTagsContext.php +++ b/includes/changetags/ChangeTagsContext.php @@ -215,7 +215,9 @@ * Does not include tags defined somewhere but not applied * * The result is cached, and the cache is invalidated every time an - * operation on change_tag is performed. + * operation on change_tag is performed unless $wgMaxTagHitcountUpdate + * is > 0. In that case, tags with a greater hitcount do not trigger + * a cache purge and therefore are not updated. * The cache expires after 24 hours by default ($wgTagUsageCacheDuration). * * @return array Array of tags mapped to their hitcount @@ -246,15 +248,92 @@ return $changeTags; }; - $key = wfMemcKey( 'ChangeTags', 'tag-stats' ); + $keyReactive = wfMemcKey( 'ChangeTags', 'tag-stats-reactive' ); return ObjectCache::getMainWANInstance()->getWithSetCallback( - $key, + $keyReactive, $callBack, $wgTagUsageCacheDuration, - array( $key ), + array( $keyReactive, $keyStable ), array( 'lockTSE' => INF ) ); + } + + /** + * Returns a map of any tags used on the wiki to number of edits + * tagged with them, ordered descending by the hitcount as of the + * latest caching. + * Does not include tags defined somewhere but not applied + * + * This cache is invalidated only for first hits of a tag. + * Updates may be delayed by up to 48 hours by default + * (twice $wgTagUsageCacheDuration). + * + * @return array Array of tags mapped to their hitcount + * @since 1.26 + */ + public static function cachedTagStats() { + global $wgTagUsageCacheDuration; + $keyStable = wfMemcKey( 'ChangeTags', 'tag-stats-stable' ); + + $callBack = function () { + return self::tagStats(); + }; + + return ObjectCache::getMainWANInstance()->getWithSetCallback( + $keyStable, + $callBack, + $wgTagUsageCacheDuration, + array( $keyStable ), + array( 'lockTSE' => INF ) + ); + } + + /** + * Clear caches after tags have been updated + * This should be called after writes on the change_tag table. + * + * @param array $tagsToAdd: tags that were added + * @param array $tagsToRemove: tags that were removed + * @since 1.26 + */ + public static function clearCachesAfterUpdate( $tagsToAdd, $tagsToRemove ) { + global $wgTagMaxHitcountUpdate; + $cache = ObjectCache::getMainWANInstance(); + $key = wfMemcKey( 'ChangeTags', 'tag-stats-reactive' ); + + // Retrieve cached stats + $stats = $cache->get( $key, $ttl ); + + // We do the basic purge of the reactive cache unless all of the added tags + // have more hits than $wgTagMaxHitcountUpdate. + $doBasicPurge = true; + // If the reactive cache was no longer valid, or one of the added tags doesn't + // appear in it, we purge the stable cache too since it might be a newly + // defined tag applied for the first time. + // We also purge the cache of extensions since they might not have purged it + // and we don't want the tag to appear out of nowhere at Special:Tags. + $doExtraPurge = ( $ttl === null ) || ( $ttl < 0 ); + if ( count( $tagsToAdd ) && ( $wgTagMaxHitcountUpdate > 0 || !$doExtraPurge ) ) { + foreach ( $tagsToAdd as $tag ) { + if ( !isset( $stats[$tag] ) ) { + $doBasicPurge = true; + $doExtraPurge = true; + break; + } elseif ( $stats[$tag] > $wgTagMaxHitcountUpdate ) { + $doBasicPurge = false; + } else { + $doBasicPurge = true; + } + } + } + if ( $doBasicPurge ) { + $cache->touchCheckKey( $key ); + if ( $doExtraPurge ) { + $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'tag-stats-stable' ) ); + $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'valid-tags-hook' ) ); + } + } } /** @@ -284,14 +363,14 @@ } /** - * Invalidates the cache of tag usage stats. + * Invalidates the reactive cache of tag usage stats. * This should be called when we really need the up to date stats (e.g. deletion). * * @since 1.26 */ public static function purgeTagUsageCache() { $cache = ObjectCache::getMainWANInstance(); - $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'tag-stats' ) ); + $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'tag-stats-reactive' ) ); } /** @@ -304,6 +383,7 @@ $cache = ObjectCache::getMainWANInstance(); $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'valid-tags-db' ) ); $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'valid-tags-hook' ) ); - $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'tag-stats' ) ); + $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'tag-stats-reactive' ) ); + $cache->touchCheckKey( wfMemcKey( 'ChangeTags', 'tag-stats-stable' ) ); } } -- To view, visit https://gerrit.wikimedia.org/r/218265 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I1b6838b975917fadc2c60998cce26ab490f22a31 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Cenarium <cenarium.sy...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits