'TrackBlobs' => __DIR__ . '/maintenance/storage/trackBlobs.php',
'TrackingCategories' => __DIR__ . '/includes/TrackingCategories.php',
'TraditionalImageGallery' => __DIR__ . '/includes/gallery/TraditionalImageGallery.php',
+ 'TransactionRoundDefiningUpdate' => __DIR__ . '/includes/deferred/TransactionRoundDefiningUpdate.php',
'TransformParameterError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
'TransformTooBigImageAreaError' => __DIR__ . '/includes/media/MediaTransformOutput.php',
'TransformationalImageHandler' => __DIR__ . '/includes/media/TransformationalImageHandler.php',
// Run only the job enqueue logic to complete the update later
$spec = $update->getAsJobSpecification();
JobQueueGroup::singleton( $spec['wiki'] )->push( $spec['job'] );
+ } elseif ( $update instanceof TransactionRoundDefiningUpdate ) {
+ $update->doUpdate();
} else {
// Run the bulk of the update now
$fnameTrxOwner = get_class( $update ) . '::doUpdate';
--- /dev/null
+<?php
+
+/**
+ * Deferrable update for closure/callback updates that need LBFactory and Database
+ * to be outside any active transaction round.
+ *
+ * @since 1.31
+ */
+class TransactionRoundDefiningUpdate implements DeferrableUpdate, DeferrableCallback {
+ /** @var callable|null */
+ private $callback;
+ /** @var string */
+ private $fname;
+
+ /**
+ * @param callable $callback
+ * @param string $fname Calling method
+ */
+ public function __construct( callable $callback, $fname = 'unknown' ) {
+ $this->callback = $callback;
+ $this->fname = $fname;
+ }
+
+ public function doUpdate() {
+ call_user_func( $this->callback );
+ }
+
+ public function getOrigin() {
+ return $this->fname;
+ }
+}
<?php
+use MediaWiki\MediaWikiServices;
+
class DeferredUpdatesTest extends MediaWikiTestCase {
/**
$this->assertTrue( $x, "Outer POSTSEND update ran" );
$this->assertTrue( $y, "Nested PRESEND update ran" );
}
+
+ /**
+ * @covers DeferredUpdates::runUpdate
+ */
+ public function testRunUpdateTransactionScope() {
+ $this->setMwGlobals( 'wgCommandLineMode', false );
+
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $this->assertFalse( $lbFactory->hasTransactionRound(), 'Initial state' );
+
+ $ran = 0;
+ DeferredUpdates::addCallableUpdate( function () use ( &$ran, $lbFactory ) {
+ $ran++;
+ $this->assertTrue( $lbFactory->hasTransactionRound(), 'Has transaction' );
+ } );
+ DeferredUpdates::doUpdates();
+
+ $this->assertSame( 1, $ran, 'Update ran' );
+ $this->assertFalse( $lbFactory->hasTransactionRound(), 'Final state' );
+ }
+
+ /**
+ * @covers DeferredUpdates::runUpdate
+ * @covers TransactionRoundDefiningUpdate::getOrigin
+ */
+ public function testRunOuterScopeUpdate() {
+ $this->setMwGlobals( 'wgCommandLineMode', false );
+
+ $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ $this->assertFalse( $lbFactory->hasTransactionRound(), 'Initial state' );
+
+ $ran = 0;
+ DeferredUpdates::addUpdate( new TransactionRoundDefiningUpdate(
+ function () use ( &$ran, $lbFactory ) {
+ $ran++;
+ $this->assertFalse( $lbFactory->hasTransactionRound(), 'No transaction' );
+ } )
+ );
+ DeferredUpdates::doUpdates();
+
+ $this->assertSame( 1, $ran, 'Update ran' );
+ }
}
--- /dev/null
+<?php
+
+/**
+ * @covers TransactionRoundDefiningUpdate
+ */
+class TransactionRoundDefiningUpdateTest extends PHPUnit_Framework_TestCase {
+
+ public function testDoUpdate() {
+ $ran = 0;
+ $update = new TransactionRoundDefiningUpdate( function () use ( &$ran ) {
+ $ran++;
+ } );
+ $this->assertSame( 0, $ran );
+ $update->doUpdate();
+ $this->assertSame( 1, $ran );
+ }
+}