Uncache things in WatchedItemStore::updateNotificationTimestamp
authoraddshore <addshorewiki@gmail.com>
Thu, 10 Mar 2016 13:07:35 +0000 (13:07 +0000)
committeraddshore <addshorewiki@gmail.com>
Mon, 14 Mar 2016 15:13:19 +0000 (15:13 +0000)
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
tests/phpunit/includes/WatchedItemStoreIntegrationTest.php
tests/phpunit/includes/WatchedItemStoreUnitTest.php

index 1aed8e0..721901f 100644 (file)
@@ -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 );
                                }
                        );
                }
index 9341fb8..e2ab512 100644 (file)
@@ -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() {
index 709b4b4..fdb2545 100644 (file)
@@ -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() )