Make UserEditCountUpdate faster by using auto-commit mode
authorAaron Schulz <aschulz@wikimedia.org>
Sat, 27 Oct 2018 14:34:52 +0000 (07:34 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Sat, 27 Oct 2018 20:52:45 +0000 (13:52 -0700)
Bug: T202715
Change-Id: I92c08694cb5e1c367809439cff42e33a56ff9878

includes/deferred/UserEditCountUpdate.php
includes/user/User.php

index 2a1205c..5194e4f 100644 (file)
@@ -66,48 +66,48 @@ class UserEditCountUpdate implements DeferrableUpdate, MergeableUpdate {
         * Purges the list of URLs passed to the constructor.
         */
        public function doUpdate() {
-               foreach ( $this->infoByUser as $userId => $info ) {
-                       $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
-                       $dbw = $lb->getConnection( DB_MASTER );
+               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
+               $dbw = $lb->getConnection( DB_MASTER );
 
-                       $dbw->startAtomic( __METHOD__ ); // define minimum row lock duration
-                       $dbw->update(
-                               'user',
-                               [ 'user_editcount=user_editcount+' . (int)$info['increment'] ],
-                               [ 'user_id' => $userId, 'user_editcount IS NOT NULL' ],
-                               __METHOD__
-                       );
-                       /** @var User[] $affectedInstances */
-                       $affectedInstances = $info['instances'];
-                       // Lazy initialization check...
-                       if ( $dbw->affectedRows() == 0 ) {
-                               // No rows will be "affected" if user_editcount is NULL.
-                               // Get the generic "replica" connection to see if it actually uses the master.
-                               $dbr = $lb->getConnection( DB_REPLICA );
-                               if ( $dbr !== $dbw ) {
-                                       // This method runs after the new revisions were committed.
-                                       // Wait for the replica to catch up so they will all be counted.
-                                       $dbr->flushSnapshot( __METHOD__ );
-                                       $lb->safeWaitForMasterPos( $dbr );
+               ( new AutoCommitUpdate( $dbw, __METHOD__, function () use ( $lb, $dbw ) {
+                       foreach ( $this->infoByUser as $userId => $info ) {
+                               $dbw->update(
+                                       'user',
+                                       [ 'user_editcount=user_editcount+' . (int)$info['increment'] ],
+                                       [ 'user_id' => $userId, 'user_editcount IS NOT NULL' ],
+                                       __METHOD__
+                               );
+                               /** @var User[] $affectedInstances */
+                               $affectedInstances = $info['instances'];
+                               // Lazy initialization check...
+                               if ( $dbw->affectedRows() == 0 ) {
+                                       // No rows will be "affected" if user_editcount is NULL.
+                                       // Check if the generic "replica" connection is not the master.
+                                       $dbr = $lb->getConnection( DB_REPLICA );
+                                       if ( $dbr !== $dbw ) {
+                                               // This method runs after the new revisions were committed.
+                                               // Wait for the replica to catch up so they will all be counted.
+                                               $dbr->flushSnapshot( __METHOD__ );
+                                               $lb->safeWaitForMasterPos( $dbr );
+                                       }
+                                       $affectedInstances[0]->initEditCountInternal();
                                }
-                               $affectedInstances[0]->initEditCountInternal();
-                       }
-                       $newCount = (int)$dbw->selectField(
-                               'user',
-                               [ 'user_editcount' ],
-                               [ 'user_id' => $userId ],
-                               __METHOD__
-                       );
-                       $dbw->endAtomic( __METHOD__ );
+                               $newCount = (int)$dbw->selectField(
+                                       'user',
+                                       [ 'user_editcount' ],
+                                       [ 'user_id' => $userId ],
+                                       __METHOD__
+                               );
 
-                       // Update the edit count in the instance caches. This is mostly useful
-                       // for maintenance scripts, where deferred updates might run immediately
-                       // and user instances might be reused for a long time.
-                       foreach ( $affectedInstances as $affectedInstance ) {
-                               $affectedInstance->setEditCountInternal( $newCount );
+                               // Update the edit count in the instance caches. This is mostly useful
+                               // for maintenance scripts, where deferred updates might run immediately
+                               // and user instances might be reused for a long time.
+                               foreach ( $affectedInstances as $affectedInstance ) {
+                                       $affectedInstance->setEditCountInternal( $newCount );
+                               }
+                               // Clear the edit count in user cache too
+                               $affectedInstances[0]->invalidateCache();
                        }
-                       // Clear the edit count in user cache too
-                       $affectedInstances[0]->invalidateCache();
-               }
+               } ) )->doUpdate();
        }
 }
index 9cd3ea1..43be06e 100644 (file)
@@ -5350,10 +5350,9 @@ class User implements IDBAccessObject, UserIdentity {
         *
         * This method should not be called outside User/UserEditCountUpdate
         *
-        * @param int $add Edits to add to the count from the revision table
         * @return int Number of edits
         */
-       public function initEditCountInternal( $add = 0 ) {
+       public function initEditCountInternal() {
                // Pull from a replica DB to be less cruel to servers
                // Accuracy isn't the point anyway here
                $dbr = wfGetDB( DB_REPLICA );
@@ -5366,13 +5365,15 @@ class User implements IDBAccessObject, UserIdentity {
                        [],
                        $actorWhere['joins']
                );
-               $count = $count + $add;
 
                $dbw = wfGetDB( DB_MASTER );
                $dbw->update(
                        'user',
                        [ 'user_editcount' => $count ],
-                       [ 'user_id' => $this->getId() ],
+                       [
+                               'user_id' => $this->getId(),
+                               'user_editcount IS NULL OR user_editcount < ' . (int)$count
+                       ],
                        __METHOD__
                );