Merge "Improve some queries ordering by rev_timestamp with actor migration READ_NEW"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 1 Nov 2018 06:28:26 +0000 (06:28 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 1 Nov 2018 06:28:26 +0000 (06:28 +0000)
1  2 
includes/specials/pagers/ContribsPager.php
includes/user/User.php

@@@ -224,6 -224,12 +224,12 @@@ class ContribsPager extends RangeChrono
                                if ( isset( $conds['orconds']['actor'] ) ) {
                                        // @todo: This will need changing when revision_comment_temp goes away
                                        $queryInfo['options']['USE INDEX']['temp_rev_user'] = 'actor_timestamp';
+                                       // Alias 'rev_timestamp' => 'revactor_timestamp' so "ORDER BY rev_timestamp" is interpreted to
+                                       // use revactor_timestamp instead.
+                                       $queryInfo['fields'] = array_merge(
+                                               array_diff( $queryInfo['fields'], [ 'rev_timestamp' ] ),
+                                               [ 'rev_timestamp' => 'revactor_timestamp' ]
+                                       );
                                } else {
                                        $queryInfo['options']['USE INDEX']['revision'] =
                                                isset( $conds['orconds']['userid'] ) ? 'user_timestamp' : 'usertext_timestamp';
                                $del .= ' ';
                        }
  
 -                      $diffHistLinks = $this->msg( 'parentheses' )
 -                              ->rawParams( $difftext . $this->messages['pipe-separator'] . $histlink )
 -                              ->escaped();
 +                      $diffHistLinks = Html::rawElement( 'ul',
 +                              [ 'class' => 'mw-changeslist-link-list' ],
 +                              Html::rawElement( 'li', [], $difftext ) .
 +                              Html::rawElement( 'li', [], $histlink )
 +                      );
  
                        # Tags, if any.
                        list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow(
diff --combined includes/user/User.php
@@@ -909,8 -909,6 +909,8 @@@ class User implements IDBAccessObject, 
         * @return int|null The corresponding user's ID, or null if user is nonexistent
         */
        public static function idFromName( $name, $flags = self::READ_NORMAL ) {
 +              // Don't explode on self::$idCacheByName[$name] if $name is not a string but e.g. a User object
 +              $name = (string)$name;
                $nt = Title::makeTitleSafe( NS_USER, $name );
                if ( is_null( $nt ) ) {
                        // Illegal name
                        $this->isAnon() ? [ 'user_ip' => $this->getName() ] : [ 'user_id' => $this->getId() ],
                        __METHOD__ );
                $rev = $timestamp ? Revision::loadFromTimestamp( $dbr, $utp, $timestamp ) : null;
 -              return [ [ 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ] ];
 +              return [
 +                      [
 +                              'wiki' => WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() ),
 +                              'link' => $utp->getLocalURL(),
 +                              'rev' => $rev
 +                      ]
 +              ];
        }
  
        /**
                        // and it is always for the same wiki, but we double-check here in
                        // case that changes some time in the future.
                        if ( count( $newMessageLinks ) === 1
 -                              && $newMessageLinks[0]['wiki'] === wfWikiID()
 +                              && WikiMap::isCurrentWikiId( $newMessageLinks[0]['wiki'] )
                                && $newMessageLinks[0]['rev']
                        ) {
                                /** @var Revision $newMessageRevision */
  
                        if ( $count === null ) {
                                // it has not been initialized. do so.
 -                              $count = $this->initEditCount();
 +                              $count = $this->initEditCountInternal();
                        }
                        $this->mEditCount = $count;
                }
                }
                $dbr = wfGetDB( DB_REPLICA );
                $actorWhere = ActorMigration::newMigration()->getWhere( $dbr, 'rev_user', $this );
+               $tsField = isset( $actorWhere['tables']['temp_rev_user'] )
+                       ? 'revactor_timestamp' : 'rev_timestamp';
                $time = $dbr->selectField(
                        [ 'revision' ] + $actorWhere['tables'],
-                       'rev_timestamp',
+                       $tsField,
                        [ $actorWhere['conds'] ],
                        __METHOD__,
-                       [ 'ORDER BY' => 'rev_timestamp ASC' ],
+                       [ 'ORDER BY' => "$tsField ASC" ],
                        $actorWhere['joins']
                );
                if ( !$time ) {
        }
  
        /**
 -       * Deferred version of incEditCountImmediate()
 -       *
 -       * This function, rather than incEditCountImmediate(), should be used for
 -       * most cases as it avoids potential deadlocks caused by concurrent editing.
 +       * Schedule a deferred update to update the user's edit count
         */
        public function incEditCount() {
 -              wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
 -                      function () {
 -                              $this->incEditCountImmediate();
 -                      },
 -                      __METHOD__
 +              if ( $this->isAnon() ) {
 +                      return; // sanity
 +              }
 +
 +              DeferredUpdates::addUpdate(
 +                      new UserEditCountUpdate( $this, 1 ),
 +                      DeferredUpdates::POSTSEND
                );
        }
  
        /**
 -       * Increment the user's edit-count field.
 -       * Will have no effect for anonymous users.
 -       * @since 1.26
 +       * This method should not be called outside User/UserEditCountUpdate
 +       *
 +       * @param int $count
         */
 -      public function incEditCountImmediate() {
 -              if ( $this->isAnon() ) {
 -                      return;
 -              }
 -
 -              $dbw = wfGetDB( DB_MASTER );
 -              // No rows will be "affected" if user_editcount is NULL
 -              $dbw->update(
 -                      'user',
 -                      [ 'user_editcount=user_editcount+1' ],
 -                      [ 'user_id' => $this->getId(), 'user_editcount IS NOT NULL' ],
 -                      __METHOD__
 -              );
 -              // Lazy initialization check...
 -              if ( $dbw->affectedRows() == 0 ) {
 -                      // Now here's a goddamn hack...
 -                      $dbr = wfGetDB( DB_REPLICA );
 -                      if ( $dbr !== $dbw ) {
 -                              // If we actually have a replica DB server, the count is
 -                              // at least one behind because the current transaction
 -                              // has not been committed and replicated.
 -                              $this->mEditCount = $this->initEditCount( 1 );
 -                      } else {
 -                              // But if DB_REPLICA is selecting the master, then the
 -                              // count we just read includes the revision that was
 -                              // just added in the working transaction.
 -                              $this->mEditCount = $this->initEditCount();
 -                      }
 -              } else {
 -                      if ( $this->mEditCount === null ) {
 -                              $this->getEditCount();
 -                              $dbr = wfGetDB( DB_REPLICA );
 -                              $this->mEditCount += ( $dbr !== $dbw ) ? 1 : 0;
 -                      } else {
 -                              $this->mEditCount++;
 -                      }
 -              }
 -              // Edit count in user cache too
 -              $this->invalidateCache();
 +      public function setEditCountInternal( $count ) {
 +              $this->mEditCount = $count;
        }
  
        /**
         * Initialize user_editcount from data out of the revision table
         *
 -       * @param int $add Edits to add to the count from the revision table
 +       * This method should not be called outside User/UserEditCountUpdate
 +       *
         * @return int Number of edits
         */
 -      protected function initEditCount( $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 );
                        [],
                        $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__
                );