MaxSem has uploaded a new change for review.
https://gerrit.wikimedia.org/r/148280
Change subject: Configuration rewrite, part 1
......................................................................
Configuration rewrite, part 1
Switch from ini config to JSON, ditch most command-line parameters.
To preserve testability for now, retain current weird internal representation.
Change-Id: I54058836e19c94c5ab6b46b37bd43df3652411a5
---
A .gitignore
A jobrunner.sample.json
M redisJobRunnerService
3 files changed, 177 insertions(+), 80 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/jobrunner
refs/changes/80/148280/1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5a07715
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+jobrunner.ini
+jobrunner.json.sample
diff --git a/jobrunner.sample.json b/jobrunner.sample.json
new file mode 100644
index 0000000..1b96fa8
--- /dev/null
+++ b/jobrunner.sample.json
@@ -0,0 +1,100 @@
+// Configuration file for MediaWiki Jobrunner
+// This file is managed by Puppet
+
+{
+ "groups": {
+ "basic": {
+ "runners": 19,
+ "include": [
+ "*"
+ ],
+ "exclude": [
+ "AssembleUploadChunks",
+ "ParsoidCacheUpdateJobOnDependencyChange",
+ "ParsoidCacheUpdateJobOnEdit",
+ "PublishStashedFile",
+ "gwtoolsetGWTFileBackendCleanupJob",
+ "gwtoolsetUploadMediafileJob",
+ "gwtoolsetUploadMetadataJob",
+ "uploadFromUrl",
+ "webVideoTranscode"
+ ],
+ "nice": [
+ "cirrusSearchLinksUpdate",
+ "htmlCacheUpdate",
+ "refreshLinks"
+ ]
+ },
+ "gwt": {
+ "runners": 1,
+ "include": [
+ "gwtoolsetUploadMetadataJob",
+ "gwtoolsetUploadMediafileJob",
+ "gwtoolsetGWTFileBackendCleanupJob"
+ ]
+ },
+ "parsoid": {
+ "runners": 20,
+ "include": [
+ "ParsoidCacheUpdateJobOnEdit"
+ ],
+ "nice": [
+ "ParsoidCacheUpdateJobOnDependencyChange"
+ ]
+ },
+ "transcode": {
+ "runners": 0,
+ "include": [
+ "webVideoTranscode"
+ ]
+ },
+ "upload": {
+ "runners": 7,
+ "include": [
+ "AssembleUploadChunks",
+ "PublishStashedFile",
+ "uploadFromUrl"
+ ]
+ }
+ },
+
+ "limits": {
+ // How many times to let jobs be recycled before abandoning
+ "attempts": {
+ "*": 3
+ },
+ // How long jobs can be claimed before being recycled
+ "claimTTL": {
+ "*": 3600,
+ "webVideoTranscode": 86400
+ },
+ // runJobs.php process time limits
+ "real": {
+ "*": 300,
+ "webVideoTranscode": 86400
+ },
+ // runJobs.php memory limits
+ "memory": {
+ "*": "300M"
+ }
+ },
+
+ "redis": {
+ // Ready queue trackers
+ "aggregators": [
+ "rdb1001.eqiad.wmnet",
+ "rdb1003.eqiad.wmnet"
+ ],
+ // Main queue servers
+ "queues": [
+ "rdb1001.eqiad.wmnet",
+ "rdb1003.eqiad.wmnet"
+ ],
+ "password": "XXXXX"
+ },
+
+ "statsd": "statsd.eqiad.wmnet:8125",
+
+ // HET deploy wrapper
+ "wrapper": "php /usr/local/apache/common/multiversion/MWScript.php"
+}
\ No newline at end of file
diff --git a/redisJobRunnerService b/redisJobRunnerService
index 2f3c2c5..6c2fcaa 100755
--- a/redisJobRunnerService
+++ b/redisJobRunnerService
@@ -9,18 +9,14 @@
function posix_kill() {} // no-op on Windows; procs killed with
proc_terminate()
}
-error_reporting( E_ALL );
+error_reporting( E_ALL | E_STRICT );
+ini_set( 'display_errors', 1 );
// Run the server...
set_time_limit( 0 );
ini_set( 'memory_limit', '256M' );
RedisJobRunnerService::init(
- getopt( '', array(
- 'aggrSrvs:', 'queueSrvs:', 'runners:',
- 'config-file::', 'wrapper::', 'claimTTL::', 'attempts::',
'maxReal::', 'maxMem::',
- 'statsdAddr::',
- 'verbose', 'help'
- ) )
+ getopt( '', array( 'config-file::', 'help' ) )
)->main();
class RedisJobRunnerService {
@@ -88,39 +84,32 @@
protected static $instance;
/**
- * @param array $config
+ * @param array $args
* @return RedisJobRunnerService
* @throws Exception
*/
- public static function init( array $config ) {
+ public static function init( array $args ) {
if ( self::$instance ) {
throw new Exception( 'RedisJobRunnerService already
initialized.' );
}
- if ( isset( $config['config-file'] ) ) {
- $iniConfig = parse_ini_file( $config['config-file'] );
- if ( is_array( $iniConfig ) ) {
- $config += $iniConfig; // params take precedence
- } else {
- throw new Exception( "Could not parse file at
'{$config['config-file']}'." );
- }
+ if ( !isset( $args['config-file'] ) || isset( $args['help'] ) )
{
+ die( "Usage: php RedisJobRunnerService.php\n"
+ . "--config-file=<path>\n"
+ . "--help\n"
+ );
}
+ $file = $args['config-file'];
- foreach ( array( 'aggrSrvs', 'queueSrvs', 'runners' ) as $par )
{
- if ( !isset( $config[$par] ) || isset( $config['help']
) ) {
- die( "Usage: php RedisJobRunnerService.php\n" .
- "--config-file=<path>\n" .
- "--aggrSrvs=<address>[,address]...\n" .
- "--queueSrvs=<address>[,address]...>\n"
.
-
"--runners=<count>:<type>[,type]...[|<count>:<type>[,type]...]...\n" .
- "--wrapper=<script wrapper>" .
-
"--claimTTL=<type>:<seconds>[,<type>:<seconds>]...\n" .
-
"--attempts=<type>:<integer>[,<type>:<integer>]...\n" .
-
"--maxReal=<type>:<integer>[,<type>:<integer>]...\n" .
-
"--maxMem=<type>:<integer>[,<type>:<integer>(B|M)]...\n" .
- "--statsdAddr=<address>\n"
- );
- }
+ $content = file_get_contents( $file );
+ if ( !$content ) {
+ throw new Exception( "Coudn't open configuration file
'{$file}''" );
+ }
+ // Remove comments
+ $content = trim( preg_replace( '/\/\/.*$/m', '', $content ) );
+ $config = json_decode( $content, true );
+ if ( !is_array( $config ) ) {
+ throw new Exception( "Could not parse JSON file
'{$file}'." );
}
self::$instance = new self( $config );
@@ -131,88 +120,95 @@
* @param array $config
*/
protected function __construct( array $config ) {
- $this->aggrSrvs = explode( ',', $config['aggrSrvs'] );
+ $this->aggrSrvs = $config['redis']['aggregators'];
if ( !count( $this->aggrSrvs ) ) {
- throw new Exception( "Empty list for 'aggrSrvs'." );
+ throw new Exception( "Empty list for
'redis.aggregators'." );
}
- $this->queueSrvs = explode( ',', $config['queueSrvs'] );
+ $this->queueSrvs = $config['redis']['queues'];
if ( !count( $this->queueSrvs ) ) {
throw new Exception( "Empty list for 'queueSrvs'." );
}
- foreach ( explode( '&', $config['runners'] ) as $loop ) {
- list( $runners, $types ) = explode( ':', trim( $loop )
);
- if ( !preg_match( '/^\d+$/', $runners ) ) {
+ foreach ( $config['groups'] as $group ) {
+ $runners = $group['runners'];
+ if ( !is_int( $runners ) ) {
throw new Exception( "Invalid number of job
runners '$runners'." );
} elseif ( $runners == 0 ) {
continue; // loop disabled
}
- $types = explode( ',', $types );
+
+ $types = $group['include'];
+ if ( isset( $group['exclude']) ) {
+ foreach ( $group['exclude'] as $job ) {
+ $types[] = "-$job";
+ }
+ }
+ $types = array_combine( $types, $types );
+ if ( isset( $group['nice'] ) ) {
+ foreach ( $group['nice'] as $job ) {
+ if ( isset( $types[$job] ) ) {
+ $types[$job] = "~$job";
+ }
+ }
+ }
foreach ( $types as $type ) {
- $type = trim( $type );
if ( !preg_match( '/^[-~]?[a-zA-Z0-9]+|\*$/',
$type ) ) {
throw new Exception( "Invalid job type
'$type'." );
}
}
- $this->loopMap[] = array( 'types' => $types, 'runners'
=> (int)$runners );
+ $this->loopMap[] = array( 'types' => $types, 'runners'
=> $runners );
}
if ( isset( $config['timeout'] ) ) {
$this->maxProcReal = (int)$config['timeout'];
}
+
if ( isset( $config['wrapper'] ) ) {
$this->wrapper = $config['wrapper'];
}
- if ( isset( $config['password'] ) ) {
- $this->password = $config['password'];
+ if ( isset( $config['redis']['password'] ) ) {
+ $this->password = $config['redis']['password'];
}
- $this->claimTTLMap['*'] = 3600; // default
- if ( isset( $config['claimTTL'] ) ) {
- $tuples = explode( ',', $config['claimTTL'] );
- foreach ( $tuples as $tuple ) {
- list( $type, $value ) = explode( ':', trim(
$tuple ) );
- if ( $value < 30 ) {
- throw new Exception( "Invalid claim TTL
'$value'." );
- }
- $this->claimTTLMap[$type] = (int)$value;
- }
+
+ if ( isset( $config['limits']['claimTTL'] ) ) {
+ $this->claimTTLMap = $config['limits']['claimTTL'];
}
- $this->attemptsMap['*'] = 3; // default
- if ( isset( $config['attempts'] ) ) {
- $tuples = explode( ',', $config['attempts'] );
- foreach ( $tuples as $tuple ) {
- list( $type, $value ) = explode( ':', trim(
$tuple ) );
- if ( $value < 1 ) {
- throw new Exception( "Invalid max
attempts '$value'." );
- }
- $this->attemptsMap[$type] = (int)$value;
- }
+ if ( !isset( $this->claimTTLMap['*'] ) ) {
+ $this->claimTTLMap['*'] = 3600;
}
+
+ if ( isset( $config['limits']['attempts'] ) ) {
+ $this->attemptsMap = $config['limits']['attempts'];
+ }
+ if ( !isset( $config['limits']['attempts'] ) ) {
+ $this->attemptsMap['*'] = 3;
+ }
+
// Avoid killing processes before they get a fair chance to exit
$minRealTime = 2 * max( $this->lpMaxTime, $this->hpMaxTime );
- $this->maxRealMap['*'] = 3600; // default
- if ( isset( $config['maxReal'] ) ) {
- $tuples = explode( ',', $config['maxReal'] );
- foreach ( $tuples as $tuple ) {
- list( $type, $value ) = explode( ':', trim(
$tuple ) );
+ if ( isset( $config['limits']['real'] ) ) {
+ foreach ( $config['limits']['real'] as $type => $value
) {
$this->maxRealMap[$type] = max( (int)$value,
$minRealTime );
}
}
- $this->maxMemMap['*'] = '300M'; // default
- if ( isset( $config['maxMem'] ) ) {
- $tuples = explode( ',', $config['maxMem'] );
- foreach ( $tuples as $tuple ) {
- list( $type, $value ) = explode( ':', trim(
$tuple ) );
- $this->maxMemMap[$type] = $value;
- }
+ if ( !isset( $this->maxRealMap['*'] ) ) {
+ $this->maxRealMap['*'] = 3600;
}
- if ( isset( $config['statsdAddr'] ) ) {
+
+ if ( isset( $config['limits']['memory'] ) ) {
+ $this->maxMemMap = $config['limits']['memory'];
+ }
+ if ( !isset( $this->maxMemMap['*'] ) ) {
+ $this->maxMemMap['*'] = '300M';
+ }
+
+ if ( isset( $config['statsd'] ) ) {
$m = array();
- if ( preg_match( '/^(.+):(\d+)$/',
$config['statsdAddr'], $m ) ) {
+ if ( preg_match( '/^(.+):(\d+)$/', $config['statsd'],
$m ) ) {
$this->statsdHost = $m[1];
$this->statsdPort = (int)$m[2];
} else {
- throw new Exception( "Invalid profiler address
'{$config['statsdAddr']}'." );
+ throw new Exception( "Invalid profiler address
'{$config['statsd']}'." );
}
}
$this->verbose = isset( $config['verbose'] );
@@ -413,7 +409,7 @@
$this->incrStats( 'runner-status.none', 1 );
}
++$free;
- $queue = $this->selectQueue( $id, $slot, $prioMap,
$pending );
+ $queue = $this->selectQueue( $id, $prioMap, $pending );
if ( !$queue ) {
break;
}
@@ -428,12 +424,11 @@
/**
* @param integer $id
- * @param integer $slot
* @param array $prioMap
* @param array $pending
* @return array|boolean
*/
- protected function selectQueue( $id, $slot, array $prioMap, array
$pending ) {
+ protected function selectQueue( $id, array $prioMap, array $pending ) {
$include = array();
$exclude = array();
foreach ( $this->loopMap[$id]['types'] as $type ) {
--
To view, visit https://gerrit.wikimedia.org/r/148280
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I54058836e19c94c5ab6b46b37bd43df3652411a5
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/services/jobrunner
Gerrit-Branch: master
Gerrit-Owner: MaxSem <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits