From ccabd0efb05ef07cbca76da18390f68c6c4fc00d Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Wed, 17 Apr 2013 22:05:47 -0700 Subject: [PATCH] Changed on-request job running to shell out instead of doing a loop. * Also factored out a new wfShellExecDisabled() function. * This will keep the process in the background if possible to avoid killing site performance, especially with slow jobs. * This also keep fatals and uncatcheable exceptions from hitting the user. * If $wgPhpCli is not set to an actual path or safe mode is on, then the old code will be used. Change-Id: I6a28152251659ee53eee2604f16d5bf02c85a44f --- includes/GlobalFunctions.php | 40 +++++++++++++++++----------- includes/Wiki.php | 51 ++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 458ab548df..a81a33837e 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -2736,22 +2736,12 @@ function wfEscapeShellArg() { } /** - * Execute a shell command, with time and memory limits mirrored from the PHP - * configuration if supported. - * @param string $cmd Command line, properly escaped for shell. - * @param &$retval null|Mixed optional, will receive the program's exit code. - * (non-zero is usually failure) - * @param array $environ optional environment variables which should be - * added to the executed command environment. - * @param array $limits optional array with limits(filesize, memory, time, walltime) - * this overwrites the global wgShellMax* limits. - * @return string collected stdout as a string (trailing newlines stripped) + * Check if wfShellExec() is effectively disabled via php.ini config + * @return bool|string False or one of (safemode,disabled) + * @since 1.22 */ -function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) { - global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime, - $wgMaxShellWallClockTime, $wgShellCgroup; - - static $disabled; +function wfShellExecDisabled() { + static $disabled = null; if ( is_null( $disabled ) ) { $disabled = false; if ( wfIniGetBool( 'safe_mode' ) ) { @@ -2767,6 +2757,26 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array } } } + return $disabled; +} + +/** + * Execute a shell command, with time and memory limits mirrored from the PHP + * configuration if supported. + * @param string $cmd Command line, properly escaped for shell. + * @param &$retval null|Mixed optional, will receive the program's exit code. + * (non-zero is usually failure) + * @param array $environ optional environment variables which should be + * added to the executed command environment. + * @param array $limits optional array with limits(filesize, memory, time, walltime) + * this overwrites the global wgShellMax* limits. + * @return string collected stdout as a string (trailing newlines stripped) + */ +function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) { + global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime, + $wgMaxShellWallClockTime, $wgShellCgroup; + + $disabled = wfShellExecDisabled(); if ( $disabled ) { $retval = 1; return $disabled == 'safemode' ? diff --git a/includes/Wiki.php b/includes/Wiki.php index 7d0d5af374..2fd12d5e84 100644 --- a/includes/Wiki.php +++ b/includes/Wiki.php @@ -599,7 +599,7 @@ class MediaWiki { * Do a job from the job queue */ private function doJobs() { - global $wgJobRunRate; + global $wgJobRunRate, $wgPhpCli, $IP; if ( $wgJobRunRate <= 0 || wfReadOnly() ) { return; @@ -615,25 +615,36 @@ class MediaWiki { $n = intval( $wgJobRunRate ); } - $group = JobQueueGroup::singleton(); - do { - $job = $group->pop( JobQueueGroup::USE_CACHE ); // job from any queue - if ( $job ) { - $output = $job->toString() . "\n"; - $t = - microtime( true ); - wfProfileIn( __METHOD__ . '-' . get_class( $job ) ); - $success = $job->run(); - wfProfileOut( __METHOD__ . '-' . get_class( $job ) ); - $group->ack( $job ); // done - $t += microtime( true ); - $t = round( $t * 1000 ); - if ( $success === false ) { - $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n"; - } else { - $output .= "Success, Time: $t ms\n"; + if ( !wfShellExecDisabled() && is_executable( $wgPhpCli ) ) { + // Start a background process to run some of the jobs. + // This will be asynchronous on *nix though not on Windows. + wfProfileIn( __METHOD__ . '-exec' ); + $retVal = 1; + $cmd = wfShellWikiCmd( "$IP/maintenance/runJobs.php", array( '--maxjobs', $n ) ); + wfShellExec( "$cmd &", $retVal ); + wfProfileOut( __METHOD__ . '-exec' ); + } else { + // Fallback to running the jobs here while the user waits + $group = JobQueueGroup::singleton(); + do { + $job = $group->pop( JobQueueGroup::USE_CACHE ); // job from any queue + if ( $job ) { + $output = $job->toString() . "\n"; + $t = - microtime( true ); + wfProfileIn( __METHOD__ . '-' . get_class( $job ) ); + $success = $job->run(); + wfProfileOut( __METHOD__ . '-' . get_class( $job ) ); + $group->ack( $job ); // done + $t += microtime( true ); + $t = round( $t * 1000 ); + if ( $success === false ) { + $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n"; + } else { + $output .= "Success, Time: $t ms\n"; + } + wfDebugLog( 'jobqueue', $output ); } - wfDebugLog( 'jobqueue', $output ); - } - } while ( --$n && $job ); + } while ( --$n && $job ); + } } } -- 2.20.1