*
* Some fields are public only for backwards-compatibility. Use accessors.
* In the past, this class was part of Article.php and everything was public.
- *
- * @internal documentation reviewed 15 Mar 2010
*/
class WikiPage implements Page, IDBAccessObject {
// Constants for $mDataLoadedFrom and related
$data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
} elseif ( $from === self::READ_NORMAL ) {
$data = $this->pageDataFromTitle( wfGetDB( DB_SLAVE ), $this->mTitle );
- // Use a "last rev inserted" timestamp key to diminish the issue of slave lag.
- // Note that DB also stores the master position in the session and checks it.
- $touched = $this->getCachedLastEditTime();
- if ( $touched ) { // key set
- if ( !$data || $touched > wfTimestamp( TS_MW, $data->page_touched ) ) {
- $from = self::READ_LATEST;
- $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
- }
+ if ( !$data
+ && wfGetLB()->getServerCount() > 1
+ && wfGetLB()->hasOrMadeRecentMasterChanges()
+ ) {
+ $from = self::READ_LATEST;
+ $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
}
} else {
// No idea from where the caller got this data, assume slave database.
return; // page doesn't exist or is missing page_latest info
}
- // Bug 37225: if session S1 loads the page row FOR UPDATE, the result always includes the
- // latest changes committed. This is true even within REPEATABLE-READ transactions, where
- // S1 normally only sees changes committed before the first S1 SELECT. Thus we need S1 to
- // also gets the revision row FOR UPDATE; otherwise, it may not find it since a page row
- // UPDATE and revision row INSERT by S2 may have happened after the first S1 SELECT.
- // http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read.
- $flags = ( $this->mDataLoadedFrom == self::READ_LOCKING ) ? Revision::READ_LOCKING : 0;
+ if ( $this->mDataLoadedFrom == self::READ_LOCKING ) {
+ // Bug 37225: if session S1 loads the page row FOR UPDATE, the result always
+ // includes the latest changes committed. This is true even within REPEATABLE-READ
+ // transactions, where S1 normally only sees changes committed before the first S1
+ // SELECT. Thus we need S1 to also gets the revision row FOR UPDATE; otherwise, it
+ // may not find it since a page row UPDATE and revision row INSERT by S2 may have
+ // happened after the first S1 SELECT.
+ // http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read.
+ $flags = Revision::READ_LOCKING;
+ } elseif ( $this->mDataLoadedFrom == self::READ_LATEST ) {
+ // Bug T93976: if page_latest was loaded from the master, fetch the
+ // revision from there as well, as it may not exist yet on a slave DB.
+ // Also, this keeps the queries in the same REPEATABLE-READ snapshot.
+ $flags = Revision::READ_LATEST;
+ } else {
+ $flags = 0;
+ }
$revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
if ( $revision ) { // sanity
$this->setLastEdit( $revision );
}
}
- /**
- * Get the cached timestamp for the last time the page changed.
- * This is only used to help handle slave lag by comparing to page_touched.
- * @return string MW timestamp
- */
- protected function getCachedLastEditTime() {
- global $wgMemc;
- $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
- return $wgMemc->get( $key );
- }
-
- /**
- * Set the cached timestamp for the last time the page changed.
- * This is only used to help handle slave lag by comparing to page_touched.
- * @param string $timestamp
- * @return void
- */
- public function setCachedLastEditTime( $timestamp ) {
- global $wgMemc;
- $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
- $wgMemc->set( $key, wfTimestamp( TS_MW, $timestamp ), 60 * 15 );
- }
-
/**
* Determine whether a page would be suitable for being counted as an
* article in the site_stats table based on the title & its content
) {
global $wgContentHandlerUseDB;
+ // Assertion to try to catch T92046
+ if ( (int)$revision->getId() === 0 ) {
+ throw new InvalidArgumentException(
+ __METHOD__ . ': Revision has ID ' . var_export( $revision->getId(), 1 )
+ );
+ }
+
$content = $revision->getContent();
$len = $content ? $content->getSize() : 0;
$rt = $content ? $content->getUltimateRedirectTarget() : null;
if ( $result ) {
$this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
$this->setLastEdit( $revision );
- $this->setCachedLastEditTime( $now );
$this->mLatest = $revision->getId();
$this->mIsRedirect = (bool)$rt;
// Update the LinkCache.
$baseRevId = null;
if ( $edittime && $sectionId !== 'new' ) {
- $dbw = wfGetDB( DB_MASTER );
- $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
+ $dbr = wfGetDB( DB_SLAVE );
+ $rev = Revision::loadFromTimestamp( $dbr, $this->mTitle, $edittime );
+ // Try the master if this thread may have just added it.
+ // This could be abstracted into a Revision method, but we don't want
+ // to encourage loading of revisions by timestamp.
+ if ( !$rev
+ && wfGetLB()->getServerCount() > 1
+ && wfGetLB()->hasOrMadeRecentMasterChanges()
+ ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
+ }
if ( $rev ) {
$baseRevId = $rev->getId();
}
if ( is_null( $baseRevId ) || $sectionId === 'new' ) {
$oldContent = $this->getContent();
} else {
- // TODO: try DB_SLAVE first
- $dbw = wfGetDB( DB_MASTER );
- $rev = Revision::loadFromId( $dbw, $baseRevId );
-
+ $rev = Revision::newFromId( $baseRevId );
if ( !$rev ) {
wfDebug( __METHOD__ . " asked for bogus section (page: " .
$this->getId() . "; section: $sectionId)\n" );
// Promote user to any groups they meet the criteria for
$dbw->onTransactionIdle( function () use ( $user ) {
$user->addAutopromoteOnceGroups( 'onEdit' );
+ $user->addAutopromoteOnceGroups( 'onView' ); // b/c
} );
return $status;
}
// Generate the edit summary if necessary
- $target = Revision::newFromId( $s->rev_id );
+ $target = Revision::newFromId( $s->rev_id, Revision::READ_LATEST );
if ( empty( $summary ) ) {
if ( $from == '' ) { // no public user name
$summary = wfMessage( 'revertpage-nouser' );
* Opportunistically enqueue link update jobs given fresh parser output if useful
*
* @param ParserOutput $parserOutput Current version page output
- * @return bool Whether a job was pushed
* @since 1.25
*/
public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) {
if ( wfReadOnly() ) {
- return false;
+ return;
+ }
+
+ if ( !Hooks::run( 'OpportunisticLinksUpdate', array( $this, $this->mTitle, $parserOutput ) ) ) {
+ return;
}
if ( $this->mTitle->areRestrictionsCascading() ) {
$params = array();
} else {
// If the inclusions are deterministic, the edit-triggered link jobs are enough
- return false;
+ return;
}
// Check if the last link refresh was before page_touched
JobQueueGroup::singleton()->push( EnqueueJob::newFromLocalJobs(
new JobSpecification( 'refreshLinks', $params, array(), $this->mTitle )
) );
- return true;
+ return;
}
- return false;
+ return;
}
/**