'AuthManagerSpecialPage' => __DIR__ . '/includes/specialpage/AuthManagerSpecialPage.php',
'AuthPlugin' => __DIR__ . '/includes/AuthPlugin.php',
'AuthPluginUser' => __DIR__ . '/includes/AuthPlugin.php',
+ 'AutoCommitUpdate' => __DIR__ . '/includes/deferred/AutoCommitUpdate.php',
'AutoLoader' => __DIR__ . '/includes/AutoLoader.php',
'AutoloadGenerator' => __DIR__ . '/includes/utils/AutoloadGenerator.php',
'Autopromote' => __DIR__ . '/includes/Autopromote.php',
return true; // avoid gap locking if we know it's not there
}
- $method = __METHOD__;
- $dbw = wfGetDB( DB_MASTER );
$conds = $this->pageCond();
- $dbw->onTransactionIdle( function () use ( $dbw, $conds, $method, $purgeTime ) {
- $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
- $dbw->update(
- 'page',
- [ 'page_touched' => $dbTimestamp ],
- $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
- $method
- );
- } );
+ DeferredUpdates::addUpdate(
+ new AutoCommitUpdate(
+ wfGetDB( DB_MASTER ),
+ __METHOD__,
+ function ( IDatabase $dbw, $fname ) use ( $conds, $purgeTime ) {
+ $dbTimestamp = $dbw->timestamp( $purgeTime ?: time() );
+ $dbw->update(
+ 'page',
+ [ 'page_touched' => $dbTimestamp ],
+ $conds + [ 'page_touched < ' . $dbw->addQuotes( $dbTimestamp ) ],
+ $fname
+ );
+ }
+ ),
+ DeferredUpdates::PRESEND
+ );
return true;
}
private $dbw;
/** @var string */
private $fname;
- /** @var Closure|callable */
+ /** @var callable */
private $callback;
/**
* @param IDatabase $dbw
* @param string $fname Caller name (usually __METHOD__)
* @param callable $callback
- * @throws InvalidArgumentException
* @see IDatabase::doAtomicSection()
*/
- public function __construct( IDatabase $dbw, $fname, $callback ) {
+ public function __construct( IDatabase $dbw, $fname, callable $callback ) {
$this->dbw = $dbw;
$this->fname = $fname;
- if ( !is_callable( $callback ) ) {
- throw new InvalidArgumentException( 'Not a valid callback/closure!' );
- }
$this->callback = $callback;
+
+ if ( $this->dbw->trxLevel() ) {
+ $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+ }
}
public function doUpdate() {
- $this->dbw->doAtomicSection( $this->fname, $this->callback );
+ if ( $this->callback ) {
+ $this->dbw->doAtomicSection( $this->fname, $this->callback );
+ }
+ }
+
+ public function cancelOnRollback( $trigger ) {
+ if ( $trigger === IDatabase::TRIGGER_ROLLBACK ) {
+ $this->callback = null;
+ }
}
}
--- /dev/null
+<?php
+
+/**
+ * Deferrable Update for closure/callback updates that should use auto-commit mode
+ * @since 1.28
+ */
+class AutoCommitUpdate implements DeferrableUpdate {
+ /** @var IDatabase */
+ private $dbw;
+ /** @var string */
+ private $fname;
+ /** @var callable */
+ private $callback;
+
+ /**
+ * @param IDatabase $dbw
+ * @param string $fname Caller name (usually __METHOD__)
+ * @param callable $callback Callback that takes (IDatabase, method name string)
+ */
+ public function __construct( IDatabase $dbw, $fname, callable $callback ) {
+ $this->dbw = $dbw;
+ $this->fname = $fname;
+ $this->callback = $callback;
+
+ if ( $this->dbw->trxLevel() ) {
+ $this->dbw->onTransactionResolution( [ $this, 'cancelOnRollback' ] );
+ }
+ }
+
+ public function doUpdate() {
+ if ( !$this->callback ) {
+ return;
+ }
+
+ $autoTrx = $this->dbw->getFlag( DBO_TRX );
+ $this->dbw->clearFlag( DBO_TRX );
+ try {
+ /** @var Exception $e */
+ $e = null;
+ call_user_func_array( $this->callback, [ $this->dbw, $this->fname ] );
+ } catch ( Exception $e ) {
+ }
+ if ( $autoTrx ) {
+ $this->dbw->setFlag( DBO_TRX );
+ }
+ if ( $e ) {
+ throw $e;
+ }
+ }
+
+ public function cancelOnRollback( $trigger ) {
+ if ( $trigger === IDatabase::TRIGGER_ROLLBACK ) {
+ $this->callback = null;
+ }
+ }
+}
// Purge the source and target files...
$oldTitleFile = wfLocalFile( $this->title );
$newTitleFile = wfLocalFile( $target );
- // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
- // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
- $this->getRepo()->getMasterDB()->onTransactionIdle(
- function () use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
- $oldTitleFile->purgeEverything();
- foreach ( $archiveNames as $archiveName ) {
- $oldTitleFile->purgeOldThumbnails( $archiveName );
+ // To avoid slow purges in the transaction, move them outside...
+ DeferredUpdates::addUpdate(
+ new AutoCommitUpdate(
+ $this->getRepo()->getMasterDB(),
+ __METHOD__,
+ function () use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
+ $oldTitleFile->purgeEverything();
+ foreach ( $archiveNames as $archiveName ) {
+ $oldTitleFile->purgeOldThumbnails( $archiveName );
+ }
+ $newTitleFile->purgeEverything();
}
- $newTitleFile->purgeEverything();
- }
+ ),
+ DeferredUpdates::PRESEND
);
if ( $status->isOK() ) {
$this->lock(); // begin
$batch->addCurrent();
- # Get old version relative paths
+ // Get old version relative paths
$archiveNames = $batch->addOlds();
$status = $batch->execute();
$this->unlock(); // done
DeferredUpdates::addUpdate( SiteStatsUpdate::factory( [ 'images' => -1 ] ) );
}
- // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
- // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
- $that = $this;
- $this->getRepo()->getMasterDB()->onTransactionIdle(
- function () use ( $that, $archiveNames ) {
- $that->purgeEverything();
- foreach ( $archiveNames as $archiveName ) {
- $that->purgeOldThumbnails( $archiveName );
+ // To avoid slow purges in the transaction, move them outside...
+ DeferredUpdates::addUpdate(
+ new AutoCommitUpdate(
+ $this->getRepo()->getMasterDB(),
+ __METHOD__,
+ function () use ( $archiveNames ) {
+ $this->purgeEverything();
+ foreach ( $archiveNames as $archiveName ) {
+ $this->purgeOldThumbnails( $archiveName );
+ }
}
- }
+ ),
+ DeferredUpdates::PRESEND
);
// Purge the CDN