exception: Tolerate no service container when trying DB rollback
If the exception handler gets triggered before a service container is available, it should not attempt a DB rollback. Attempting to access the DBLoadBalancerFactory service object would cause a secondary error, obscuring the original issue. This might happen when the exception handler is triggered during bootstrapping or in the installer. Change-Id: I644bd0953aa5e690fea16d9fc11ca3f24cb3f104
This commit is contained in:
parent
c4a818d5e7
commit
085b9d18c6
1 changed files with 38 additions and 18 deletions
|
|
@ -138,6 +138,43 @@ class MWExceptionHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll back any open database transactions
|
||||
*
|
||||
* This method is used to attempt to recover from exceptions
|
||||
*/
|
||||
private static function rollbackPrimaryChanges() {
|
||||
if ( !MediaWikiServices::hasInstance() ) {
|
||||
// MediaWiki isn't fully initialized yet, it's not safe to access services.
|
||||
// This also means that there's nothing to roll back yet.
|
||||
return;
|
||||
}
|
||||
|
||||
$services = MediaWikiServices::getInstance();
|
||||
if ( $services->isServiceDisabled( 'DBLoadBalancerFactory' ) ) {
|
||||
// The DBLoadBalancerFactory is disabled, possibly because we are in the installer,
|
||||
// or we are in the process of shutting MediaWiki. At this point, any DB transactions
|
||||
// would already have been committed or rolled back.
|
||||
return;
|
||||
}
|
||||
|
||||
// Roll back DBs to avoid transaction notices. This might fail
|
||||
// to roll back some databases due to connection issues or exceptions.
|
||||
// However, any sensible DB driver will roll back implicitly anyway.
|
||||
try {
|
||||
$lbFactory = $services->getDBLoadBalancerFactory();
|
||||
$lbFactory->rollbackPrimaryChanges( __METHOD__ );
|
||||
$lbFactory->flushPrimarySessions( __METHOD__ );
|
||||
} catch ( DBError $e ) {
|
||||
// If the DB is unreachable, rollback() will throw an error
|
||||
// and the error report() method might need messages from the DB,
|
||||
// which would result in an exception loop. PHP may escalate such
|
||||
// errors to "Exception thrown without a stack frame" fatals, but
|
||||
// it's better to be explicit here.
|
||||
self::logException( $e, self::CAUGHT_BY_HANDLER );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll back any open database transactions and log the stack trace of the throwable
|
||||
*
|
||||
|
|
@ -151,24 +188,7 @@ class MWExceptionHandler {
|
|||
Throwable $e,
|
||||
$catcher = self::CAUGHT_BY_OTHER
|
||||
) {
|
||||
$services = MediaWikiServices::getInstance();
|
||||
if ( !$services->isServiceDisabled( 'DBLoadBalancerFactory' ) ) {
|
||||
// Rollback DBs to avoid transaction notices. This might fail
|
||||
// to rollback some databases due to connection issues or exceptions.
|
||||
// However, any sensible DB driver will rollback implicitly anyway.
|
||||
try {
|
||||
$lbFactory = $services->getDBLoadBalancerFactory();
|
||||
$lbFactory->rollbackPrimaryChanges( __METHOD__ );
|
||||
$lbFactory->flushPrimarySessions( __METHOD__ );
|
||||
} catch ( DBError $e2 ) {
|
||||
// If the DB is unreachable, rollback() will throw an error
|
||||
// and the error report() method might need messages from the DB,
|
||||
// which would result in an exception loop. PHP may escalate such
|
||||
// errors to "Exception thrown without a stack frame" fatals, but
|
||||
// it's better to be explicit here.
|
||||
self::logException( $e2, $catcher );
|
||||
}
|
||||
}
|
||||
self::rollbackPrimaryChanges();
|
||||
|
||||
self::logException( $e, $catcher );
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue