Use batch inserts for watchlist
[lhc/web/wiklou.git] / includes / WatchedItem.php
index 9f2b498..93d6c0b 100644 (file)
@@ -41,8 +41,23 @@ class WatchedItem {
         */
        const CHECK_USER_RIGHTS = 1;
 
-       var $mTitle, $mUser, $mCheckRights;
-       private $loaded = false, $watched, $timestamp;
+       /** @var Title */
+       public $mTitle;
+
+       /** @var User */
+       public $mUser;
+
+       /** @var int */
+       public $mCheckRights;
+
+       /** @var bool */
+       private $loaded = false;
+
+       /** @var bool */
+       private $watched;
+
+       /** @var string */
+       private $timestamp;
 
        /**
         * Create a WatchedItem object with the given user and title
@@ -53,7 +68,9 @@ class WatchedItem {
         *     Pass either WatchedItem::IGNORE_USER_RIGHTS or WatchedItem::CHECK_USER_RIGHTS.
         * @return WatchedItem
         */
-       public static function fromUserTitle( $user, $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
+       public static function fromUserTitle( $user, $title,
+               $checkRights = WatchedItem::CHECK_USER_RIGHTS
+       ) {
                $wl = new WatchedItem;
                $wl->mUser = $user;
                $wl->mTitle = $title;
@@ -253,45 +270,61 @@ class WatchedItem {
        }
 
        /**
-        * Given a title and user (assumes the object is setup), add the watch to the database.
+        * @param WatchedItem[] $items
         * @return bool
         */
-       public function addWatch() {
-               wfProfileIn( __METHOD__ );
+       static public function batchAddWatch( array $items ) {
+               $section = new ProfileSection( __METHOD__ );
 
-               // Only loggedin user can have a watchlist
-               if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
-                       wfProfileOut( __METHOD__ );
+               if ( wfReadOnly() ) {
                        return false;
                }
 
-               // Use INSERT IGNORE to avoid overwriting the notification timestamp
-               // if there's already an entry for this page
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->insert( 'watchlist',
-                       array(
-                               'wl_user' => $this->getUserId(),
-                               'wl_namespace' => MWNamespace::getSubject( $this->getTitleNs() ),
-                               'wl_title' => $this->getTitleDBkey(),
+               $rows = array();
+               foreach ( $items as $item ) {
+                       // Only loggedin user can have a watchlist
+                       if ( $item->mUser->isAnon() || !$item->isAllowed( 'editmywatchlist' ) ) {
+                               continue;
+                       }
+                       $rows[] = array(
+                               'wl_user' => $item->getUserId(),
+                               'wl_namespace' => MWNamespace::getSubject( $item->getTitleNs() ),
+                               'wl_title' => $item->getTitleDBkey(),
+                               'wl_notificationtimestamp' => null,
+                       );
+                       // Every single watched page needs now to be listed in watchlist;
+                       // namespace:page and namespace_talk:page need separate entries:
+                       $rows[] = array(
+                               'wl_user' => $item->getUserId(),
+                               'wl_namespace' => MWNamespace::getTalk( $item->getTitleNs() ),
+                               'wl_title' => $item->getTitleDBkey(),
                                'wl_notificationtimestamp' => null
-                       ), __METHOD__, 'IGNORE' );
+                       );
+                       $item->watched = true;
+               }
 
-               // Every single watched page needs now to be listed in watchlist;
-               // namespace:page and namespace_talk:page need separate entries:
-               $dbw->insert( 'watchlist',
-                       array(
-                               'wl_user' => $this->getUserId(),
-                               'wl_namespace' => MWNamespace::getTalk( $this->getTitleNs() ),
-                               'wl_title' => $this->getTitleDBkey(),
-                               'wl_notificationtimestamp' => null
-                       ), __METHOD__, 'IGNORE' );
+               if ( !$rows ) {
+                       return false;
+               }
 
-               $this->watched = true;
+               $dbw = wfGetDB( DB_MASTER );
+               foreach( array_chunk( $rows, 100 ) 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' );
+               }
 
-               wfProfileOut( __METHOD__ );
                return true;
        }
 
+       /**
+        * Given a title and user (assumes the object is setup), add the watch to the database.
+        * @return bool
+        */
+       public function addWatch() {
+               return self::batchAddWatch( array( $this ) );
+       }
+
        /**
         * Same as addWatch, only the opposite.
         * @return bool
@@ -389,7 +422,13 @@ class WatchedItem {
                # Perform replace
                # Note that multi-row replace is very efficient for MySQL but may be inefficient for
                # some other DBMSes, mostly due to poor simulation by us
-               $dbw->replace( 'watchlist', array( array( 'wl_user', 'wl_namespace', 'wl_title' ) ), $values, __METHOD__ );
+               $dbw->replace(
+                       'watchlist',
+                       array( array( 'wl_user', 'wl_namespace', 'wl_title' ) ),
+                       $values,
+                       __METHOD__
+               );
+
                return true;
        }
 }