X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/membres/fiche.php?a=blobdiff_plain;f=includes%2Fwatcheditem%2FWatchedItemStore.php;h=85668959d0338944ca3e18a8070e255c80442f10;hb=70d9fbb0bfacaf837b7e8efb3770245d646ab522;hp=1b92f51b424c215cef66eb48a375c958b490afd7;hpb=d3ed3b8409021271d05189fc0b55cc63d0fe2e85;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/watcheditem/WatchedItemStore.php b/includes/watcheditem/WatchedItemStore.php index 1b92f51b42..85668959d0 100644 --- a/includes/watcheditem/WatchedItemStore.php +++ b/includes/watcheditem/WatchedItemStore.php @@ -3,9 +3,9 @@ use Wikimedia\Rdbms\IDatabase; use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface; use MediaWiki\Linker\LinkTarget; -use MediaWiki\MediaWikiServices; use Wikimedia\Assert\Assert; use Wikimedia\ScopedCallback; +use Wikimedia\Rdbms\ILBFactory; use Wikimedia\Rdbms\LoadBalancer; /** @@ -18,6 +18,11 @@ use Wikimedia\Rdbms\LoadBalancer; */ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterface { + /** + * @var ILBFactory + */ + private $lbFactory; + /** * @var LoadBalancer */ @@ -62,18 +67,19 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac private $stats; /** - * @param LoadBalancer $loadBalancer + * @param ILBFactory $lbFactory * @param HashBagOStuff $cache * @param ReadOnlyMode $readOnlyMode * @param int $updateRowsPerQuery */ public function __construct( - LoadBalancer $loadBalancer, + ILBFactory $lbFactory, HashBagOStuff $cache, ReadOnlyMode $readOnlyMode, $updateRowsPerQuery ) { - $this->loadBalancer = $loadBalancer; + $this->lbFactory = $lbFactory; + $this->loadBalancer = $lbFactory->getMainLB(); $this->cache = $cache; $this->readOnlyMode = $readOnlyMode; $this->stats = new NullStatsdDataFactory(); @@ -361,6 +367,50 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac return $visitingWatchers; } + /** + * @param User $user + * @param TitleValue[] $titles + * @return bool + * @throws MWException + */ + public function removeWatchBatchForUser( User $user, array $titles ) { + if ( $this->readOnlyMode->isReadOnly() ) { + return false; + } + if ( $user->isAnon() ) { + return false; + } + if ( !$titles ) { + return true; + } + + $rows = $this->getTitleDbKeysGroupedByNamespace( $titles ); + $this->uncacheTitlesForUser( $user, $titles ); + + $dbw = $this->getConnectionRef( DB_MASTER ); + $ticket = count( $titles ) > $this->updateRowsPerQuery ? + $this->lbFactory->getEmptyTransactionTicket( __METHOD__ ) : null; + $affectedRows = 0; + + // Batch delete items per namespace. + foreach ( $rows as $namespace => $namespaceTitles ) { + $rowBatches = array_chunk( $namespaceTitles, $this->updateRowsPerQuery ); + foreach ( $rowBatches as $toDelete ) { + $dbw->delete( 'watchlist', [ + 'wl_user' => $user->getId(), + 'wl_namespace' => $namespace, + 'wl_title' => $toDelete + ], __METHOD__ ); + $affectedRows += $dbw->affectedRows(); + if ( $ticket ) { + $this->lbFactory->commitAndWaitForReplication( __METHOD__, $ticket ); + } + } + } + + return (bool)$affectedRows; + } + /** * @since 1.27 * @param LinkTarget[] $targets @@ -649,6 +699,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac * @since 1.27 * @param User $user * @param LinkTarget $target + * @throws MWException */ public function addWatch( User $user, LinkTarget $target ) { $this->addWatchBatchForUser( $user, [ $target ] ); @@ -659,12 +710,13 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac * @param User $user * @param LinkTarget[] $targets * @return bool + * @throws MWException */ public function addWatchBatchForUser( User $user, array $targets ) { if ( $this->readOnlyMode->isReadOnly() ) { return false; } - // Only loggedin user can have a watchlist + // Only logged-in user can have a watchlist if ( $user->isAnon() ) { return false; } @@ -691,10 +743,18 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac } $dbw = $this->getConnectionRef( DB_MASTER ); - foreach ( array_chunk( $rows, 100 ) as $toInsert ) { + $ticket = count( $targets ) > $this->updateRowsPerQuery ? + $this->lbFactory->getEmptyTransactionTicket( __METHOD__ ) : null; + $affectedRows = 0; + $rowBatches = array_chunk( $rows, $this->updateRowsPerQuery ); + foreach ( $rowBatches as $toInsert ) { // Use INSERT IGNORE to avoid overwriting the notification timestamp // if there's already an entry for this page $dbw->insert( 'watchlist', $toInsert, __METHOD__, 'IGNORE' ); + $affectedRows += $dbw->affectedRows(); + if ( $ticket ) { + $this->lbFactory->commitAndWaitForReplication( __METHOD__, $ticket ); + } } // Update process cache to ensure skin doesn't claim that the current // page is unwatched in the response of action=watch itself (T28292). @@ -703,7 +763,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac $this->cache( $item ); } - return true; + return (bool)$affectedRows; } /** @@ -711,26 +771,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac * @param User $user * @param LinkTarget $target * @return bool + * @throws MWException */ public function removeWatch( User $user, LinkTarget $target ) { - // Only logged in user can have a watchlist - if ( $this->readOnlyMode->isReadOnly() || $user->isAnon() ) { - return false; - } - - $this->uncache( $user, $target ); - - $dbw = $this->getConnectionRef( DB_MASTER ); - $dbw->delete( 'watchlist', - [ - 'wl_user' => $user->getId(), - 'wl_namespace' => $target->getNamespace(), - 'wl_title' => $target->getDBkey(), - ], __METHOD__ - ); - $success = (bool)$dbw->affectedRows(); - - return $success; + return $this->removeWatchBatchForUser( $user, [ $target ] ); } /** @@ -819,13 +863,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac $fname = __METHOD__; DeferredUpdates::addCallableUpdate( function () use ( $timestamp, $watchers, $target, $fname ) { - global $wgUpdateRowsPerQuery; - $dbw = $this->getConnectionRef( DB_MASTER ); - $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); - $ticket = $factory->getEmptyTransactionTicket( $fname ); + $ticket = $this->lbFactory->getEmptyTransactionTicket( $fname ); - $watchersChunks = array_chunk( $watchers, $wgUpdateRowsPerQuery ); + $watchersChunks = array_chunk( $watchers, $this->updateRowsPerQuery ); foreach ( $watchersChunks as $watchersChunk ) { $dbw->update( 'watchlist', [ /* SET */ @@ -837,7 +878,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac ], $fname ); if ( count( $watchersChunks ) > 1 ) { - $factory->commitAndWaitForReplication( + $this->lbFactory->commitAndWaitForReplication( $fname, $ticket, [ 'domain' => $dbw->getDomainID() ] ); } @@ -866,6 +907,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac return false; } + if ( ! Hooks::run( 'BeforeResetNotificationTimestamp', [ &$user, &$title, $force, &$oldid ] ) ) { + return false; + } + $item = null; if ( $force != 'force' ) { $item = $this->loadWatchedItem( $user, $title ); @@ -1041,4 +1086,27 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac } } + /** + * @param TitleValue[] $titles + * @return array + */ + private function getTitleDbKeysGroupedByNamespace( array $titles ) { + $rows = []; + foreach ( $titles as $title ) { + // Group titles by namespace. + $rows[ $title->getNamespace() ][] = $title->getDBkey(); + } + return $rows; + } + + /** + * @param User $user + * @param Title[] $titles + */ + private function uncacheTitlesForUser( User $user, array $titles ) { + foreach ( $titles as $title ) { + $this->uncache( $user, $title ); + } + } + }