From 0a23d735f72fb9a5d1558c04f5aa602bce709e16 Mon Sep 17 00:00:00 2001 From: Alex Sinitsin Date: Wed, 20 Nov 2019 17:35:12 +0500 Subject: [PATCH 1/3] Added a way to set max number of jobs per server --- htdocs/js/pages/admin/Servers.js | 9 +++++++++ lib/job.js | 9 +++++++++ lib/test.js | 3 ++- sample_conf/setup.json | 6 ++++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/htdocs/js/pages/admin/Servers.js b/htdocs/js/pages/admin/Servers.js index ebd41f1f..39b1fdc3 100644 --- a/htdocs/js/pages/admin/Servers.js +++ b/htdocs/js/pages/admin/Servers.js @@ -281,6 +281,10 @@ Class.add( Page.Admin, { get_form_table_spacer() + get_form_table_row('Server Class:', '') + 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:', '') + + get_form_table_caption("Enter a maximum number of jobs per server for this group, 0 for unlimited.") + + get_form_table_spacer() + ''; app.confirm( '  ' + (edit ? "Edit Server Group" : "Add Server Group"), html, edit ? "Save Changes" : "Add Group", function(result) { @@ -297,6 +301,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(); diff --git a/lib/job.js b/lib/job.js index 2a68fd16..a832ff6a 100644 --- a/lib/job.js +++ b/lib/job.js @@ -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) ); } diff --git a/lib/test.js b/lib/test.js index 7e9ede29..d5490160 100644 --- a/lib/test.js +++ b/lib/test.js @@ -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) { @@ -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(); } ); diff --git a/sample_conf/setup.json b/sample_conf/setup.json index 3363109e..6e2fe95a 100644 --- a/sample_conf/setup.json +++ b/sample_conf/setup.json @@ -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", { From 83dfcfbae006eeeb90285be2b13beaf3505be564 Mon Sep 17 00:00:00 2001 From: Alex Sinitsin Date: Thu, 21 Nov 2019 11:47:37 +0500 Subject: [PATCH 2/3] Updated readme and unit tests --- README.md | 1 + htdocs/js/pages/admin/Servers.js | 3 +- lib/test.js | 71 ++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6c11bbc..6afe63a8 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/htdocs/js/pages/admin/Servers.js b/htdocs/js/pages/admin/Servers.js index 39b1fdc3..6d6cf0d0 100644 --- a/htdocs/js/pages/admin/Servers.js +++ b/htdocs/js/pages/admin/Servers.js @@ -282,9 +282,8 @@ Class.add( Page.Admin, { get_form_table_row('Server Class:', '') + 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:', '') + + get_form_table_row('Maximum Jobs:', '') + get_form_table_caption("Enter a maximum number of jobs per server for this group, 0 for unlimited.") + - get_form_table_spacer() + ''; app.confirm( '  ' + (edit ? "Edit Server Group" : "Add Server Group"), html, edit ? "Save Changes" : "Add Group", function(result) { diff --git a/lib/test.js b/lib/test.js index d5490160..1dce76cb 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1444,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) { From a8bd5e070873ba56509d3a1b289323382a96fb66 Mon Sep 17 00:00:00 2001 From: Ray Matos Date: Sat, 23 Nov 2019 23:35:31 -0500 Subject: [PATCH 3/3] update install script for dev testing --- bin/install.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/install.js b/bin/install.js index 7446f65a..34ab10a6 100755 --- a/bin/install.js +++ b/bin/install.js @@ -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);