Change-Id: Idafd2cf05e016a5f88b90e4c4c74f82c212d61c9
- final public function rollback( $fname = __METHOD__, $flush = '' ) {
+ final public function rollback( $fname = __METHOD__, $flush = self::FLUSHING_ONE ) {
$trxActive = $this->trxLevel;
if ( $flush !== self::FLUSHING_INTERNAL
$trxActive = $this->trxLevel;
if ( $flush !== self::FLUSHING_INTERNAL
/** @var callable[] */
private $replicationWaitCallbacks = [];
/** @var callable[] */
private $replicationWaitCallbacks = [];
+ /** var int An identifier for this class instance */
+ private $id;
+ /** @var int|null Ticket used to delegate transaction ownership */
private $ticket;
/** @var string|bool String if a requested DBO_TRX transaction round is active */
private $trxRoundId = false;
private $ticket;
/** @var string|bool String if a requested DBO_TRX transaction round is active */
private $trxRoundId = false;
$this->defaultGroup = $conf['defaultGroup'] ?? null;
$this->secret = $conf['secret'] ?? '';
$this->defaultGroup = $conf['defaultGroup'] ?? null;
$this->secret = $conf['secret'] ?? '';
$this->ticket = mt_rand();
}
$this->ticket = mt_rand();
}
}
$this->trxRoundId = $fname;
// Set DBO_TRX flags on all appropriate DBs
}
$this->trxRoundId = $fname;
// Set DBO_TRX flags on all appropriate DBs
- $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
+ $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname, $this->id ] );
$this->trxRoundStage = self::ROUND_CURSORY;
}
$this->trxRoundStage = self::ROUND_CURSORY;
}
// Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
do {
$count = 0; // number of callbacks executed this iteration
// Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
do {
$count = 0; // number of callbacks executed this iteration
- $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$count ) {
- $count += $lb->finalizeMasterChanges();
+ $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$count, $fname ) {
+ $count += $lb->finalizeMasterChanges( $fname, $this->id );
} );
} while ( $count > 0 );
$this->trxRoundId = false;
// Perform pre-commit checks, aborting on failure
} );
} while ( $count > 0 );
$this->trxRoundId = false;
// Perform pre-commit checks, aborting on failure
- $this->forEachLBCallMethod( 'approveMasterChanges', [ $options ] );
+ $this->forEachLBCallMethod( 'approveMasterChanges', [ $options, $fname, $this->id ] );
// Log the DBs and methods involved in multi-DB transactions
$this->logIfMultiDbTransaction();
// Actually perform the commit on all master DB connections and revert DBO_TRX
// Log the DBs and methods involved in multi-DB transactions
$this->logIfMultiDbTransaction();
// Actually perform the commit on all master DB connections and revert DBO_TRX
- $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
+ $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname, $this->id ] );
// Run all post-commit callbacks in a separate step
$this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
$e = $this->executePostTransactionCallbacks();
// Run all post-commit callbacks in a separate step
$this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
$e = $this->executePostTransactionCallbacks();
$this->trxRoundStage = self::ROUND_ROLLING_BACK;
$this->trxRoundId = false;
// Actually perform the rollback on all master DB connections and revert DBO_TRX
$this->trxRoundStage = self::ROUND_ROLLING_BACK;
$this->trxRoundId = false;
// Actually perform the rollback on all master DB connections and revert DBO_TRX
- $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
+ $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname, $this->id ] );
// Run all post-commit callbacks in a separate step
$this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
$this->executePostTransactionCallbacks();
// Run all post-commit callbacks in a separate step
$this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
$this->executePostTransactionCallbacks();
* @return Exception|null
*/
private function executePostTransactionCallbacks() {
* @return Exception|null
*/
private function executePostTransactionCallbacks() {
// Run all post-commit callbacks until new ones stop getting added
$e = null; // first callback exception
do {
// Run all post-commit callbacks until new ones stop getting added
$e = null; // first callback exception
do {
- $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e ) {
- $ex = $lb->runMasterTransactionIdleCallbacks();
+ $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e, $fname ) {
+ $ex = $lb->runMasterTransactionIdleCallbacks( $fname, $this->id );
$e = $e ?: $ex;
} );
} while ( $this->hasMasterChanges() );
// Run all listener callbacks once
$e = $e ?: $ex;
} );
} while ( $this->hasMasterChanges() );
// Run all listener callbacks once
- $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e ) {
- $ex = $lb->runMasterTransactionListenerCallbacks();
+ $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e, $fname ) {
+ $ex = $lb->runMasterTransactionListenerCallbacks( $fname, $this->id );
// being called later (but before the first connection attempt) (T192611)
$this->getChronologyProtector()->applySessionReplicationPosition( $lb );
},
// being called later (but before the first connection attempt) (T192611)
$this->getChronologyProtector()->applySessionReplicationPosition( $lb );
},
- 'roundStage' => $initStage
+ 'roundStage' => $initStage,
+ 'ownerId' => $this->id
*/
protected function initLoadBalancer( ILoadBalancer $lb ) {
if ( $this->trxRoundId !== false ) {
*/
protected function initLoadBalancer( ILoadBalancer $lb ) {
if ( $this->trxRoundId !== false ) {
- $lb->beginMasterChanges( $this->trxRoundId ); // set DBO_TRX
+ $lb->beginMasterChanges( $this->trxRoundId, $this->id ); // set DBO_TRX
}
$lb->setTableAliases( $this->tableAliases );
}
$lb->setTableAliases( $this->tableAliases );
* - errorLogger : Callback that takes an Exception and logs it. [optional]
* - deprecationLogger: Callback to log a deprecation warning. [optional]
* - roundStage: STAGE_POSTCOMMIT_* class constant; for internal use [optional]
* - errorLogger : Callback that takes an Exception and logs it. [optional]
* - deprecationLogger: Callback to log a deprecation warning. [optional]
* - roundStage: STAGE_POSTCOMMIT_* class constant; for internal use [optional]
+ * - ownerId: integer ID of an LBFactory instance that manages this instance [optional]
* @throws InvalidArgumentException
*/
public function __construct( array $params );
* @throws InvalidArgumentException
*/
public function __construct( array $params );
/**
* Commit transactions on all open connections
* @param string $fname Caller name
/**
* Commit transactions on all open connections
* @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @throws DBExpectedError
*/
* @throws DBExpectedError
*/
- public function commitAll( $fname = __METHOD__ );
+ public function commitAll( $fname = __METHOD__, $owner = null );
/**
* Run pre-commit callbacks and defer execution of post-commit callbacks
*
* Use this only for mutli-database commits
*
/**
* Run pre-commit callbacks and defer execution of post-commit callbacks
*
* Use this only for mutli-database commits
*
+ * @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @return int Number of pre-commit callbacks run (since 1.32)
*/
* @return int Number of pre-commit callbacks run (since 1.32)
*/
- public function finalizeMasterChanges();
+ public function finalizeMasterChanges( $fname = __METHOD__, $owner = null );
/**
* Perform all pre-commit checks for things like replication safety
/**
* Perform all pre-commit checks for things like replication safety
*
* @param array $options Includes:
* - maxWriteDuration : max write query duration time in seconds
*
* @param array $options Includes:
* - maxWriteDuration : max write query duration time in seconds
+ * @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @throws DBTransactionError
*/
* @throws DBTransactionError
*/
- public function approveMasterChanges( array $options );
+ public function approveMasterChanges( array $options, $fname, $owner = null );
/**
* Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
/**
* Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
* - commitAll()
* This allows for custom transaction rounds from any outer transaction scope.
*
* - commitAll()
* This allows for custom transaction rounds from any outer transaction scope.
*
+ * @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @throws DBExpectedError
*/
* @throws DBExpectedError
*/
- public function beginMasterChanges( $fname = __METHOD__ );
+ public function beginMasterChanges( $fname = __METHOD__, $owner = null );
/**
* Issue COMMIT on all open master connections to flush changes and view snapshots
* @param string $fname Caller name
/**
* Issue COMMIT on all open master connections to flush changes and view snapshots
* @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @throws DBExpectedError
*/
* @throws DBExpectedError
*/
- public function commitMasterChanges( $fname = __METHOD__ );
+ public function commitMasterChanges( $fname = __METHOD__, $owner = null );
/**
* Consume and run all pending post-COMMIT/ROLLBACK callbacks and commit dangling transactions
*
/**
* Consume and run all pending post-COMMIT/ROLLBACK callbacks and commit dangling transactions
*
+ * @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @return Exception|null The first exception or null if there were none
*/
* @return Exception|null The first exception or null if there were none
*/
- public function runMasterTransactionIdleCallbacks();
+ public function runMasterTransactionIdleCallbacks( $fname = __METHOD__, $owner = null );
/**
* Run all recurring post-COMMIT/ROLLBACK listener callbacks
*
/**
* Run all recurring post-COMMIT/ROLLBACK listener callbacks
*
+ * @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @return Exception|null The first exception or null if there were none
*/
* @return Exception|null The first exception or null if there were none
*/
- public function runMasterTransactionListenerCallbacks();
+ public function runMasterTransactionListenerCallbacks( $fname = __METHOD__, $owner = null );
/**
* Issue ROLLBACK only on master, only if queries were done on connection
* @param string $fname Caller name
/**
* Issue ROLLBACK only on master, only if queries were done on connection
* @param string $fname Caller name
+ * @param int|null $owner ID of the calling instance (e.g. the LBFactory ID)
* @throws DBExpectedError
*/
* @throws DBExpectedError
*/
- public function rollbackMasterChanges( $fname = __METHOD__ );
+ public function rollbackMasterChanges( $fname = __METHOD__, $owner = null );
/**
* Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshots
/**
* Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshots
/** @var bool Whether any connection has been attempted yet */
private $connectionAttempted = false;
/** @var bool Whether any connection has been attempted yet */
private $connectionAttempted = false;
+ /** @var int|null An integer ID of the managing LBFactory instance or null */
+ private $ownerId;
/** @var string|bool String if a requested DBO_TRX transaction round is active */
private $trxRoundId = false;
/** @var string Stage of the current transaction round in the transaction round life-cycle */
/** @var string|bool String if a requested DBO_TRX transaction round is active */
private $trxRoundId = false;
/** @var string Stage of the current transaction round in the transaction round life-cycle */
}
$this->defaultGroup = $params['defaultGroup'] ?? null;
}
$this->defaultGroup = $params['defaultGroup'] ?? null;
+ $this->ownerId = $params['ownerId'] ?? null;
}
public function getLocalDomainID() {
}
public function getLocalDomainID() {
- public function commitAll( $fname = __METHOD__ ) {
- $this->commitMasterChanges( $fname );
+ public function commitAll( $fname = __METHOD__, $owner = null ) {
+ $this->commitMasterChanges( $fname, $owner );
$this->flushMasterSnapshots( $fname );
$this->flushReplicaSnapshots( $fname );
}
$this->flushMasterSnapshots( $fname );
$this->flushReplicaSnapshots( $fname );
}
- public function finalizeMasterChanges() {
+ public function finalizeMasterChanges( $fname = __METHOD__, $owner = null ) {
+ $this->assertOwnership( $fname, $owner );
$this->assertTransactionRoundStage( [ self::ROUND_CURSORY, self::ROUND_FINALIZED ] );
$this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
$this->assertTransactionRoundStage( [ self::ROUND_CURSORY, self::ROUND_FINALIZED ] );
$this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
- public function approveMasterChanges( array $options ) {
+ public function approveMasterChanges( array $options, $fname = __METHOD__, $owner = null ) {
+ $this->assertOwnership( $fname, $owner );
$this->assertTransactionRoundStage( self::ROUND_FINALIZED );
$limit = $options['maxWriteDuration'] ?? 0;
$this->assertTransactionRoundStage( self::ROUND_FINALIZED );
$limit = $options['maxWriteDuration'] ?? 0;
$this->trxRoundStage = self::ROUND_APPROVED;
}
$this->trxRoundStage = self::ROUND_APPROVED;
}
- public function beginMasterChanges( $fname = __METHOD__ ) {
+ public function beginMasterChanges( $fname = __METHOD__, $owner = null ) {
+ $this->assertOwnership( $fname, $owner );
if ( $this->trxRoundId !== false ) {
throw new DBTransactionError(
null,
if ( $this->trxRoundId !== false ) {
throw new DBTransactionError(
null,
$this->trxRoundStage = self::ROUND_CURSORY;
}
$this->trxRoundStage = self::ROUND_CURSORY;
}
- public function commitMasterChanges( $fname = __METHOD__ ) {
+ public function commitMasterChanges( $fname = __METHOD__, $owner = null ) {
+ $this->assertOwnership( $fname, $owner );
$this->assertTransactionRoundStage( self::ROUND_APPROVED );
$failures = [];
$this->assertTransactionRoundStage( self::ROUND_APPROVED );
$failures = [];
$this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
}
$this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
}
- public function runMasterTransactionIdleCallbacks() {
+ public function runMasterTransactionIdleCallbacks( $fname = __METHOD__, $owner = null ) {
+ $this->assertOwnership( $fname, $owner );
if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
$type = IDatabase::TRIGGER_COMMIT;
} elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
$type = IDatabase::TRIGGER_COMMIT;
} elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
- public function runMasterTransactionListenerCallbacks() {
+ public function runMasterTransactionListenerCallbacks( $fname = __METHOD__, $owner = null ) {
+ $this->assertOwnership( $fname, $owner );
if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
$type = IDatabase::TRIGGER_COMMIT;
} elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
$type = IDatabase::TRIGGER_COMMIT;
} elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
- public function rollbackMasterChanges( $fname = __METHOD__ ) {
+ public function rollbackMasterChanges( $fname = __METHOD__, $owner = null ) {
+ $this->assertOwnership( $fname, $owner );
+
$restore = ( $this->trxRoundId !== false );
$this->trxRoundId = false;
$this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
$restore = ( $this->trxRoundId !== false );
$this->trxRoundId = false;
$this->trxRoundStage = self::ROUND_ERROR; // "failed" until proven otherwise
/**
* @param string|string[] $stage
/**
* @param string|string[] $stage
+ * @throws DBTransactionError
*/
private function assertTransactionRoundStage( $stage ) {
$stages = (array)$stage;
*/
private function assertTransactionRoundStage( $stage ) {
$stages = (array)$stage;
+ /**
+ * @param string $fname
+ * @param int|null $owner Owner ID of the caller
+ * @throws DBTransactionError
+ */
+ private function assertOwnership( $fname, $owner ) {
+ if ( $this->ownerId !== null && $owner !== $this->ownerId ) {
+ throw new DBTransactionError(
+ null,
+ "$fname: LoadBalancer is owned by LBFactory #{$this->ownerId} (got '$owner')."
+ );
+ }
+ }
+
/**
* Make all DB servers with DBO_DEFAULT/DBO_TRX set join the transaction round
*
/**
* Make all DB servers with DBO_DEFAULT/DBO_TRX set join the transaction round
*