From 32a8eb1822c3572ae595ca7065f57662ec3a94ff Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Sat, 13 Aug 2016 12:28:53 -0700 Subject: [PATCH] Make various MWCallableUpdate usages rollback-safe Support for this added to MWCallableUpdate and its helper method in DeferredUpdates. Bug: T141988 Change-Id: Ief150ea655036e2708557dd8ae847379edf21933 --- includes/WatchedItemStore.php | 4 +- includes/changes/RecentChange.php | 48 +++++++++++++---------- includes/deferred/AtomicSectionUpdate.php | 2 +- includes/deferred/AutoCommitUpdate.php | 2 +- includes/deferred/DeferredUpdates.php | 7 +++- includes/deferred/MWCallableUpdate.php | 19 +++++++-- includes/page/WikiPage.php | 10 +++-- includes/revisiondelete/RevDelList.php | 3 +- 8 files changed, 63 insertions(+), 32 deletions(-) diff --git a/includes/WatchedItemStore.php b/includes/WatchedItemStore.php index 89ca50c00b..b8a47fae40 100644 --- a/includes/WatchedItemStore.php +++ b/includes/WatchedItemStore.php @@ -761,7 +761,9 @@ class WatchedItemStore implements StatsdAwareInterface { $this->uncacheLinkTarget( $target ); $this->reuseConnection( $dbw ); - } + }, + DeferredUpdates::POSTSEND, + $dbw ); } diff --git a/includes/changes/RecentChange.php b/includes/changes/RecentChange.php index 159cfd93de..daf76dfedd 100644 --- a/includes/changes/RecentChange.php +++ b/includes/changes/RecentChange.php @@ -589,16 +589,20 @@ class RecentChange { 'pageStatus' => 'changed' ]; - DeferredUpdates::addCallableUpdate( function() use ( $rc, $tags ) { - $rc->save(); - if ( $rc->mAttribs['rc_patrolled'] ) { - PatrolLog::record( $rc, true, $rc->getPerformer() ); - } - if ( count( $tags ) ) { - ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'], - $rc->mAttribs['rc_this_oldid'], null, null ); - } - } ); + DeferredUpdates::addCallableUpdate( + function () use ( $rc, $tags ) { + $rc->save(); + if ( $rc->mAttribs['rc_patrolled'] ) { + PatrolLog::record( $rc, true, $rc->getPerformer() ); + } + if ( count( $tags ) ) { + ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'], + $rc->mAttribs['rc_this_oldid'], null, null ); + } + }, + DeferredUpdates::POSTSEND, + wfGetDB( DB_MASTER ) + ); return $rc; } @@ -661,16 +665,20 @@ class RecentChange { 'pageStatus' => 'created' ]; - DeferredUpdates::addCallableUpdate( function() use ( $rc, $tags ) { - $rc->save(); - if ( $rc->mAttribs['rc_patrolled'] ) { - PatrolLog::record( $rc, true, $rc->getPerformer() ); - } - if ( count( $tags ) ) { - ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'], - $rc->mAttribs['rc_this_oldid'], null, null ); - } - } ); + DeferredUpdates::addCallableUpdate( + function () use ( $rc, $tags ) { + $rc->save(); + if ( $rc->mAttribs['rc_patrolled'] ) { + PatrolLog::record( $rc, true, $rc->getPerformer() ); + } + if ( count( $tags ) ) { + ChangeTags::addTags( $tags, $rc->mAttribs['rc_id'], + $rc->mAttribs['rc_this_oldid'], null, null ); + } + }, + DeferredUpdates::POSTSEND, + wfGetDB( DB_MASTER ) + ); return $rc; } diff --git a/includes/deferred/AtomicSectionUpdate.php b/includes/deferred/AtomicSectionUpdate.php index 0da5d7dde3..a348719211 100644 --- a/includes/deferred/AtomicSectionUpdate.php +++ b/includes/deferred/AtomicSectionUpdate.php @@ -9,7 +9,7 @@ class AtomicSectionUpdate implements DeferrableUpdate, DeferrableCallback { private $dbw; /** @var string */ private $fname; - /** @var callable */ + /** @var callable|null */ private $callback; /** diff --git a/includes/deferred/AutoCommitUpdate.php b/includes/deferred/AutoCommitUpdate.php index ef5903b668..d26cf9d1c6 100644 --- a/includes/deferred/AutoCommitUpdate.php +++ b/includes/deferred/AutoCommitUpdate.php @@ -9,7 +9,7 @@ class AutoCommitUpdate implements DeferrableUpdate, DeferrableCallback { private $dbw; /** @var string */ private $fname; - /** @var callable */ + /** @var callable|null */ private $callback; /** diff --git a/includes/deferred/DeferredUpdates.php b/includes/deferred/DeferredUpdates.php index 9768838c0a..ee14e1a89d 100644 --- a/includes/deferred/DeferredUpdates.php +++ b/includes/deferred/DeferredUpdates.php @@ -68,9 +68,12 @@ class DeferredUpdates { * * @param callable $callable * @param integer $type DeferredUpdates constant (PRESEND or POSTSEND) (since 1.27) + * @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28) */ - public static function addCallableUpdate( $callable, $type = self::POSTSEND ) { - self::addUpdate( new MWCallableUpdate( $callable, wfGetCaller() ), $type ); + public static function addCallableUpdate( + $callable, $type = self::POSTSEND, IDatabase $dbw = null + ) { + self::addUpdate( new MWCallableUpdate( $callable, wfGetCaller(), $dbw ), $type ); } /** diff --git a/includes/deferred/MWCallableUpdate.php b/includes/deferred/MWCallableUpdate.php index d63c292ba9..47b162c270 100644 --- a/includes/deferred/MWCallableUpdate.php +++ b/includes/deferred/MWCallableUpdate.php @@ -4,7 +4,7 @@ * Deferrable Update for closure/callback */ class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback { - /** @var callable */ + /** @var callable|null */ private $callback; /** @var string */ private $fname; @@ -12,14 +12,27 @@ class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback { /** * @param callable $callback * @param string $fname Calling method + * @param IDatabase|null $dbw Abort if this DB is rolled back [optional] (since 1.28) */ - public function __construct( callable $callback, $fname = 'unknown' ) { + public function __construct( callable $callback, $fname = 'unknown', IDatabase $dbw = null ) { $this->callback = $callback; $this->fname = $fname; + + if ( $dbw && $dbw->trxLevel() ) { + $dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] ); + } } public function doUpdate() { - call_user_func( $this->callback ); + if ( $this->callback ) { + call_user_func( $this->callback ); + } + } + + public function cancelOnRollback( $trigger ) { + if ( $trigger === IDatabase::TRIGGER_ROLLBACK ) { + $this->callback = null; + } } public function getOrigin() { diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index 8eba1ccf7b..8545c36c07 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -887,9 +887,13 @@ class WikiPage implements Page, IDBAccessObject { // Update the DB post-send if the page has not cached since now $that = $this; $latest = $this->getLatest(); - DeferredUpdates::addCallableUpdate( function() use ( $that, $retval, $latest ) { - $that->insertRedirectEntry( $retval, $latest ); - } ); + DeferredUpdates::addCallableUpdate( + function () use ( $that, $retval, $latest ) { + $that->insertRedirectEntry( $retval, $latest ); + }, + DeferredUpdates::POSTSEND, + wfGetDB( DB_MASTER ) + ); return $retval; } diff --git a/includes/revisiondelete/RevDelList.php b/includes/revisiondelete/RevDelList.php index d365bf6fe5..48604e1a01 100644 --- a/includes/revisiondelete/RevDelList.php +++ b/includes/revisiondelete/RevDelList.php @@ -275,7 +275,8 @@ abstract class RevDelList extends RevisionListBase { function () use ( $visibilityChangeMap ) { $this->doPostCommitUpdates( $visibilityChangeMap ); }, - DeferredUpdates::PRESEND + DeferredUpdates::PRESEND, + $dbw ); $dbw->endAtomic( __METHOD__ ); -- 2.20.1