*/
public function commitAll( $fname = __METHOD__ ) {
$this->forEachOpenConnection( function ( DatabaseBase $conn ) use ( $fname ) {
- $conn->commit( $fname, 'flush' );
+ $conn->commit( $fname, IDatabase::FLUSHING_ALL_PEERS );
} );
}
public function approveMasterChanges( array $options ) {
$limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
$this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $limit ) {
+ // If atomic sections or explicit transactions are still open, some caller must have
+ // caught an exception but failed to properly rollback any changes. Detect that and
+ // throw and error (causing rollback).
+ if ( $conn->explicitTrxActive() ) {
+ throw new DBTransactionError(
+ $conn,
+ "Explicit transaction still active. A caller may have caught an error."
+ );
+ }
// Assert that the time to replicate the transaction will be sane.
// If this fails, then all DB transactions will be rollback back together.
$time = $conn->pendingWriteQueryDuration();
wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->text()
);
}
+ // If a connection sits idle while slow queries execute on another, that connection
+ // may end up dropped before the commit round is reached. Ping servers to detect this.
+ if ( $conn->writesOrCallbacksPending() && !$conn->ping() ) {
+ throw new DBTransactionError(
+ $conn,
+ "A connection to the {$conn->getDBname()} database was lost before commit."
+ );
+ }
} );
}
public function commitMasterChanges( $fname = __METHOD__ ) {
$this->forEachOpenMasterConnection( function ( DatabaseBase $conn ) use ( $fname ) {
if ( $conn->writesOrCallbacksPending() ) {
- $conn->commit( $fname, 'flush' );
+ $conn->commit( $fname, IDatabase::FLUSHING_ALL_PEERS );
}
} );
}
/**
* Issue all pending post-commit callbacks
+ * @return Exception|null The first exception or null if there were none
* @since 1.28
*/
public function runMasterPostCommitCallbacks() {
- $this->forEachOpenMasterConnection( function ( DatabaseBase $db ) {
+ $e = null; // first exception
+ $this->forEachOpenMasterConnection( function ( DatabaseBase $db ) use ( &$e ) {
$db->setPostCommitCallbackSupression( false );
- $db->runOnTransactionIdleCallbacks( IDatabase::TRIGGER_COMMIT );
+ try {
+ $db->runOnTransactionIdleCallbacks( IDatabase::TRIGGER_COMMIT );
+ } catch ( Exception $ex ) {
+ $e = $e ?: $ex;
+ }
} );
+
+ return $e;
}
/**
foreach ( $conns2[$masterIndex] as $conn ) {
if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
try {
- $conn->rollback( $fname, 'flush' );
+ $conn->rollback( $fname, IDatabase::FLUSHING_ALL_PEERS );
} catch ( DBError $e ) {
MWExceptionHandler::logException( $e );
$failedServers[] = $conn->getServer();