SpecialRunJobs: Use MediaWiki's built-in async/post-send mode

Disabling OutputPage is fine, but disabling MediaWiki's own
output handling from MediaWiki.php as well (with ignore_user_abort
and ob_flush) made this code incompatible with becoming an API module.

Make use of DeferredUpdates instead, MediaWiki's built-in post-send
mechanism.

Bug: T175146
Change-Id: Ia131341d447fd6501a070da89cd3a2af470e0f7d
This commit is contained in:
Timo Tijhof 2017-10-12 01:01:58 +01:00 committed by Aaron Schulz
parent e994e89e76
commit bfa2cf4246

View file

@ -39,17 +39,21 @@ class SpecialRunJobs extends UnlistedSpecialPage {
public function execute( $par = '' ) {
$this->getOutput()->disable();
if ( wfReadOnly() ) {
wfHttpError( 423, 'Locked', 'Wiki is in read-only mode.' );
return;
} elseif ( !$this->getRequest()->wasPosted() ) {
}
// Validate request method
if ( !$this->getRequest()->wasPosted() ) {
wfHttpError( 400, 'Bad Request', 'Request must be POSTed.' );
return;
}
// Validate request parameters
$optional = [ 'maxjobs' => 0, 'maxtime' => 30, 'type' => false, 'async' => true ];
$required = array_flip( [ 'title', 'tasks', 'signature', 'sigexpiry' ] );
$params = array_intersect_key( $this->getRequest()->getValues(), $required + $optional );
$missing = array_diff_key( $required, $params );
if ( count( $missing ) ) {
@ -59,11 +63,11 @@ class SpecialRunJobs extends UnlistedSpecialPage {
return;
}
// Validate request signature
$squery = $params;
unset( $squery['signature'] );
$correctSignature = self::getQuerySignature( $squery, $this->getConfig()->get( 'SecretKey' ) );
$providedSignature = $params['signature'];
$verified = is_string( $providedSignature )
&& hash_equals( $correctSignature, $providedSignature );
if ( !$verified || $params['sigexpiry'] < time() ) {
@ -75,39 +79,34 @@ class SpecialRunJobs extends UnlistedSpecialPage {
$params += $optional;
if ( $params['async'] ) {
// Client will usually disconnect before checking the response,
// but it needs to know when it is safe to disconnect. Until this
// reaches ignore_user_abort(), it is not safe as the jobs won't run.
ignore_user_abort( true ); // jobs may take a bit of time
// HTTP 202 Accepted
HttpStatus::header( 202 );
ob_flush();
flush();
// Once the client receives this response, it can disconnect
set_error_handler( function ( $errno, $errstr ) {
if ( strpos( $errstr, 'Cannot modify header information' ) !== false ) {
return true; // bug T115413
}
// Delegate unhandled errors to the default MediaWiki handler
// so that fatal errors get proper logging (T89169)
return call_user_func_array(
'MWExceptionHandler::handleError', func_get_args()
);
} );
// Clients are meant to disconnect without waiting for the full response.
// Let the page output happen before the jobs start, so that clients know it's
// safe to disconnect. MediaWiki::preOutputCommit() calls ignore_user_abort()
// or similar to make sure we stay alive to run the deferred update.
DeferredUpdates::addUpdate(
new TransactionRoundDefiningUpdate(
function () use ( $params ) {
$this->doRun( $params );
},
__METHOD__
),
DeferredUpdates::POSTSEND
);
} else {
$this->doRun( $params );
print "Done\n";
}
}
// Do all of the specified tasks...
if ( in_array( 'jobs', explode( '|', $params['tasks'] ) ) ) {
$runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) );
$runner->run( [
'type' => $params['type'],
'maxJobs' => $params['maxjobs'] ? $params['maxjobs'] : 1,
'maxTime' => $params['maxtime'] ? $params['maxjobs'] : 30
] );
if ( !$params['async'] ) {
print "Done\n";
}
}
protected function doRun( array $params ) {
$runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) );
$runner->run( [
'type' => $params['type'],
'maxJobs' => $params['maxjobs'] ?: 1,
'maxTime' => $params['maxtime'] ?: 30
] );
}
/**