Merge "Provide command to adjust phpunit.xml for code coverage"
[lhc/web/wiklou.git] / includes / libs / rdbms / lbfactory / LBFactory.php
index e20f6de..07a5fe6 100644 (file)
@@ -155,13 +155,16 @@ abstract class LBFactory implements ILBFactory {
                $this->defaultGroup = $conf['defaultGroup'] ?? null;
                $this->secret = $conf['secret'] ?? '';
 
-               $this->id = mt_rand();
-               $this->ticket = mt_rand();
+               static $nextId, $nextTicket;
+               $this->id = $nextId = ( is_int( $nextId ) ? $nextId++ : mt_rand() );
+               $this->ticket = $nextTicket = ( is_int( $nextTicket ) ? $nextTicket++ : mt_rand() );
        }
 
        public function destroy() {
-               $this->shutdown( self::SHUTDOWN_NO_CHRONPROT );
-               $this->forEachLBCallMethod( 'disable' );
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $scope = ScopedCallback::newScopedIgnoreUserAbort();
+
+               $this->forEachLBCallMethod( 'disable', [ __METHOD__, $this->id ] );
        }
 
        public function getLocalDomainID() {
@@ -178,6 +181,9 @@ abstract class LBFactory implements ILBFactory {
                &$cpIndex = null,
                &$cpClientId = null
        ) {
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $scope = ScopedCallback::newScopedIgnoreUserAbort();
+
                $chronProt = $this->getChronologyProtector();
                if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
                        $this->shutdownChronologyProtector( $chronProt, $workCallback, 'sync', $cpIndex );
@@ -190,34 +196,6 @@ abstract class LBFactory implements ILBFactory {
                $this->commitMasterChanges( __METHOD__ ); // sanity
        }
 
-       /**
-        * @see ILBFactory::newMainLB()
-        * @param bool $domain
-        * @return ILoadBalancer
-        */
-       abstract public function newMainLB( $domain = false );
-
-       /**
-        * @see ILBFactory::getMainLB()
-        * @param bool $domain
-        * @return ILoadBalancer
-        */
-       abstract public function getMainLB( $domain = false );
-
-       /**
-        * @see ILBFactory::newExternalLB()
-        * @param string $cluster
-        * @return ILoadBalancer
-        */
-       abstract public function newExternalLB( $cluster );
-
-       /**
-        * @see ILBFactory::getExternalLB()
-        * @param string $cluster
-        * @return ILoadBalancer
-        */
-       abstract public function getExternalLB( $cluster );
-
        /**
         * Call a method of each tracked load balancer
         *
@@ -234,22 +212,31 @@ abstract class LBFactory implements ILBFactory {
        }
 
        public function flushReplicaSnapshots( $fname = __METHOD__ ) {
-               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
+               if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
+                       $this->queryLogger->warning(
+                               "$fname: transaction round '{$this->trxRoundId}' still running",
+                               [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
+                       );
+               }
+               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname, $this->id ] );
        }
 
        final public function commitAll( $fname = __METHOD__, array $options = [] ) {
                $this->commitMasterChanges( $fname, $options );
-               $this->forEachLBCallMethod( 'flushMasterSnapshots', [ $fname ] );
-               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
+               $this->forEachLBCallMethod( 'flushMasterSnapshots', [ $fname, $this->id ] );
+               $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname, $this->id ] );
        }
 
        final public function beginMasterChanges( $fname = __METHOD__ ) {
                $this->assertTransactionRoundStage( self::ROUND_CURSORY );
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $scope = ScopedCallback::newScopedIgnoreUserAbort();
+
                $this->trxRoundStage = self::ROUND_BEGINNING;
                if ( $this->trxRoundId !== false ) {
                        throw new DBTransactionError(
                                null,
-                               "$fname: transaction round '{$this->trxRoundId}' already started."
+                               "$fname: transaction round '{$this->trxRoundId}' already started"
                        );
                }
                $this->trxRoundId = $fname;
@@ -260,15 +247,16 @@ abstract class LBFactory implements ILBFactory {
 
        final public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
                $this->assertTransactionRoundStage( self::ROUND_CURSORY );
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $scope = ScopedCallback::newScopedIgnoreUserAbort();
+
                $this->trxRoundStage = self::ROUND_COMMITTING;
                if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
                        throw new DBTransactionError(
                                null,
-                               "$fname: transaction round '{$this->trxRoundId}' still running."
+                               "$fname: transaction round '{$this->trxRoundId}' still running"
                        );
                }
-               /** @noinspection PhpUnusedLocalVariableInspection */
-               $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
                // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
                do {
                        $count = 0; // number of callbacks executed this iteration
@@ -294,6 +282,9 @@ abstract class LBFactory implements ILBFactory {
        }
 
        final public function rollbackMasterChanges( $fname = __METHOD__ ) {
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $scope = ScopedCallback::newScopedIgnoreUserAbort();
+
                $this->trxRoundStage = self::ROUND_ROLLING_BACK;
                $this->trxRoundId = false;
                // Actually perform the rollback on all master DB connections and revert DBO_TRX
@@ -416,14 +407,20 @@ abstract class LBFactory implements ILBFactory {
                // time needed to wait on the next clusters.
                $masterPositions = array_fill( 0, count( $lbs ), false );
                foreach ( $lbs as $i => $lb ) {
-                       if ( !$lb->hasStreamingReplicaServers() ) {
-                               continue; // T29975: no replication; avoid getMasterPos() permissions errors
-                       } elseif (
-                               $opts['ifWritesSince'] &&
-                               $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
+                       if (
+                               // No writes to wait on getting replicated
+                               !$lb->hasMasterConnection() ||
+                               // No replication; avoid getMasterPos() permissions errors (T29975)
+                               !$lb->hasStreamingReplicaServers() ||
+                               // No writes since the last replication wait
+                               (
+                                       $opts['ifWritesSince'] &&
+                                       $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
+                               )
                        ) {
-                               continue; // no writes since the last wait
+                               continue; // no need to wait
                        }
+
                        $masterPositions[$i] = $lb->getMasterPos();
                }
 
@@ -456,8 +453,10 @@ abstract class LBFactory implements ILBFactory {
 
        public function getEmptyTransactionTicket( $fname ) {
                if ( $this->hasMasterChanges() ) {
-                       $this->queryLogger->error( __METHOD__ . ": $fname does not have outer scope.\n" .
-                               ( new RuntimeException() )->getTraceAsString() );
+                       $this->queryLogger->error(
+                               __METHOD__ . ": $fname does not have outer scope",
+                               [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
+                       );
 
                        return null;
                }
@@ -467,8 +466,10 @@ abstract class LBFactory implements ILBFactory {
 
        final public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
                if ( $ticket !== $this->ticket ) {
-                       $this->perfLogger->error( __METHOD__ . ": $fname does not have outer scope.\n" .
-                               ( new RuntimeException() )->getTraceAsString() );
+                       $this->perfLogger->error(
+                               __METHOD__ . ": $fname does not have outer scope",
+                               [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
+                       );
 
                        return false;
                }
@@ -476,7 +477,7 @@ abstract class LBFactory implements ILBFactory {
                // 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->queryLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
+                       $this->queryLogger->info( "$fname: committing on behalf of {$this->trxRoundId}" );
                        $fnameEffective = $this->trxRoundId;
                } else {
                        $fnameEffective = $fname;
@@ -530,11 +531,13 @@ abstract class LBFactory implements ILBFactory {
                } elseif ( $this->memStash instanceof EmptyBagOStuff ) {
                        // No where to store any DB positions and wait for them to appear
                        $this->chronProt->setEnabled( false );
-                       $this->replLogger->info( 'Cannot use ChronologyProtector with EmptyBagOStuff.' );
+                       $this->replLogger->info( 'Cannot use ChronologyProtector with EmptyBagOStuff' );
                }
 
-               $this->replLogger->debug( __METHOD__ . ': using request info ' .
-                       json_encode( $this->requestInfo, JSON_PRETTY_PRINT ) );
+               $this->replLogger->debug(
+                       __METHOD__ . ': request info ' .
+                       json_encode( $this->requestInfo, JSON_PRETTY_PRINT )
+               );
 
                return $this->chronProt;
        }
@@ -574,10 +577,12 @@ abstract class LBFactory implements ILBFactory {
        }
 
        /**
-        * Base parameters to ILoadBalancer::__construct()
+        * Get parameters to ILoadBalancer::__construct()
+        *
+        * @param int|null $owner Use getOwnershipId() if this is for getMainLB()/getExternalLB()
         * @return array
         */
-       final protected function baseLoadBalancerParams() {
+       final protected function baseLoadBalancerParams( $owner ) {
                if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
                        $initStage = ILoadBalancer::STAGE_POSTCOMMIT_CALLBACKS;
                } elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
@@ -609,7 +614,7 @@ abstract class LBFactory implements ILBFactory {
                                $this->getChronologyProtector()->applySessionReplicationPosition( $lb );
                        },
                        'roundStage' => $initStage,
-                       'ownerId' => $this->id
+                       'ownerId' => $owner
                ];
        }
 
@@ -633,14 +638,6 @@ abstract class LBFactory implements ILBFactory {
                $this->indexAliases = $aliases;
        }
 
-       /**
-        * @param string $prefix
-        * @deprecated Since 1.33
-        */
-       public function setDomainPrefix( $prefix ) {
-               $this->setLocalDomainPrefix( $prefix );
-       }
-
        public function setLocalDomainPrefix( $prefix ) {
                $this->localDomain = new DatabaseDomain(
                        $this->localDomain->getDatabase(),
@@ -664,7 +661,10 @@ abstract class LBFactory implements ILBFactory {
        }
 
        public function closeAll() {
-               $this->forEachLBCallMethod( 'closeAll' );
+               /** @noinspection PhpUnusedLocalVariableInspection */
+               $scope = ScopedCallback::newScopedIgnoreUserAbort();
+
+               $this->forEachLBCallMethod( 'closeAll', [ __METHOD__, $this->id ] );
        }
 
        public function setAgentName( $agent ) {
@@ -726,12 +726,20 @@ abstract class LBFactory implements ILBFactory {
 
        public function setRequestInfo( array $info ) {
                if ( $this->chronProt ) {
-                       throw new LogicException( 'ChronologyProtector already initialized.' );
+                       throw new LogicException( 'ChronologyProtector already initialized' );
                }
 
                $this->requestInfo = $info + $this->requestInfo;
        }
 
+       /**
+        * @return int Internal instance ID used to assert ownership of ILoadBalancer instances
+        * @since 1.34
+        */
+       final protected function getOwnershipId() {
+               return $this->id;
+       }
+
        /**
         * @param string $stage
         */