23 use Wikimedia\ScopedCallback;
97 public const PRESEND = 1;
99 public const POSTSEND = 2;
102 public const STAGES = [ self::PRESEND, self::POSTSEND ];
105 private const BIG_QUEUE_SIZE = 100;
108 private static $scopeStack;
113 private static $preventOpportunisticUpdates = 0;
120 return self::$scopeStack;
128 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
129 throw new LogicException(
'Cannot reconfigure DeferredUpdates outside tests' );
131 self::$scopeStack = $scopeStack;
155 self::getScopeStack()->current()->addUpdate( $update, $stage );
156 self::tryOpportunisticExecute();
183 $type = get_class( $update )
185 $updateId = spl_object_id( $update );
186 $logger->debug( __METHOD__ .
": started $type #$updateId" );
188 $updateException =
null;
190 $startTime = microtime(
true );
192 self::attemptUpdate( $update );
193 }
catch ( Throwable $updateException ) {
196 "Deferred update '{deferred_type}' failed to run.",
198 'deferred_type' => $type,
199 'exception' => $updateException,
202 self::getScopeStack()->onRunUpdateFailed( $update );
204 $walltime = microtime(
true ) - $startTime;
205 $logger->debug( __METHOD__ .
": ended $type #$updateId, processing time: $walltime" );
211 self::getScopeStack()->queueDataUpdate( $update );
212 }
catch ( Throwable $jobException ) {
215 "Deferred update '{deferred_type}' failed to enqueue as a job.",
217 'deferred_type' => $type,
218 'exception' => $jobException,
221 self::getScopeStack()->onRunUpdateFailed( $update );
225 return $updateException;
246 public static function doUpdates( $stage = self::ALL ) {
252 $scope = self::getScopeStack()->current();
255 $activeUpdate = $scope->getActiveUpdate();
256 if ( $activeUpdate ) {
257 $class = get_class( $activeUpdate );
259 throw new LogicException(
260 __METHOD__ .
": reached from $class, which is not TransactionRoundAwareUpdate"
263 if ( $activeUpdate->getTransactionRoundRequirement() !== $activeUpdate::TRX_ROUND_ABSENT ) {
264 throw new LogicException(
265 __METHOD__ .
": reached from $class, which does not specify TRX_ROUND_ABSENT"
270 $scope->processUpdates(
272 static function (
DeferrableUpdate $update, $activeStage ) use ( &$guiError, &$exception ) {
273 $scopeStack = self::getScopeStack();
274 $childScope = $scopeStack->descend( $activeStage, $update );
276 $e = self::run( $update );
277 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
278 $exception = $exception ?: $e;
284 $childScope->processUpdates(
287 $e = self::run( $sub );
288 $guiError = $guiError ?: ( $e instanceof
ErrorPageError ? $e : null );
289 $exception = $exception ?: $e;
293 $scopeStack->ascend();
300 if ( $exception && defined(
'MW_PHPUNIT_TEST' ) ) {
308 if ( $guiError && $stage === self::PRESEND && !headers_sent() ) {
352 if ( self::getRecursiveExecutionStackDepth()
353 || self::$preventOpportunisticUpdates
358 if ( self::getScopeStack()->allowOpportunisticUpdates() ) {
359 self::doUpdates( self::ALL );
363 if ( self::pendingUpdatesCount() >= self::BIG_QUEUE_SIZE ) {
372 self::getScopeStack()->current()->consumeMatchingUpdates(
374 EnqueueableDataUpdate::class,
376 self::getScopeStack()->queueDataUpdate( $update );
391 self::$preventOpportunisticUpdates++;
392 return new ScopedCallback(
static function () {
393 self::$preventOpportunisticUpdates--;
407 return self::getScopeStack()->current()->pendingUpdatesCount();
423 return self::getScopeStack()->current()->getPendingUpdates( $stage );
435 self::getScopeStack()->current()->clearPendingUpdates();
445 return self::getScopeStack()->getRecursiveDepth();
461 self::getScopeStack()->onRunUpdateStart( $update );
465 self::getScopeStack()->onRunUpdateEnd( $update );
wfGetCaller( $level=2)
Get the name of the function which called this function wfGetCaller( 1 ) is the function with the wfG...
if(!defined('MW_SETUP_CALLBACK'))
DeferredUpdates helper class for tracking DeferrableUpdate::doUpdate() nesting levels caused by neste...
Defer callable updates to run later in the PHP process.
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the pending update queue for execution at the appropriate time.
static tryOpportunisticExecute()
Consume and execute pending updates now if possible, instead of waiting.
static pendingUpdatesCount()
Get the number of pending updates for the current execution context.
static getRecursiveExecutionStackDepth()
Get the number of in-progress calls to DeferredUpdates::doUpdates()
static clearPendingUpdates()
Cancel all pending updates for the current execution context.
static setScopeStack(DeferredUpdatesScopeStack $scopeStack)
static attemptUpdate(DeferrableUpdate $update)
Attempt to run an update with the appropriate transaction round state if needed.
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add an update to the pending update queue that invokes the specified callback when run.
static doUpdates( $stage=self::ALL)
Consume and execute all pending updates.
static preventOpportunisticUpdates()
Prevent opportunistic updates until the returned ScopedCallback is consumed.
static getPendingUpdates( $stage=self::ALL)
Get a list of the pending updates for the current execution context.
An error page which can definitely be safely rendered using the OutputPage.
DeferrableUpdate for closure/callable.
static logException(Throwable $e, $catcher=self::CAUGHT_BY_OTHER, $extraData=[])
Log a throwable to the exception log (if enabled).
Callback wrapper that has an originating method.
Interface that deferrable updates should implement.
doUpdate()
Perform the actual work.
Interface that marks a DataUpdate as enqueuable via the JobQueue.
Deferrable update that specifies whether it must run outside of any explicit LBFactory transaction ro...