<?php
/**
* See docs/deferred.txt
- *
+ *
* @todo document (e.g. one-sentence top-level class description).
*/
class LinksUpdate {
$mProperties, //!< Map of arbitrary name to value
$mDb, //!< Database connection reference
$mOptions, //!< SELECT options to be used (array)
- $mRecursive; //!< Whether to queue jobs for recursive updates
+ $mRecursive, //!< Whether to queue jobs for recursive updates
+ $mTouchTmplLinks; //!< Whether to queue HTMLCacheUpdate jobs IF recursive
/**@}}*/
/**
$this->mTitle = $title;
$this->mId = $title->getArticleID();
+ $this->mParserOutput = $parserOutput;
$this->mLinks = $parserOutput->getLinks();
$this->mImages = $parserOutput->getImages();
$this->mTemplates = $parserOutput->getTemplates();
$this->mProperties = $parserOutput->getProperties();
# Convert the format of the interlanguage links
- # I didn't want to change it in the ParserOutput, because that array is passed all
- # the way back to the skin, so either a skin API break would be required, or an
+ # I didn't want to change it in the ParserOutput, because that array is passed all
+ # the way back to the skin, so either a skin API break would be required, or an
# inefficient back-conversion.
$ill = $parserOutput->getLanguageLinks();
$this->mInterlangs = array();
}
$this->mRecursive = $recursive;
-
+ $this->mTouchTmplLinks = false;
+
wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
}
+
+ /**
+ * Invalidate HTML cache of pages that include this page?
+ */
+ public function setRecursiveTouch( $val ) {
+ $this->mTouchTmplLinks = (bool)$val;
+ if( $val ) // Cannot invalidate without queueRecursiveJobs()
+ $this->mRecursive = true;
+ }
/**
* Update link tables with outgoing links from an updated article
*/
- function doUpdate() {
+ public function doUpdate() {
global $wgUseDumbLinkUpdate;
-
+
wfRunHooks( 'LinksUpdate', array( &$this ) );
if ( $wgUseDumbLinkUpdate ) {
$this->doDumbUpdate();
}
- function doIncrementalUpdate() {
+ protected function doIncrementalUpdate() {
wfProfileIn( __METHOD__ );
-
+
# Page links
$existing = $this->getExistingLinks();
$this->incrTableUpdate( 'pagelinks', 'pl', $this->getLinkDeletions( $existing ),
# Image links
$existing = $this->getExistingImages();
- $this->incrTableUpdate( 'imagelinks', 'il', $this->getImageDeletions( $existing ),
- $this->getImageInsertions( $existing ) );
+
+ $imageDeletes = $this->getImageDeletions( $existing );
+ $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes, $this->getImageInsertions( $existing ) );
# Invalidate all image description pages which had links added or removed
- $imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing );
+ $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
$this->invalidateImageDescriptions( $imageUpdates );
# External links
# Category links
$existing = $this->getExistingCategories();
- $this->incrTableUpdate( 'categorylinks', 'cl', $this->getCategoryDeletions( $existing ),
- $this->getCategoryInsertions( $existing ) );
+
+ $categoryDeletes = $this->getCategoryDeletions( $existing );
+
+ $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes, $this->getCategoryInsertions( $existing ) );
# Invalidate all categories which were added, deleted or changed (set symmetric difference)
$categoryInserts = array_diff_assoc( $this->mCategories, $existing );
- $categoryDeletes = array_diff_assoc( $existing, $this->mCategories );
$categoryUpdates = $categoryInserts + $categoryDeletes;
$this->invalidateCategories( $categoryUpdates );
$this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
# Page properties
$existing = $this->getExistingProperties();
- $this->incrTableUpdate( 'page_props', 'pp', $this->getPropertyDeletions( $existing ),
- $this->getPropertyInsertions( $existing ) );
+
+ $propertiesDeletes = $this->getPropertyDeletions( $existing );
+
+ $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes, $this->getPropertyInsertions( $existing ) );
# Invalidate the necessary pages
- $changed = array_diff_assoc( $existing, $this->mProperties ) + array_diff_assoc( $this->mProperties, $existing );
+ $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
$this->invalidateProperties( $changed );
# Refresh links of all pages including this page
if ( $this->mRecursive ) {
$this->queueRecursiveJobs();
}
-
+
wfProfileOut( __METHOD__ );
}
* May be slower or faster depending on level of lock contention and write speed of DB
* Also useful where link table corruption needs to be repaired, e.g. in refreshLinks.php
*/
- function doDumbUpdate() {
+ protected function doDumbUpdate() {
wfProfileIn( __METHOD__ );
# Refresh category pages and image description pages
$existing = $this->getExistingCategories();
$categoryInserts = array_diff_assoc( $this->mCategories, $existing );
- $categoryDeletes = array_diff_assoc( $existing, $this->mCategoties );
+ $categoryDeletes = array_diff_assoc( $existing, $this->mCategories );
$categoryUpdates = $categoryInserts + $categoryDeletes;
$existing = $this->getExistingImages();
$imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing );
}
function queueRecursiveJobs() {
+ global $wgUpdateRowsPerJob;
wfProfileIn( __METHOD__ );
-
- $batchSize = 100;
+
$dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( array( 'templatelinks', 'page' ),
- array( 'page_namespace', 'page_title' ),
+ $res = $dbr->select( 'templatelinks',
+ array( 'tl_from' ),
array(
- 'page_id=tl_from',
'tl_namespace' => $this->mTitle->getNamespace(),
'tl_title' => $this->mTitle->getDBkey()
), __METHOD__
);
- $done = false;
- while ( !$done ) {
- $jobs = array();
- for ( $i = 0; $i < $batchSize; $i++ ) {
- $row = $dbr->fetchObject( $res );
- if ( !$row ) {
- $done = true;
+ $numRows = $res->numRows();
+ if( !$numRows ) {
+ wfProfileOut( __METHOD__ );
+ return; // nothing to do
+ }
+ $numBatches = ceil( $numRows / $wgUpdateRowsPerJob );
+ $realBatchSize = $numRows / $numBatches;
+ $start = false;
+ $jobs = array();
+ do {
+ for( $i = 0; $i <= $realBatchSize - 1; $i++ ) {
+ $row = $res->fetchRow();
+ if( $row ) {
+ $id = $row[0];
+ } else {
+ $id = false;
break;
}
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $jobs[] = new RefreshLinksJob( $title, '' );
}
- Job::batchInsert( $jobs );
- }
+ $params = array(
+ 'start' => $start,
+ 'end' => ( $id !== false ? $id - 1 : false ),
+ );
+ $jobs[] = new RefreshLinksJob2( $this->mTitle, $params );
+ # Hit page caches while we're at it if set to do so...
+ if( $this->mTouchTmplLinks ) {
+ $params['table'] = 'templatelinks';
+ $jobs[] = new HTMLCacheUpdateJob( $this->mTitle, $params );
+ }
+ $start = $id;
+ } while ( $start );
+
$dbr->freeResult( $res );
+
+ Job::batchInsert( $jobs );
+
wfProfileOut( __METHOD__ );
}
-
+
/**
* Invalidate the cache of a list of pages from a single namespace
*
if ( !count( $dbkeys ) ) {
return;
}
-
+
/**
* Determine which pages need to be updated
* This is necessary to prevent the job queue from smashing the DB with
*/
$now = $this->mDb->timestamp();
$ids = array();
- $res = $this->mDb->select( 'page', array( 'page_id' ),
- array(
+ $res = $this->mDb->select( 'page', array( 'page_id' ),
+ array(
'page_namespace' => $namespace,
'page_title IN (' . $this->mDb->makeList( $dbkeys ) . ')',
'page_touched < ' . $this->mDb->addQuotes( $now )
if ( !count( $ids ) ) {
return;
}
-
+
/**
* Do the update
- * We still need the page_touched condition, in case the row has changed since
+ * We still need the page_touched condition, in case the row has changed since
* the non-locking select above.
*/
- $this->mDb->update( 'page', array( 'page_touched' => $now ),
- array(
+ $this->mDb->update( 'page', array( 'page_touched' => $now ),
+ array(
'page_id IN (' . $this->mDb->makeList( $ids ) . ')',
'page_touched < ' . $this->mDb->addQuotes( $now )
), __METHOD__
function updateCategoryCounts( $added, $deleted ) {
$a = new Article($this->mTitle);
$a->updateCategoryCounts(
- array_keys( $added ), array_keys( $deleted ), $this->mDb
+ array_keys( $added ), array_keys( $deleted )
);
}
/**
* Get an array of interlanguage link insertions
- * @param array $existing Array mapping existing language codes to titles
+ * @param array $existing Array mapping existing language codes to titles
* @private
*/
function getInterlangInsertions( $existing = array() ) {
return array_diff_key( $existing, $this->mImages );
}
- /**
+ /**
* Given an array of existing external links, returns those links which are not
* in $this and thus should be deleted.
* @private
return array_diff_assoc( $existing, $this->mCategories );
}
- /**
+ /**
* Given an array of existing interlanguage links, returns those links which are not
* in $this and thus should be deleted.
* @private
}
/**
- * Get an array of existing interlanguage links, with the language code in the key and the
+ * Get an array of existing interlanguage links, with the language code in the key and the
* title in the value.
* @private
*/
function getExistingInterlangs() {
- $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
+ $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
$arr = array();
while ( $row = $this->mDb->fetchObject( $res ) ) {
return $arr;
}
-
+
/**
* Return the title object of the page being updated
- */
+ */
function getTitle() {
return $this->mTitle;
}
}
}
}
-