X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fwatcheditem%2FWatchedItemStore.php;h=f9435a1b0e7947129af2f7f289d1ddc6def1a5c7;hb=f57c8f9da04b42361beb1d396f5d0f614a4895bb;hp=6b0c2aab1104b371f56292d21a09f56ed2328933;hpb=1de7117197761961736ea43b237599569eee93a2;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/watcheditem/WatchedItemStore.php b/includes/watcheditem/WatchedItemStore.php index 6b0c2aab11..f9435a1b0e 100644 --- a/includes/watcheditem/WatchedItemStore.php +++ b/includes/watcheditem/WatchedItemStore.php @@ -3,9 +3,10 @@ use Wikimedia\Rdbms\IDatabase; use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface; use MediaWiki\Linker\LinkTarget; -use MediaWiki\MediaWikiServices; use Wikimedia\Assert\Assert; +use Wikimedia\Rdbms\LBFactory; use Wikimedia\ScopedCallback; +use Wikimedia\Rdbms\ILBFactory; use Wikimedia\Rdbms\LoadBalancer; /** @@ -18,6 +19,11 @@ use Wikimedia\Rdbms\LoadBalancer; */ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterface { + /** + * @var ILBFactory + */ + private $lbFactory; + /** * @var LoadBalancer */ @@ -62,18 +68,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 +368,47 @@ 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 = $this->lbFactory->getEmptyTransactionTicket( __METHOD__ ); + $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(); + $this->lbFactory->commitAndWaitForReplication( __METHOD__, $ticket ); + } + } + + return (bool)$affectedRows; + } + /** * @since 1.27 * @param LinkTarget[] $targets @@ -649,6 +697,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,6 +708,7 @@ 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() ) { @@ -691,10 +741,15 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac } $dbw = $this->getConnectionRef( DB_MASTER ); - foreach ( array_chunk( $rows, 100 ) as $toInsert ) { + $ticket = $this->lbFactory->getEmptyTransactionTicket( __METHOD__ ); + $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(); + $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 +758,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac $this->cache( $item ); } - return true; + return (bool)$affectedRows; } /** @@ -711,26 +766,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 +858,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( __METHOD__ ); + $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,8 +873,8 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac ], $fname ); if ( count( $watchersChunks ) > 1 ) { - $factory->commitAndWaitForReplication( - __METHOD__, $ticket, [ 'domain' => $dbw->getDomainID() ] + $this->lbFactory->commitAndWaitForReplication( + $fname, $ticket, [ 'domain' => $dbw->getDomainID() ] ); } } @@ -1041,4 +1077,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 ); + } + } + }