From 8d984cebb252a074bafd8d01e21a1c831a0e5aea Mon Sep 17 00:00:00 2001 From: addshore Date: Thu, 10 Mar 2016 13:07:35 +0000 Subject: [PATCH] Uncache things in WatchedItemStore::updateNotificationTimestamp When the notification timestamp for a LinkTarget is updated all items relating to that LinkTarget should be uncached!.. This also switches this class from a BagOStuff to a HashBagOStuff as this cache index is only maintained per process.. Change-Id: I5dc58e018a6a4a15903abc1e0b326b4220abc75e --- includes/WatchedItemStore.php | 32 +++++++++++++++---- .../WatchedItemStoreIntegrationTest.php | 2 +- .../includes/WatchedItemStoreUnitTest.php | 4 +-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/includes/WatchedItemStore.php b/includes/WatchedItemStore.php index 1aed8e01f2..721901f2ce 100644 --- a/includes/WatchedItemStore.php +++ b/includes/WatchedItemStore.php @@ -18,10 +18,18 @@ class WatchedItemStore { private $loadBalancer; /** - * @var BagOStuff + * @var HashBagOStuff */ private $cache; + /** + * @var array[] Looks like $cacheIndex[Namespace ID][Target DB Key][User Id] => 'key' + * The index is needed so that on mass changes all relevant items can be un-cached. + * For example: Clearing a users watchlist of all items or updating notification timestamps + * for all users watching a single target. + */ + private $cacheIndex = []; + /** * @var callable|null */ @@ -37,7 +45,7 @@ class WatchedItemStore { */ private static $instance; - public function __construct( LoadBalancer $loadBalancer, BagOStuff $cache ) { + public function __construct( LoadBalancer $loadBalancer, HashBagOStuff $cache ) { $this->loadBalancer = $loadBalancer; $this->cache = $cache; $this->deferredUpdatesAddCallableUpdateCallback = [ 'DeferredUpdates', 'addCallableUpdate' ]; @@ -121,14 +129,25 @@ class WatchedItemStore { } private function cache( WatchedItem $item ) { - $this->cache->set( - $this->getCacheKey( $item->getUser(), $item->getLinkTarget() ), - $item - ); + $user = $item->getUser(); + $target = $item->getLinkTarget(); + $key = $this->getCacheKey( $user, $target ); + $this->cache->set( $key, $item ); + $this->cacheIndex[$target->getNamespace()][$target->getDBkey()][$user->getId()] = $key; } private function uncache( User $user, LinkTarget $target ) { $this->cache->delete( $this->getCacheKey( $user, $target ) ); + unset( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()][$user->getId()] ); + } + + private function uncacheLinkTarget( LinkTarget $target ) { + if ( !isset( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()] ) ) { + return; + } + foreach ( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()] as $key ) { + $this->cache->delete( $key ); + } } /** @@ -350,6 +369,7 @@ class WatchedItemStore { 'wl_title' => $target->getDBkey(), ], $fname ); + $this->uncacheLinkTarget( $target ); } ); } diff --git a/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php b/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php index 9341fb89b6..e2ab512b29 100644 --- a/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php +++ b/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php @@ -57,7 +57,7 @@ class WatchedItemStoreIntegrationTest extends MediaWikiTestCase { ); $this->assertTrue( $store->resetNotificationTimestamp( $user, $title ) ); - $this->assertNull( $store->loadWatchedItem( $user, $title )->getNotificationTimestamp() ); + $this->assertNull( $store->getWatchedItem( $user, $title )->getNotificationTimestamp() ); } public function testDuplicateAllAssociatedEntries() { diff --git a/tests/phpunit/includes/WatchedItemStoreUnitTest.php b/tests/phpunit/includes/WatchedItemStoreUnitTest.php index 709b4b419f..fdb25455ee 100644 --- a/tests/phpunit/includes/WatchedItemStoreUnitTest.php +++ b/tests/phpunit/includes/WatchedItemStoreUnitTest.php @@ -31,10 +31,10 @@ class WatchedItemStoreUnitTest extends PHPUnit_Framework_TestCase { } /** - * @return PHPUnit_Framework_MockObject_MockObject|BagOStuff + * @return PHPUnit_Framework_MockObject_MockObject|HashBagOStuff */ private function getMockCache() { - $mock = $this->getMockBuilder( BagOStuff::class ) + $mock = $this->getMockBuilder( HashBagOStuff::class ) ->disableOriginalConstructor() ->getMock(); $mock->expects( $this->any() ) -- 2.20.1