X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FRevision.php;h=6acc528f76bcde938da16cabfb024d5df1cab6cc;hb=956967415397d64f7c67259f190e8122a0b32506;hp=39fd1d1bcfc680ddedf5b9e67f73172bafb3b9a1;hpb=9509503f51476fa9ae736db51402b2da52f5fc83;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Revision.php b/includes/Revision.php index 39fd1d1bcf..6acc528f76 100644 --- a/includes/Revision.php +++ b/includes/Revision.php @@ -92,6 +92,8 @@ class Revision implements IDBAccessObject { const FOR_THIS_USER = 2; const RAW = 3; + const TEXT_CACHE_GROUP = 'revisiontext:10'; // process cache name and max key count + /** * Load a page revision from a given revision ID number. * Returns null if no such revision can be found. @@ -134,7 +136,7 @@ class Revision implements IDBAccessObject { } else { // Use a join to get the latest revision $conds[] = 'rev_id=page_latest'; - $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE ); + $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); return self::loadFromConds( $db, $conds, $flags ); } } @@ -161,7 +163,7 @@ class Revision implements IDBAccessObject { } else { // Use a join to get the latest revision $conds[] = 'rev_id = page_latest'; - $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE ); + $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); return self::loadFromConds( $db, $conds, $flags ); } } @@ -316,7 +318,7 @@ class Revision implements IDBAccessObject { * @return Revision|null */ private static function newFromConds( $conditions, $flags = 0 ) { - $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE ); + $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA ); $rev = self::loadFromConds( $db, $conditions, $flags ); // Make sure new pending/committed revision are visibile later on @@ -370,7 +372,7 @@ class Revision implements IDBAccessObject { */ public static function fetchRevision( LinkTarget $title ) { $row = self::fetchFromConds( - wfGetDB( DB_SLAVE ), + wfGetDB( DB_REPLICA ), [ 'rev_id=page_latest', 'page_namespace' => $title->getNamespace(), @@ -803,7 +805,7 @@ class Revision implements IDBAccessObject { } // rev_id is defined as NOT NULL, but this revision may not yet have been inserted. if ( $this->mId !== null ) { - $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_SLAVE, [], $this->mWiki ); + $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki ); $row = $dbr->selectRow( [ 'page', 'revision' ], self::selectPageFields(), @@ -989,7 +991,7 @@ class Revision implements IDBAccessObject { * @return RecentChange|null */ public function getRecentChange( $flags = 0 ) { - $dbr = wfGetDB( DB_SLAVE ); + $dbr = wfGetDB( DB_REPLICA ); list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags ); @@ -1079,13 +1081,14 @@ class Revision implements IDBAccessObject { } /** - * Fetch original serialized data without regard for view restrictions + * Get original serialized data (without checking view restrictions) * * @since 1.21 * @return string */ public function getSerializedData() { if ( $this->mText === null ) { + // Revision is immutable. Load on demand. $this->mText = $this->loadText(); } @@ -1103,17 +1106,14 @@ class Revision implements IDBAccessObject { */ protected function getContentInternal() { if ( $this->mContent === null ) { - // Revision is immutable. Load on demand: - if ( $this->mText === null ) { - $this->mText = $this->loadText(); - } + $text = $this->getSerializedData(); - if ( $this->mText !== null && $this->mText !== false ) { + if ( $text !== null && $text !== false ) { // Unserialize content $handler = $this->getContentHandler(); $format = $this->getContentFormat(); - $this->mContent = $handler->unserializeContent( $this->mText, $format ); + $this->mContent = $handler->unserializeContent( $text, $format ); } } @@ -1576,29 +1576,30 @@ class Revision implements IDBAccessObject { * * @return string|bool The revision's text, or false on failure */ - protected function loadText() { - // Caching may be beneficial for massive use of external storage + private function loadText() { global $wgRevisionCacheExpiry; - static $processCache = null; - if ( !$processCache ) { - $processCache = new MapCacheLRU( 10 ); + $cache = ObjectCache::getMainWANInstance(); + if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) { + // Do not cache RDBMs blobs in...the RDBMs store + $ttl = $cache::TTL_UNCACHEABLE; + } else { + $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE; } - $cache = ObjectCache::getMainWANInstance(); - $textId = $this->getTextId(); - $key = wfMemcKey( 'revisiontext', 'textid', $textId ); + // No negative caching; negative hits on text rows may be due to corrupted replica DBs + return $cache->getWithSetCallback( + $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ), + $ttl, + function () { + return $this->fetchText(); + }, + [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ] + ); + } - if ( $wgRevisionCacheExpiry ) { - if ( $processCache->has( $key ) ) { - return $processCache->get( $key ); - } - $text = $cache->get( $key ); - if ( is_string( $text ) ) { - $processCache->set( $key, $text ); - return $text; - } - } + private function fetchText() { + $textId = $this->getTextId(); // If we kept data for lazy extraction, use it now... if ( $this->mTextRow !== null ) { @@ -1608,25 +1609,38 @@ class Revision implements IDBAccessObject { $row = null; } + // Callers doing updates will pass in READ_LATEST as usual. Since the text/blob tables + // do not normally get rows changed around, set READ_LATEST_IMMUTABLE in those cases. + $flags = $this->mQueryFlags; + $flags |= DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ) + ? self::READ_LATEST_IMMUTABLE + : 0; + + list( $index, $options, $fallbackIndex, $fallbackOptions ) = + DBAccessObjectUtils::getDBOptions( $flags ); + if ( !$row ) { // Text data is immutable; check replica DBs first. - $dbr = wfGetDB( DB_SLAVE ); - $row = $dbr->selectRow( 'text', + $row = wfGetDB( $index )->selectRow( + 'text', [ 'old_text', 'old_flags' ], [ 'old_id' => $textId ], - __METHOD__ ); + __METHOD__, + $options + ); } - // Fallback to the master in case of replica DB lag. Also use FOR UPDATE if it was - // used to fetch this revision to avoid missing the row due to REPEATABLE-READ. - $forUpdate = ( $this->mQueryFlags & self::READ_LOCKING == self::READ_LOCKING ); - if ( !$row && ( $forUpdate || wfGetLB()->getServerCount() > 1 ) ) { - $dbw = wfGetDB( DB_MASTER ); - $row = $dbw->selectRow( 'text', + // Fallback to DB_MASTER in some cases if the row was not found + if ( !$row && $fallbackIndex !== null ) { + // Use FOR UPDATE if it was used to fetch this revision. This avoids missing the row + // due to REPEATABLE-READ. Also fallback to the master if READ_LATEST is provided. + $row = wfGetDB( $fallbackIndex )->selectRow( + 'text', [ 'old_text', 'old_flags' ], [ 'old_id' => $textId ], __METHOD__, - $forUpdate ? [ 'FOR UPDATE' ] : [] ); + $fallbackOptions + ); } if ( !$row ) { @@ -1638,13 +1652,7 @@ class Revision implements IDBAccessObject { wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." ); } - # No negative caching -- negative hits on text rows may be due to corrupted replica DB servers - if ( $wgRevisionCacheExpiry && $text !== false ) { - $processCache->set( $key, $text ); - $cache->set( $key, $text, $wgRevisionCacheExpiry ); - } - - return $text; + return is_string( $text ) ? $text : false; } /** @@ -1792,7 +1800,7 @@ class Revision implements IDBAccessObject { static function getTimestampFromId( $title, $id, $flags = 0 ) { $db = ( $flags & self::READ_LATEST ) ? wfGetDB( DB_MASTER ) - : wfGetDB( DB_SLAVE ); + : wfGetDB( DB_REPLICA ); // Casting fix for databases that can't take '' for rev_id if ( $id == '' ) { $id = 0; @@ -1919,7 +1927,7 @@ class Revision implements IDBAccessObject { } $this->mRefreshMutableFields = false; - $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_SLAVE, [], $this->mWiki ); + $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki ); $row = $dbr->selectRow( [ 'revision', 'user' ], [ 'rev_deleted', 'user_name' ],