Merge "Make updateCategoryCounts() have better lag checks"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 28 Oct 2016 21:28:40 +0000 (21:28 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 28 Oct 2016 21:28:40 +0000 (21:28 +0000)
1  2 
includes/page/WikiPage.php

@@@ -2399,10 -2399,6 +2399,10 @@@ class WikiPage implements Page, IDBAcce
                } elseif ( $options['changed'] ) { // bug 50785
                        self::onArticleEdit( $this->mTitle, $revision );
                }
 +
 +              ResourceLoaderWikiModule::invalidateModuleCache(
 +                      $this->mTitle, $options['oldrevision'], $revision, wfWikiID()
 +              );
        }
  
        /**
                // unless they actually try to catch exceptions (which is rare).
  
                // we need to remember the old content so we can use it to generate all deletion updates.
 +              $revision = $this->getRevision();
                try {
                        $content = $this->getContent( Revision::RAW );
                } catch ( Exception $ex ) {
                        $content = null;
                }
  
 +              $fields = Revision::selectFields();
 +              $bitfield = false;
 +
                // Bitfields to further suppress the content
                if ( $suppress ) {
 -                      $bitfield = 0;
 -                      // This should be 15...
 -                      $bitfield |= Revision::DELETED_TEXT;
 -                      $bitfield |= Revision::DELETED_COMMENT;
 -                      $bitfield |= Revision::DELETED_USER;
 -                      $bitfield |= Revision::DELETED_RESTRICTED;
 -                      $deletionFields = [ $dbw->addQuotes( $bitfield ) . ' AS deleted' ];
 -              } else {
 -                      $deletionFields = [ 'rev_deleted AS deleted' ];
 +                      $bitfield = Revision::SUPPRESSED_ALL;
 +                      $fields = array_diff( $fields, [ 'rev_deleted' ] );
                }
  
                // For now, shunt the revision data into the archive table.
                // the rev_deleted field, which is reserved for this purpose.
  
                // Get all of the page revisions
 -              $fields = array_diff( Revision::selectFields(), [ 'rev_deleted' ] );
                $res = $dbw->select(
                        'revision',
 -                      array_merge( $fields, $deletionFields ),
 +                      $fields,
                        [ 'rev_page' => $id ],
                        __METHOD__,
                        'FOR UPDATE'
                                'ar_flags'      => '',
                                'ar_len'        => $row->rev_len,
                                'ar_page_id'    => $id,
 -                              'ar_deleted'    => $row->deleted,
 +                              'ar_deleted'    => $suppress ? $bitfield : $row->rev_deleted,
                                'ar_sha1'       => $row->rev_sha1,
                        ];
                        if ( $wgContentHandlerUseDB ) {
  
                $dbw->endAtomic( __METHOD__ );
  
 -              $this->doDeleteUpdates( $id, $content );
 +              $this->doDeleteUpdates( $id, $content, $revision );
  
                Hooks::run( 'ArticleDeleteComplete', [
                        &$wikiPageBeforeDelete,
         * Do some database updates after deletion
         *
         * @param int $id The page_id value of the page being deleted
 -       * @param Content $content Optional page content to be used when determining
 +       * @param Content|null $content Optional page content to be used when determining
         *   the required updates. This may be needed because $this->getContent()
         *   may already return null when the page proper was deleted.
 +       * @param Revision|null $revision The latest page revision
         */
 -      public function doDeleteUpdates( $id, Content $content = null ) {
 +      public function doDeleteUpdates( $id, Content $content = null, Revision $revision = null ) {
                try {
                        $countable = $this->isCountable();
                } catch ( Exception $ex ) {
  
                // Clear caches
                WikiPage::onArticleDelete( $this->mTitle );
 +              ResourceLoaderWikiModule::invalidateModuleCache(
 +                      $this->mTitle, $revision, null, wfWikiID()
 +              );
  
                // Reset this object and the Title object
                $this->loadFromRow( false, self::READ_LATEST );
         * Update all the appropriate counts in the category table, given that
         * we've added the categories $added and deleted the categories $deleted.
         *
+        * This should only be called from deferred updates or jobs to avoid contention.
+        *
         * @param array $added The names of categories that were added
         * @param array $deleted The names of categories that were deleted
         * @param integer $id Page ID (this should be the original deleted page ID)
         */
        public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
                $id = $id ?: $this->getId();
+               $ns = $this->getTitle()->getNamespace();
+               $addFields = [ 'cat_pages = cat_pages + 1' ];
+               $removeFields = [ 'cat_pages = cat_pages - 1' ];
+               if ( $ns == NS_CATEGORY ) {
+                       $addFields[] = 'cat_subcats = cat_subcats + 1';
+                       $removeFields[] = 'cat_subcats = cat_subcats - 1';
+               } elseif ( $ns == NS_FILE ) {
+                       $addFields[] = 'cat_files = cat_files + 1';
+                       $removeFields[] = 'cat_files = cat_files - 1';
+               }
                $dbw = wfGetDB( DB_MASTER );
-               $method = __METHOD__;
-               // Do this at the end of the commit to reduce lock wait timeouts
-               $dbw->onTransactionPreCommitOrIdle(
-                       function () use ( $dbw, $added, $deleted, $id, $method ) {
-                               $ns = $this->getTitle()->getNamespace();
-                               $addFields = [ 'cat_pages = cat_pages + 1' ];
-                               $removeFields = [ 'cat_pages = cat_pages - 1' ];
-                               if ( $ns == NS_CATEGORY ) {
-                                       $addFields[] = 'cat_subcats = cat_subcats + 1';
-                                       $removeFields[] = 'cat_subcats = cat_subcats - 1';
-                               } elseif ( $ns == NS_FILE ) {
-                                       $addFields[] = 'cat_files = cat_files + 1';
-                                       $removeFields[] = 'cat_files = cat_files - 1';
-                               }
  
-                               if ( count( $added ) ) {
-                                       $existingAdded = $dbw->selectFieldValues(
-                                               'category',
-                                               'cat_title',
-                                               [ 'cat_title' => $added ],
-                                               $method
-                                       );
+               if ( count( $added ) ) {
+                       $existingAdded = $dbw->selectFieldValues(
+                               'category',
+                               'cat_title',
+                               [ 'cat_title' => $added ],
+                               __METHOD__
+                       );
  
-                                       // For category rows that already exist, do a plain
-                                       // UPDATE instead of INSERT...ON DUPLICATE KEY UPDATE
-                                       // to avoid creating gaps in the cat_id sequence.
-                                       if ( count( $existingAdded ) ) {
-                                               $dbw->update(
-                                                       'category',
-                                                       $addFields,
-                                                       [ 'cat_title' => $existingAdded ],
-                                                       $method
-                                               );
-                                       }
+                       // For category rows that already exist, do a plain
+                       // UPDATE instead of INSERT...ON DUPLICATE KEY UPDATE
+                       // to avoid creating gaps in the cat_id sequence.
+                       if ( count( $existingAdded ) ) {
+                               $dbw->update(
+                                       'category',
+                                       $addFields,
+                                       [ 'cat_title' => $existingAdded ],
+                                       __METHOD__
+                               );
+                       }
  
-                                       $missingAdded = array_diff( $added, $existingAdded );
-                                       if ( count( $missingAdded ) ) {
-                                               $insertRows = [];
-                                               foreach ( $missingAdded as $cat ) {
-                                                       $insertRows[] = [
-                                                               'cat_title'   => $cat,
-                                                               'cat_pages'   => 1,
-                                                               'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
-                                                               'cat_files'   => ( $ns == NS_FILE ) ? 1 : 0,
-                                                       ];
-                                               }
-                                               $dbw->upsert(
-                                                       'category',
-                                                       $insertRows,
-                                                       [ 'cat_title' ],
-                                                       $addFields,
-                                                       $method
-                                               );
-                                       }
+                       $missingAdded = array_diff( $added, $existingAdded );
+                       if ( count( $missingAdded ) ) {
+                               $insertRows = [];
+                               foreach ( $missingAdded as $cat ) {
+                                       $insertRows[] = [
+                                               'cat_title'   => $cat,
+                                               'cat_pages'   => 1,
+                                               'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
+                                               'cat_files'   => ( $ns == NS_FILE ) ? 1 : 0,
+                                       ];
                                }
+                               $dbw->upsert(
+                                       'category',
+                                       $insertRows,
+                                       [ 'cat_title' ],
+                                       $addFields,
+                                       __METHOD__
+                               );
+                       }
+               }
  
-                               if ( count( $deleted ) ) {
-                                       $dbw->update(
-                                               'category',
-                                               $removeFields,
-                                               [ 'cat_title' => $deleted ],
-                                               $method
-                                       );
-                               }
+               if ( count( $deleted ) ) {
+                       $dbw->update(
+                               'category',
+                               $removeFields,
+                               [ 'cat_title' => $deleted ],
+                               __METHOD__
+                       );
+               }
  
-                               foreach ( $added as $catName ) {
-                                       $cat = Category::newFromName( $catName );
-                                       Hooks::run( 'CategoryAfterPageAdded', [ $cat, $this ] );
-                               }
+               foreach ( $added as $catName ) {
+                       $cat = Category::newFromName( $catName );
+                       Hooks::run( 'CategoryAfterPageAdded', [ $cat, $this ] );
+               }
  
-                               foreach ( $deleted as $catName ) {
-                                       $cat = Category::newFromName( $catName );
-                                       Hooks::run( 'CategoryAfterPageRemoved', [ $cat, $this, $id ] );
-                               }
+               foreach ( $deleted as $catName ) {
+                       $cat = Category::newFromName( $catName );
+                       Hooks::run( 'CategoryAfterPageRemoved', [ $cat, $this, $id ] );
+               }
  
-                               // Refresh counts on categories that should be empty now, to
-                               // trigger possible deletion. Check master for the most
-                               // up-to-date cat_pages.
-                               if ( count( $deleted ) ) {
-                                       $rows = $dbw->select(
-                                               'category',
-                                               [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
-                                               [ 'cat_title' => $deleted, 'cat_pages <= 0' ],
-                                               $method
-                                       );
-                                       foreach ( $rows as $row ) {
-                                               $cat = Category::newFromRow( $row );
-                                               $cat->refreshCounts();
-                                       }
-                               }
-                       },
-                       __METHOD__
-               );
+               // Refresh counts on categories that should be empty now, to
+               // trigger possible deletion. Check master for the most
+               // up-to-date cat_pages.
+               if ( count( $deleted ) ) {
+                       $rows = $dbw->select(
+                               'category',
+                               [ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
+                               [ 'cat_title' => $deleted, 'cat_pages <= 0' ],
+                               __METHOD__
+                       );
+                       foreach ( $rows as $row ) {
+                               $cat = Category::newFromRow( $row );
+                               $cat->refreshCounts();
+                       }
+               }
        }
  
        /**