From 0a13e6557d524ca951e71eea1cbcb6271c0a413f Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Fri, 23 Sep 2016 23:03:56 -0700 Subject: [PATCH] Set ignore_user_abort( true ) during DB commit for sanity MediaWiki already does this in preOutputCommit(), but it should also be in the DB layer. Change-Id: I3193c236a7823f27938077b372887867aeaf78c5 --- .../ChronologyProtector.php | 2 +- includes/libs/rdbms/lbfactory/ILBFactory.php | 6 +++--- includes/libs/rdbms/lbfactory/LBFactory.php | 19 ++++++++++++++++++ .../libs/rdbms/loadbalancer/LoadBalancer.php | 20 +++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/includes/libs/rdbms/chronologyprotector/ChronologyProtector.php b/includes/libs/rdbms/chronologyprotector/ChronologyProtector.php index 94a3b6ce1e..1f9aff161e 100644 --- a/includes/libs/rdbms/chronologyprotector/ChronologyProtector.php +++ b/includes/libs/rdbms/chronologyprotector/ChronologyProtector.php @@ -27,7 +27,7 @@ use Psr\Log\LoggerInterface; * Class for ensuring a consistent ordering of events as seen by the user, despite replication. * Kind of like Hawking's [[Chronology Protection Agency]]. */ -class ChronologyProtector implements LoggerAwareInterface{ +class ChronologyProtector implements LoggerAwareInterface { /** @var BagOStuff */ protected $store; /** @var LoggerInterface */ diff --git a/includes/libs/rdbms/lbfactory/ILBFactory.php b/includes/libs/rdbms/lbfactory/ILBFactory.php index d7ca7cd1a9..9c9f18d2b0 100644 --- a/includes/libs/rdbms/lbfactory/ILBFactory.php +++ b/includes/libs/rdbms/lbfactory/ILBFactory.php @@ -136,9 +136,9 @@ interface ILBFactory { public function flushReplicaSnapshots( $fname = __METHOD__ ); /** - * 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. + * Commit open transactions on all connections. This is useful for two main cases: + * - a) To commit changes to the masters. + * - b) To release the snapshot on all connections, master and replica DBs. * @param string $fname Caller name * @param array $options Options map: * - maxWriteDuration: abort if more than this much time was spent in write queries diff --git a/includes/libs/rdbms/lbfactory/LBFactory.php b/includes/libs/rdbms/lbfactory/LBFactory.php index 85194bc009..0635d04c7b 100644 --- a/includes/libs/rdbms/lbfactory/LBFactory.php +++ b/includes/libs/rdbms/lbfactory/LBFactory.php @@ -178,6 +178,8 @@ abstract class LBFactory implements ILBFactory { "$fname: transaction round '{$this->trxRoundId}' still running." ); } + /** @noinspection PhpUnusedLocalVariableInspection */ + $scope = $this->getScopedPHPBehaviorForCommit(); // try to ignore client aborts // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure $this->forEachLBCallMethod( 'finalizeMasterChanges' ); $this->trxRoundId = false; @@ -516,6 +518,23 @@ abstract class LBFactory implements ILBFactory { $this->requestInfo = $info + $this->requestInfo; } + /** + * Make PHP ignore user aborts/disconnects until the returned + * value leaves scope. This returns null and does nothing in CLI mode. + * + * @return ScopedCallback|null + */ + final protected function getScopedPHPBehaviorForCommit() { + if ( PHP_SAPI != 'cli' ) { // http://bugs.php.net/bug.php?id=47540 + $old = ignore_user_abort( true ); // avoid half-finished operations + return new ScopedCallback( function () use ( $old ) { + ignore_user_abort( $old ); + } ); + } + + return null; + } + function __destruct() { $this->destroy(); } diff --git a/includes/libs/rdbms/loadbalancer/LoadBalancer.php b/includes/libs/rdbms/loadbalancer/LoadBalancer.php index 36e4c6c662..7ad275a96c 100644 --- a/includes/libs/rdbms/loadbalancer/LoadBalancer.php +++ b/includes/libs/rdbms/loadbalancer/LoadBalancer.php @@ -1101,6 +1101,9 @@ class LoadBalancer implements ILoadBalancer { public function commitMasterChanges( $fname = __METHOD__ ) { $failures = []; + /** @noinspection PhpUnusedLocalVariableInspection */ + $scope = $this->getScopedPHPBehaviorForCommit(); // try to ignore client aborts + $restore = ( $this->trxRoundId !== false ); $this->trxRoundId = false; $this->forEachOpenMasterConnection( @@ -1516,6 +1519,23 @@ class LoadBalancer implements ILoadBalancer { } ); } + /** + * Make PHP ignore user aborts/disconnects until the returned + * value leaves scope. This returns null and does nothing in CLI mode. + * + * @return ScopedCallback|null + */ + final protected function getScopedPHPBehaviorForCommit() { + if ( PHP_SAPI != 'cli' ) { // http://bugs.php.net/bug.php?id=47540 + $old = ignore_user_abort( true ); // avoid half-finished operations + return new ScopedCallback( function () use ( $old ) { + ignore_user_abort( $old ); + } ); + } + + return null; + } + function __destruct() { // Avoid connection leaks for sanity $this->closeAll(); -- 2.20.1