Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Max Jobs per node #672

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,7 @@ Here you will need to provide:
- A **Group Title** which is used for display purposes.
- A **Hostname Match** which is a regular expression applied to every server hostname (used to automatically add servers to the group).
- A **Server Class** which sets the servers in your group as "Master Eligible" or "Slave Only".
- A **Maximum Jobs** which is used to determine which servers can run an event that targets this group, only the servers in the group that have less active jobs than the maximum are considered as potential candidates.

Note that "Master Eligible" servers all need to be properly configured and have access to your storage back-end. Meaning, if you opted to use the filesystem, you'll need to make sure it is mounted (via NFS or similar mechanism) on all the servers who could become master. Or, if you opted to use a NoSQL DB such as Couchbase or S3, they need all the proper settings and/or credentials to connect. For more details, see the [Multi-Server Cluster](#multi-server-cluster) section.

Expand Down
6 changes: 3 additions & 3 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ var installer_version = '1.1';
var base_dir = '/opt/cronicle';
var log_dir = base_dir + '/logs';
var log_file = '';
var gh_repo_url = 'http://github.com/jhuckaby/Cronicle';
var gh_releases_url = 'https://api.github.com/repos/jhuckaby/Cronicle/releases';
var gh_head_tarball_url = 'https://github.com/jhuckaby/Cronicle/archive/master.tar.gz';
var gh_repo_url = 'http://github.com/raymatos/Cronicle';
var gh_releases_url = 'https://api.github.com/repos/raymatos/Cronicle/releases';
var gh_head_tarball_url = 'https://github.com/raymatos/Cronicle/archive/master.tar.gz';

var print = function(msg) {
process.stdout.write(msg);
Expand Down
8 changes: 8 additions & 0 deletions htdocs/js/pages/admin/Servers.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ Class.add( Page.Admin, {
get_form_table_spacer() +
get_form_table_row('Server Class:', '<select id="fe_eg_master">' + render_menu_options([ [1,'Master Eligible'], [0,'Slave Only'] ], group.master, false) + '</select>') +
get_form_table_caption("Select whether servers in the group are eligible to become the master server, or run as slaves only.") +
get_form_table_spacer() +
get_form_table_row('Maximum Jobs:', '<input type="number" id="fe_eg_max_jobs" min="0" value="'+(group.max_jobs || 0)+'" />') +
get_form_table_caption("Enter a maximum number of jobs per server for this group, 0 for unlimited.") +
'</table>';

app.confirm( '<i class="mdi mdi-server-network">&nbsp;&nbsp;</i>' + (edit ? "Edit Server Group" : "Add Server Group"), html, edit ? "Save Changes" : "Add Group", function(result) {
Expand All @@ -297,6 +300,11 @@ Class.add( Page.Admin, {
return app.badField('fe_eg_regexp', "Invalid regular expression: " + err);
}

group.max_jobs = parseInt( $('#fe_eg_max_jobs').val() );
if (isNaN(group.max_jobs) || group.max_jobs < 0) {
return app.badField('fe_eg_max_jobs', "Please enter a non-negative integer for the maximum jobs.");
}

group.master = parseInt( $('#fe_eg_master').val() );
Dialog.hide();

Expand Down
9 changes: 9 additions & 0 deletions lib/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ module.exports = Class.create({
}
}

// filter out the candidates that don't have the capacity
var max_jobs = server_group.max_jobs || 0;
if (max_jobs > 0) {
candidates = candidates.filter( function(server) {
var jobs = Tools.findObjectsIdx( job_list, { 'hostname': server.hostname } );
return jobs.length < max_jobs;
} );
}

if (!candidates.length) {
return callback( new Error("Could not find any servers for group: " + server_group.title) );
}
Expand Down
74 changes: 73 additions & 1 deletion lib/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ module.exports = {
function testAPICreateServerGroup(test) {
// test app/create_server_group api
var self = this;
var params = {"title":"del gap","regexp":"dasds","master":0,"session_id":session_id};
var params = {"title":"del gap","regexp":"dasds","master":0,"max_jobs":42,"session_id":session_id};

request.json( api_url + '/app/create_server_group', params, function(err, resp, data) {

Expand All @@ -472,6 +472,7 @@ module.exports = {
test.ok( !!group, "Data record record is non-null" );
test.ok( group.title == "del gap", "Title is correct" );
test.ok( group.regexp == "dasds", "Regexp is correct" );
test.ok( group.max_jobs === 42, "Max jobs is correct" );

test.done();
} );
Expand Down Expand Up @@ -1443,6 +1444,77 @@ module.exports = {
}, 500 );
},

function testMaxJobsPerServer(test) {
// limit the main server group to a certain number of jobs per server
// set number of concurrent jobs to unlimited for the event and also allow it to be queued
// launch the event multiple times and check that the number of active jobs
// is equal to the maximum per server and that the rest are being queued
var event_id = this.event_id;
var eventQueue = cronicle.eventQueue;
var n_queued = 3;
var max_jobs = 2;

async.auto({
update_group: function (callback) {
var params = {
"id": "maingrp",
"max_jobs": max_jobs,
"session_id": session_id
};
request.json( api_url + '/app/update_server_group', params, callback );
},
update_event: function (callback) {
var params = {
"id": event_id,
"session_id": session_id,
"queue": 1,
"max_children": 0
};
request.json( api_url + '/app/update_event', params, callback );
},
event: [ 'update_event', function (results, callback) {
storage.listFind( 'global/schedule', { id: event_id }, callback );
} ],
jobs: [ 'event', function (results, callback) {
async.times( max_jobs + n_queued, function(_, next) {
var job = Tools.copyHash( results.event[0], true );
job.params.script = "#!/bin/sh\nsleep 2s";
cronicle.launchOrQueueJob( job, next );
}, callback );
} ]
},
function(err, results) {
test.ok( !err, "No errors" );
test.ok( active_job_count() == max_jobs, "Number of active jobs is correct" );
test.ok( eventQueue[event_id] == n_queued, "Number of queued jobs is correct" );

// kill the processes and wait for the jobs to end
delete eventQueue[event_id];

results.jobs.forEach( function(jobs) {
if (jobs.length) {
try { process.kill( jobs[0].pid, 9 ); }
catch (e) {;}
}
} );

async.doWhilst(
function (callback) {
setTimeout( callback, 100 );
},
active_job_count,
function () {
test.done();
}
);

function active_job_count() {
var active_jobs = cronicle.getAllActiveJobs();
return Object.keys(active_jobs).length;
}
} ); // async.auto
},

// app/delete_event

function testAPIDeleteEvent(test) {
Expand Down
6 changes: 4 additions & 2 deletions sample_conf/setup.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,15 @@
"id": "maingrp",
"title": "Master Group",
"regexp": "_HOSTNAME_",
"master": 1
"master": 1,
"max_jobs": 0
} ],
[ "listPush", "global/server_groups", {
"id": "allgrp",
"title": "All Servers",
"regexp": ".+",
"master": 0
"master": 0,
"max_jobs": 0
} ],
[ "listCreate", "global/servers", {} ],
[ "listPush", "global/servers", {
Expand Down