Daniel Kinzler has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/398884 )

Change subject: Fetch revisions from master DB if not found on replica.
......................................................................

Fetch revisions from master DB if not found on replica.

This overhauls the logic that determins when we will fall back
to loading a revision from the master databasase.

Change-Id: I316a1c25ecfe9fd9d0e2d0acdef3be6baec6417a
---
M includes/Storage/RevisionStore.php
1 file changed, 97 insertions(+), 45 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/84/398884/1

diff --git a/includes/Storage/RevisionStore.php 
b/includes/Storage/RevisionStore.php
index b8debb8..cb0e11a 100644
--- a/includes/Storage/RevisionStore.php
+++ b/includes/Storage/RevisionStore.php
@@ -842,15 +842,18 @@
         *
         * MCR migration note: this replaces Revision::newFromId
         *
-        * $flags include:
-        *      IDBAccessObject::READ_LATEST: Select the data from the master
-        *      IDBAccessObject::READ_LOCKING : Select & lock the data from the 
master
+        * Depending on $flags, this method may try to load the revision from a 
replica
+        * before hitting the master database, or try the master database 
immediately.
         *
         * @param int $id
         * @param int $flags (optional)
         * @return RevisionRecord|null
         */
        public function getRevisionById( $id, $flags = 0 ) {
+               // Since we know the revision ID, we are pretty sure it exists, 
so we want to fall
+               // back to hitting master if it'S not found on a replica, even 
if no writes were done
+               // in this request. READ_LATEST_IMMUTABLE forces this.
+               $flags |= self::READ_LATEST_IMMUTABLE;
                return $this->newRevisionFromConds( [ 'rev_id' => intval( $id ) 
], $flags );
        }
 
@@ -861,9 +864,9 @@
         *
         * MCR migration note: this replaces Revision::newFromTitle
         *
-        * $flags include:
-        *      IDBAccessObject::READ_LATEST: Select the data from the master
-        *      IDBAccessObject::READ_LOCKING : Select & lock the data from the 
master
+        * Depending on $flags, this method may try to load the revision from a 
replica
+        * before hitting the master database, try the master database 
immediately,
+        * or give up after trying only the replica.
         *
         * @param LinkTarget $linkTarget
         * @param int $revId (optional)
@@ -881,14 +884,16 @@
                        // and fall back to master if the page is not found on 
a replica.
                        // Since the caller supplied a revision ID, we are 
pretty sure the revision is
                        // supposed to exist, so we should try hard to find it.
+                       // The READ_LATEST_IMMUTABLE flag will force a retry 
even if no writes were done
+                       // in the present request.
+                       $flags |= self::READ_LATEST_IMMUTABLE;
                        $conds['rev_id'] = $revId;
                        return $this->newRevisionFromConds( $conds, $flags );
                } else {
                        // Use a join to get the latest revision.
-                       // Note that we don't use newRevisionFromConds here 
because we don't want to retry
-                       // and fall back to master. The assumption is that we 
only want to force the fallback
-                       // if we are quite sure the revision exists because the 
caller supplied a revision ID.
-                       // If the page isn't found at all on a replica, it 
probably simply does not exist.
+                       // Note that we don't use newRevisionFromConds since we 
do not need to fall back to
+                       // master: page_latest is guaranteed to always point to 
an existing revision on the same
+                       // database server.
                        $db = $this->getDBConnection( ( $flags & 
self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
 
                        $conds[] = 'rev_id=page_latest';
@@ -906,9 +911,9 @@
         *
         * MCR migration note: this replaces Revision::newFromPageId
         *
-        * $flags include:
-        *      IDBAccessObject::READ_LATEST: Select the data from the master 
(since 1.20)
-        *      IDBAccessObject::READ_LOCKING : Select & lock the data from the 
master
+        * Depending on $flags, this method may try to load the revision from a 
replica
+        * before hitting the master database, try the master database 
immediately,
+        * or give up after trying only the replica.
         *
         * @param int $pageId
         * @param int $revId (optional)
@@ -923,14 +928,16 @@
                        // and fall back to master if the page is not found on 
a replica.
                        // Since the caller supplied a revision ID, we are 
pretty sure the revision is
                        // supposed to exist, so we should try hard to find it.
+                       // The READ_LATEST_IMMUTABLE flag will force a retry 
even if no writes were done
+                       // in the present request.
+                       $flags |= self::READ_LATEST_IMMUTABLE;
                        $conds['rev_id'] = $revId;
                        return $this->newRevisionFromConds( $conds, $flags );
                } else {
                        // Use a join to get the latest revision.
-                       // Note that we don't use newRevisionFromConds here 
because we don't want to retry
-                       // and fall back to master. The assumption is that we 
only want to force the fallback
-                       // if we are quite sure the revision exists because the 
caller supplied a revision ID.
-                       // If the page isn't found at all on a replica, it 
probably simply does not exist.
+                       // Note that we don't use newRevisionFromConds since we 
do not need to fall back to
+                       // master: page_latest is guaranteed to always point to 
an existing revision on the same
+                       // database server.
                        $db = $this->getDBConnection( ( $flags & 
self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
 
                        $conds[] = 'rev_id=page_latest';
@@ -1394,8 +1401,11 @@
         * Given a set of conditions, fetch a revision
         *
         * This method should be used if we are pretty sure the revision exists.
-        * Unless $flags has READ_LATEST set, this method will first try to 
find the revision
-        * on a replica before hitting the master database.
+        * If READ_LATEST is set in $flags, only the master database is tried.
+        * If READ_LATEST is not set in $flags, this method will first try to 
find the revision
+        * on a replica; if the revision is not found and READ_LATEST_IMMUTABLE 
is set or
+        * the database was updated during the present request, this method 
will try to find the
+        * revision on the master database.
         *
         * MCR migration note: this corresponds to Revision::newFromConds
         *
@@ -1406,26 +1416,63 @@
         * @return RevisionRecord|null
         */
        private function newRevisionFromConds( $conditions, $flags = 0, Title 
$title = null ) {
-               $db = $this->getDBConnection( ( $flags & self::READ_LATEST ) ? 
DB_MASTER : DB_REPLICA );
-               $rev = $this->loadRevisionFromConds( $db, $conditions, $flags, 
$title );
-               $this->releaseDBConnection( $db );
+               $row = $this->findRevisionRowFromConds( $conditions, $flags );
+               if ( $row ) {
+                       $rev = $this->newRevisionFromRow( $row, $flags, $title 
);
 
-               $lb = $this->getDBLoadBalancer();
-
-               // Make sure new pending/committed revision are visibile later 
on
-               // within web requests to certain avoid bugs like T93866 and 
T94407.
-               if ( !$rev
-                       && !( $flags & self::READ_LATEST )
-                       && $lb->getServerCount() > 1
-                       && $lb->hasOrMadeRecentMasterChanges()
-               ) {
-                       $flags = self::READ_LATEST;
-                       $db = $this->getDBConnection( DB_MASTER );
-                       $rev = $this->loadRevisionFromConds( $db, $conditions, 
$flags, $title );
-                       $this->releaseDBConnection( $db );
+                       return $rev;
                }
 
-               return $rev;
+               return null;
+       }
+
+       /**
+        * Given a set of conditions, return a row with the
+        * fields necessary to build RevisionRecord objects.
+        *
+        * If READ_LATEST is set in $flags, only the master database is tried.
+        * If READ_LATEST is not set in $flags, this method will first try to 
find the revision
+        * on a replica; if the revision is not found and READ_LATEST_IMMUTABLE 
is set, or
+        * the database was updated during the present request, this method 
will try to find the
+        * revision on the master database.
+        *
+        * MCR migration note: this corresponds to Revision::newFromConds
+        *
+        * @param array $conditions
+        * @param int $flags (optional)
+        * @param array &$cacheOpts Cache options, as an associative array; 
will be updated with
+        *        database lag information according to 
Database::getCacheSetOptions.
+        *
+        * @return false|object data row as a raw object
+        */
+       private function findRevisionRowFromConds( $conditions, $flags = 0, 
array &$cacheOpts = [] ) {
+               $db = $this->getDBConnection( ( $flags & self::READ_LATEST ) ? 
DB_MASTER : DB_REPLICA );
+               $row = $this->selectRevisionRowFromConds( $db, $conditions, 
$flags );
+               $this->releaseDBConnection( $db );
+
+               if ( $row ) {
+                       $cacheOpts += Database::getCacheSetOptions( $db );
+               } else {
+                       $lb = $this->getDBLoadBalancer();
+
+                       // Make sure new pending/committed revision are 
visibile later on
+                       // within web requests to certain avoid bugs like 
T93866 and T94407.
+                       if ( !( $flags & self::READ_LATEST )
+                               && $lb->getServerCount() > 1
+                               && ( $lb->hasOrMadeRecentMasterChanges() || ( 
$flags & self::READ_LATEST_IMMUTABLE ) )
+                       ) {
+                               $flags = self::READ_LATEST;
+                               $db = $this->getDBConnection( DB_MASTER );
+                               $row = $this->selectRevisionRowFromConds( $db, 
$conditions, $flags );
+                               $this->releaseDBConnection( $db );
+                       }
+
+                       if ( $row ) {
+                               $cacheOpts += Database::getCacheSetOptions( $db 
);
+                       }
+               }
+
+               return $row;
        }
 
        /**
@@ -1447,7 +1494,7 @@
                $flags = 0,
                Title $title = null
        ) {
-               $row = $this->fetchRevisionRowFromConds( $db, $conditions, 
$flags );
+               $row = $this->selectRevisionRowFromConds( $db, $conditions, 
$flags );
                if ( $row ) {
                        $rev = $this->newRevisionFromRow( $row, $flags, $title 
);
 
@@ -1455,6 +1502,15 @@
                }
 
                return null;
+       }
+
+       /**
+        * Returns the domain ID for the wiki this RevisionStore is associated 
with
+        *
+        * @return string
+        */
+       private function getDBDomainID() {
+               return $this->wikiId ?: wfWikiID();
        }
 
        /**
@@ -1494,7 +1550,7 @@
         *
         * @return object|false data row as a raw object
         */
-       private function fetchRevisionRowFromConds( IDatabase $db, $conditions, 
$flags = 0 ) {
+       private function selectRevisionRowFromConds( IDatabase $db, 
$conditions, $flags = 0 ) {
                $this->checkDatabaseWikiId( $db );
 
                $revQuery = self::getQueryInfo( [ 'page', 'user' ] );
@@ -1863,8 +1919,6 @@
         * @return RevisionRecord|bool Returns false if missing
         */
        public function getKnownCurrentRevision( Title $title, $revId ) {
-               $db = $this->getDBConnectionRef( DB_REPLICA );
-
                $pageId = $title->getArticleID();
 
                if ( !$pageId ) {
@@ -1885,18 +1939,16 @@
 
                $row = $this->cache->getWithSetCallback(
                        // Page/rev IDs passed in from DB to reflect history 
merges
-                       $this->cache->makeGlobalKey( 'revision-row-1.29', 
$db->getDomainID(), $pageId, $revId ),
+                       $this->cache->makeGlobalKey( 'revision-row-1.29', 
$this->getDBDomainID(), $pageId, $revId ),
                        WANObjectCache::TTL_WEEK,
-                       function ( $curValue, &$ttl, array &$setOpts ) use ( 
$db, $pageId, $revId ) {
-                               $setOpts += Database::getCacheSetOptions( $db );
-
+                       function ( $curValue, &$ttl, array &$setOpts ) use ( 
$pageId, $revId ) {
                                $conds = [
                                        'rev_page' => intval( $pageId ),
                                        'page_id' => intval( $pageId ),
                                        'rev_id' => intval( $revId ),
                                ];
 
-                               $row = $this->fetchRevisionRowFromConds( $db, 
$conds );
+                               $row = $this->findRevisionRowFromConds( $conds, 
self::READ_LATEST_IMMUTABLE, $setOpts );
                                return $row ?: false; // don't cache negatives
                        }
                );

-- 
To view, visit https://gerrit.wikimedia.org/r/398884
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I316a1c25ecfe9fd9d0e2d0acdef3be6baec6417a
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to