* In web request mode, deferred updates can be run at the end of the request, either before or
* after the HTTP response has been sent. In either case, they run after the DB commit step. If
* an update runs after the response is sent, it will not block clients. If sent before, it will
- * run synchronously. If such an update works via queueing, it will be more likely to complete by
- * the time the client makes their next request after this one.
+ * run synchronously. These two modes are defined via PRESEND and POSTSEND constants, the latter
+ * being the default for addUpdate() and addCallableUpdate().
+ *
+ * Updates that work through this system will be more likely to complete by the time the client
+ * makes their next request after this one than with the JobQueue system.
*
* In CLI mode, updates run immediately if no DB writes are pending. Otherwise, they run when:
* - a) Any waitForReplication() call if no writes are pending on any DB
* - c) EnqueueableDataUpdate tasks may enqueue on commit of Maintenance::getDB( DB_MASTER )
* - d) At the completion of Maintenance::execute()
*
- * When updates are deferred, they use a FIFO queue (one for pre-send and one for post-send).
+ * When updates are deferred, they go into one two FIFO "top-queues" (one for pre-send and one
+ * for post-send). Updates enqueued *during* doUpdate() of a "top" update go into the "sub-queue"
+ * for that update. After that method finishes, the sub-queue is run until drained. This continues
+ * for each top-queue job until the entire top queue is drained. This happens for the pre-send
+ * top-queue, and later on, the post-send top-queue, in execute().
*
* @since 1.19
*/
private static $preSendUpdates = [];
/** @var DeferrableUpdate[] Updates to be deferred until after request end */
private static $postSendUpdates = [];
+ /** @var bool Whether to just run updates in addUpdate() */
+ private static $immediateMode = false;
const ALL = 0; // all updates; in web requests, use only after flushing the output buffer
const PRESEND = 1; // for updates that should run before flushing output buffer
self::push( self::$postSendUpdates, $update );
}
+ if ( self::$immediateMode ) {
+ // No more explicit doUpdates() calls will happen, so run this now
+ self::doUpdates( 'run' );
+ return;
+ }
+
// Try to run the updates now if in CLI mode and no transaction is active.
// This covers scripts that don't/barely use the DB but make updates to other stores.
if ( $wgCommandLineMode ) {
}
}
+ /**
+ * @param bool $value Whether to just immediately run updates in addUpdate()
+ * @since 1.28
+ */
+ public static function setImmediateMode( $value ) {
+ self::$immediateMode = (bool)$value;
+ }
+
/**
* @param DeferrableUpdate[] $queue
* @param DeferrableUpdate $update
}
/**
+ * Immediately run/queue a list of updates
+ *
* @param DeferrableUpdate[] &$queue List of DeferrableUpdate objects
* @param string $mode Use "enqueue" to use the job queue when possible
* @param integer $stage Class constant (PRESEND, POSTSEND) (since 1.28)
* @throws ErrorPageError Happens on top-level calls
* @throws Exception Happens on second-level calls
*/
- public static function execute( array &$queue, $mode, $stage ) {
+ protected static function execute( array &$queue, $mode, $stage ) {
$services = MediaWikiServices::getInstance();
$stats = $services->getStatsdDataFactory();
$lbFactory = $services->getDBLoadBalancerFactory();
$method = RequestContext::getMain()->getRequest()->getMethod();
+ $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+
/** @var ErrorPageError $reportableError */
$reportableError = null;
/** @var DeferrableUpdate[] $updates Snapshot of queue */
// Order will be DataUpdate followed by generic DeferrableUpdate tasks
$updatesByType = [ 'data' => [], 'generic' => [] ];
foreach ( $updates as $du ) {
- $updatesByType[$du instanceof DataUpdate ? 'data' : 'generic'][] = $du;
+ if ( $du instanceof DataUpdate ) {
+ $du->setTransactionTicket( $ticket );
+ $updatesByType['data'][] = $du;
+ } else {
+ $updatesByType['generic'][] = $du;
+ }
+
$name = ( $du instanceof DeferrableCallback )
? get_class( $du ) . '-' . $du->getOrigin()
: get_class( $du );
$firstKey = key( self::$executeContext['subqueue'] );
unset( self::$executeContext['subqueue'][$firstKey] );
+ if ( $subUpdate instanceof DataUpdate ) {
+ $subUpdate->setTransactionTicket( $ticket );
+ }
+
$guiError = self::runUpdate( $subUpdate, $lbFactory, $stage );
$reportableError = $reportableError ?: $guiError;
}
private static function runUpdate( DeferrableUpdate $update, LBFactory $lbFactory, $stage ) {
$guiError = null;
try {
- $lbFactory->beginMasterChanges( __METHOD__ );
+ $fnameTrxOwner = get_class( $update ) . '::doUpdate';
+ $lbFactory->beginMasterChanges( $fnameTrxOwner );
$update->doUpdate();
- $lbFactory->commitMasterChanges( __METHOD__ );
+ $lbFactory->commitMasterChanges( $fnameTrxOwner );
} catch ( Exception $e ) {
// Reporting GUI exceptions does not work post-send
if ( $e instanceof ErrorPageError && $stage === self::PRESEND ) {