Various dependency injection cleanups to LoadBalancer
[lhc/web/wiklou.git] / includes / db / loadbalancer / LBFactory.php
index 9f8f919..df1d948 100644 (file)
@@ -147,7 +147,7 @@ abstract class LBFactory implements DestructibleService {
         * @deprecated since 1.27, use LBFactory::destroy()
         */
        public static function destroyInstance() {
-               self::singleton()->destroy();
+               MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
        }
 
        /**
@@ -185,7 +185,7 @@ abstract class LBFactory implements DestructibleService {
         * @param bool|string $wiki Wiki ID, or false for the current wiki
         * @return LoadBalancer
         */
-       abstract public function &getExternalLB( $cluster, $wiki = false );
+       abstract public function getExternalLB( $cluster, $wiki = false );
 
        /**
         * Execute a function for each tracked load balancer
@@ -223,6 +223,29 @@ abstract class LBFactory implements DestructibleService {
                );
        }
 
+       /**
+        * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
+        *
+        * @param string $fname Caller name
+        * @since 1.28
+        */
+       public function flushReplicaSnapshots( $fname = __METHOD__ ) {
+               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
+       }
+
+       /**
+        * Commit on all connections. Done for two reasons:
+        * 1. To commit changes to the masters.
+        * 2. To release the snapshot on all connections, master and replica DB.
+        * @param string $fname Caller name
+        * @param array $options Options map:
+        *   - maxWriteDuration: abort if more than this much time was spent in write queries
+        */
+       public function commitAll( $fname = __METHOD__, array $options = [] ) {
+               $this->commitMasterChanges( $fname, $options );
+               $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
+       }
+
        /**
         * Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
         *
@@ -230,6 +253,7 @@ abstract class LBFactory implements DestructibleService {
         *   - commitMasterChanges()
         *   - rollbackMasterChanges()
         *   - commitAll()
+        *
         * This allows for custom transaction rounds from any outer transaction scope.
         *
         * @param string $fname
@@ -240,7 +264,7 @@ abstract class LBFactory implements DestructibleService {
                if ( $this->trxRoundId !== false ) {
                        throw new DBTransactionError(
                                null,
-                               "Transaction round '{$this->trxRoundId}' already started."
+                               "$fname: transaction round '{$this->trxRoundId}' already started."
                        );
                }
                $this->trxRoundId = $fname;
@@ -248,29 +272,6 @@ abstract class LBFactory implements DestructibleService {
                $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
        }
 
-       /**
-        * Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot
-        *
-        * @param string $fname Caller name
-        * @since 1.28
-        */
-       public function flushReplicaSnapshots( $fname = __METHOD__ ) {
-               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
-       }
-
-       /**
-        * Commit on all connections. Done for two reasons:
-        * 1. To commit changes to the masters.
-        * 2. To release the snapshot on all connections, master and replica DB.
-        * @param string $fname Caller name
-        * @param array $options Options map:
-        *   - maxWriteDuration: abort if more than this much time was spent in write queries
-        */
-       public function commitAll( $fname = __METHOD__, array $options = [] ) {
-               $this->commitMasterChanges( $fname, $options );
-               $this->forEachLBCallMethod( 'commitAll', [ $fname ] );
-       }
-
        /**
         * Commit changes on all master connections
         * @param string $fname Caller name
@@ -279,6 +280,12 @@ abstract class LBFactory implements DestructibleService {
         * @throws Exception
         */
        public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
+               if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
+                       throw new DBTransactionError(
+                               null,
+                               "$fname: transaction round '{$this->trxRoundId}' still running."
+                       );
+               }
                // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
                $this->forEachLBCallMethod( 'finalizeMasterChanges' );
                $this->trxRoundId = false;
@@ -357,18 +364,27 @@ abstract class LBFactory implements DestructibleService {
 
        /**
         * Detemine if any lagged replica DB connection was used
-        * @since 1.27
         * @return bool
+        * @since 1.28
         */
-       public function laggedSlaveUsed() {
+       public function laggedReplicaUsed() {
                $ret = false;
                $this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) {
-                       $ret = $ret || $lb->laggedSlaveUsed();
+                       $ret = $ret || $lb->laggedReplicaUsed();
                } );
 
                return $ret;
        }
 
+       /**
+        * @return bool
+        * @since 1.27
+        * @deprecated Since 1.28; use laggedReplicaUsed()
+        */
+       public function laggedSlaveUsed() {
+               return $this->laggedReplicaUsed();
+       }
+
        /**
         * Determine if any master connection has pending/written changes from this request
         * @return bool
@@ -509,7 +525,7 @@ abstract class LBFactory implements DestructibleService {
         * This will commit and wait unless $ticket indicates it is unsafe to do so
         *
         * @param string $fname Caller name (e.g. __METHOD__)
-        * @param mixed $ticket Result of getOuterTransactionScopeTicket()
+        * @param mixed $ticket Result of getEmptyTransactionTicket()
         * @param array $opts Options to waitForReplication()
         * @throws DBReplicationWaitError
         * @since 1.28
@@ -521,8 +537,31 @@ abstract class LBFactory implements DestructibleService {
                        return;
                }
 
-               $this->commitMasterChanges( $fname );
+               // The transaction owner and any caller with the empty transaction ticket can commit
+               // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
+               if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
+                       $this->trxLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
+                       $fnameEffective = $this->trxRoundId;
+               } else {
+                       $fnameEffective = $fname;
+               }
+
+               $this->commitMasterChanges( $fnameEffective );
                $this->waitForReplication( $opts );
+               // If a nested caller committed on behalf of $fname, start another empty $fname
+               // transaction, leaving the caller with the same empty transaction state as before.
+               if ( $fnameEffective !== $fname ) {
+                       $this->beginMasterChanges( $fnameEffective );
+               }
+       }
+
+       /**
+        * @param string $dbName DB master name (e.g. "db1052")
+        * @return float|bool UNIX timestamp when client last touched the DB or false if not recent
+        * @since 1.28
+        */
+       public function getChronologyProtectorTouched( $dbName ) {
+               return $this->chronProt->getTouched( $dbName );
        }
 
        /**
@@ -581,6 +620,20 @@ abstract class LBFactory implements DestructibleService {
                } );
        }
 
+       /**
+        * Base parameters to LoadBalancer::__construct()
+        */
+       final protected function baseLoadBalancerParams() {
+               return [
+                       'readOnlyReason' => $this->readOnlyReason,
+                       'trxProfiler' => $this->trxProfiler,
+                       'srvCache' => $this->srvCache,
+                       'wanCache' => $this->wanCache,
+                       'localDomain' => wfWikiID(),
+                       'errorLogger' => [ MWExceptionHandler::class, 'logException' ]
+               ];
+       }
+
        /**
         * @param LoadBalancer $lb
         */