Daniel Kinzler has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/189489

Change subject: ConnectionManager: do not return slave connection after 
returning master conenction.
......................................................................

ConnectionManager: do not return slave connection after returning master 
conenction.

The idea is that we should never read from a slave after we have written to 
master.

Bug: T88985
Change-Id: I691cf4cfb505124bf1ac89ce4f1550b9ff1e6f41
---
M client/includes/store/sql/ConnectionManager.php
M client/tests/phpunit/includes/store/sql/ConnectionManagerTest.php
2 files changed, 57 insertions(+), 7 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase 
refs/changes/89/189489/1

diff --git a/client/includes/store/sql/ConnectionManager.php 
b/client/includes/store/sql/ConnectionManager.php
index 000da2f..f1426e3 100644
--- a/client/includes/store/sql/ConnectionManager.php
+++ b/client/includes/store/sql/ConnectionManager.php
@@ -28,6 +28,11 @@
        private $dbName;
 
        /**
+        * @var bool If true, getReadConnection() will also return a DB_MASTER 
connection.
+        */
+       private $forceMaster = false;
+
+       /**
         * @param LoadBalancer $loadBalancer
         * @param string|false $dbName Optional, defaults to current wiki.
         *        This follows the convention for database names used by 
$loadBalancer.
@@ -42,17 +47,35 @@
        }
 
        /**
-        * @return DatabaseBase
+        * Forces all future calls to getReadConnection() to return a 
connection to the master DB.
+        * Use this before performing read operations that are critical for a 
future update.
+        * Calling beginAtomicSection() implies a call to forceMaster().
         */
-       public function getReadConnection() {
-               return $this->loadBalancer->getConnection( DB_READ, array(), 
$this->dbName );
+       public function forceMaster() {
+               $this->forceMaster = true;
        }
 
        /**
+        * Returns a database connection for reading.
+        *
+        * @note: If forceMaster() or beginAtomicSection() were previously 
called on this
+        * ConnectionManager instance, this method will return a connection to 
the master database,
+        * to avoid inconsistencies.
+        *
+        * @return DatabaseBase
+        */
+       public function getReadConnection() {
+               $dbIndex = $this->forceMaster ? DB_MASTER : DB_SLAVE;
+               return $this->loadBalancer->getConnection( $dbIndex, array(), 
$this->dbName );
+       }
+
+       /**
+        * Returns a connection to the master DB, for updating.
+        *
         * @return DatabaseBase
         */
        private function getWriteConnection() {
-               return $this->loadBalancer->getConnection( DB_WRITE, array(), 
$this->dbName );
+               return $this->loadBalancer->getConnection( DB_MASTER, array(), 
$this->dbName );
        }
 
        /**
@@ -63,11 +86,20 @@
        }
 
        /**
+        * Begins an atomic section and returns a database connection to the 
master DB, for updating.
+        *
+        * @note: This causes all future calls to getReadConnection() to return 
a connection
+        * to the master DB, even after commitAtomicSection() or 
rollbackAtomicSection() have
+        * been called.
+        *
         * @param string $fname
         *
         * @return DatabaseBase
         */
        public function beginAtomicSection( $fname ) {
+               // Once we have written to master, do not read from slave.
+               $this->forceMaster();
+
                $db = $this->getWriteConnection();
                $db->startAtomic( $fname );
                return $db;
diff --git a/client/tests/phpunit/includes/store/sql/ConnectionManagerTest.php 
b/client/tests/phpunit/includes/store/sql/ConnectionManagerTest.php
index 64bb5b5..434632f 100644
--- a/client/tests/phpunit/includes/store/sql/ConnectionManagerTest.php
+++ b/client/tests/phpunit/includes/store/sql/ConnectionManagerTest.php
@@ -38,13 +38,27 @@
 
                $lb->expects( $this->once() )
                        ->method( 'getConnection' )
-                       ->with( DB_READ )
+                       ->with( DB_SLAVE )
                        ->will( $this->returnValue( $connection ) );
 
                $manager = new ConnectionManager( $lb );
                $actual = $manager->getReadConnection();
 
                $this->assertSame( $connection, $actual );
+       }
+
+       public function testForceMaster() {
+               $connection = $this->getConnectionMock();
+               $lb = $this->getLoadBalancerMock();
+
+               $lb->expects( $this->once() )
+                       ->method( 'getConnection' )
+                       ->with( DB_MASTER )
+                       ->will( $this->returnValue( $connection ) );
+
+               $manager = new ConnectionManager( $lb );
+               $manager->forceMaster();
+               $manager->getReadConnection();
        }
 
        public function testReleaseConnection() {
@@ -64,9 +78,9 @@
                $connection = $this->getConnectionMock();
                $lb = $this->getLoadBalancerMock( );
 
-               $lb->expects( $this->once() )
+               $lb->expects( $this->exactly( 2 ) )
                        ->method( 'getConnection' )
-                       ->with( DB_WRITE )
+                       ->with( DB_MASTER )
                        ->will( $this->returnValue( $connection ) );
 
                $connection->expects( $this->once() )
@@ -75,6 +89,10 @@
 
                $manager = new ConnectionManager( $lb );
                $manager->beginAtomicSection( 'TEST' );
+
+               // Should also ask for a DB_MASTER connection.
+               // This is asserted by the $lb mock.
+               $manager->getReadConnection();
        }
 
        public function testCommitAtomicSection() {

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I691cf4cfb505124bf1ac89ce4f1550b9ff1e6f41
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to