From 58668a7744027e18ccb89765919938df2d5f22df Mon Sep 17 00:00:00 2001 From: Lloyd Hilaiel <lloyd@hilaiel.com> Date: Wed, 30 Nov 2011 14:00:16 -0700 Subject: [PATCH] keysigner to saturate multiple cores - issue #213 --- bin/keysigner | 59 ++++++++++++++++++++++++------------- lib/configuration.js | 13 ++++++-- lib/keysigner/subprocess.js | 19 ++++++++++++ package.json | 1 + 4 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 lib/keysigner/subprocess.js diff --git a/bin/keysigner b/bin/keysigner index ff422d35c..f8b0e0585 100755 --- a/bin/keysigner +++ b/bin/keysigner @@ -47,9 +47,9 @@ httputils = require('../lib/httputils.js'), validate = require('../lib/validate.js'), metrics = require('../lib/metrics.js'), logger = require('../lib/logging.js').logger, -ca = require('../lib/keysigner/ca.js'), heartbeat = require('../lib/heartbeat'), -shutdown = require('../lib/shutdown'); +shutdown = require('../lib/shutdown'), +computecluster = require('compute-cluster'); // create an express server var app = express.createServer(); @@ -84,32 +84,51 @@ app.use(function(req, resp, next) { // parse POST bodies app.use(express.bodyParser()); +// allocate a compute cluster +try { + var cc = new computecluster({ + module: path.join(__dirname, "..", "lib", "keysigner", "subprocess.js"), + max_processes: config.get('max_compute_processes') + }).on('error', function(e) { + logger.error("error detected in keysigning computation process! fatal: " + e.toString()); + setTimeout(function() { process.exit(1); }, 0); + }).on('info', function(msg) { + logger.info("(compute cluster): " + msg); + }).on('debug', function(msg) { + logger.debug("(compute cluster): " + msg); + }); +} catch(e) { + process.stderr.write("can't allocate compute cluster: " + e + "\n"); + process.exit(1); +} + // and our single function app.post('/wsapi/cert_key', validate(["email", "pubkey"]), function(req, resp) { - try { - // parse the pubkey - var pk = ca.parsePublicKey(req.body.pubkey); - - // same account, we certify the key - // we certify it for a day for now - var expiration = new Date(); - expiration.setTime(new Date().valueOf() + config.get('certificate_validity_ms')); - var cert = ca.certify(req.body.email, pk, expiration); - - resp.writeHead(200, {'Content-Type': 'text/plain'}); - resp.write(cert); - resp.end(); - } catch (e) { - logger.error("certification generation error: " + e.toString()); - httputils.serverError(resp, "certification generation error"); - } + cc.enqueue({ + pubkey: req.body.pubkey, + email: req.body.email + }, function (err, r) { + // consider "application" errors to be the same as harder errors + if (!err && r && r.error) err = r.error; + if (!r || !r.success) err = "no certificate returned from child process"; + if (err) { + logger.error("certification generation error: " + err); + httputils.serverError(resp, "certification generation error"); + } else { + resp.writeHead(200, {'Content-Type': 'text/plain'}); + resp.write(r.success); + resp.end(); + } + }); }); // shutdown when code_update is invoked shutdown.installUpdateHandler(app); // shutdown nicely on signals -shutdown.handleTerminationSignals(app); +shutdown.handleTerminationSignals(app, function() { + cc.exit(); +}); var bindTo = config.get('bind_to'); app.listen(bindTo.port, bindTo.host, function() { diff --git a/lib/configuration.js b/lib/configuration.js index acb6f0a43..d24973ca4 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -104,7 +104,9 @@ g_configs.production = { bcrypt_work_factor: 12, authentication_duration_ms: (2 * 7 * 24 * 60 * 60 * 1000), certificate_validity_ms: (24 * 60 * 60 * 1000), - min_time_between_emails_ms: (60 * 1000) + min_time_between_emails_ms: (60 * 1000), + // may be specified to manipulate the maximum number of compute + max_compute_processes: undefined }; @@ -121,7 +123,8 @@ g_configs.local = { bcrypt_work_factor: g_configs.production.bcrypt_work_factor, authentication_duration_ms: g_configs.production.authentication_duration_ms, certificate_validity_ms: g_configs.production.certificate_validity_ms, - min_time_between_emails_ms: g_configs.production.min_time_between_emails_ms + min_time_between_emails_ms: g_configs.production.min_time_between_emails_ms, + max_compute_processes: undefined }; // test environments are variations on local @@ -204,6 +207,12 @@ if (process.env['BCRYPT_WORK_FACTOR']) { g_config.bcrypt_work_factor = parseInt(process.env['BCRYPT_WORK_FACTOR']); } +// allow the number of cores used to be specified from the environment, +// default will something reasonable. +if (process.env['MAX_COMPUTE_PROCESSES']) { + g_config.max_compute_processes = parseInt(process.env['MAX_COMPUTE_PROCESSES']); +} + // what host/port shall we bind to? g_config.bind_to = { host: process.env['IP_ADDRESS'] || process.env['HOST'] || "127.0.0.1", diff --git a/lib/keysigner/subprocess.js b/lib/keysigner/subprocess.js new file mode 100644 index 000000000..3f0f108a1 --- /dev/null +++ b/lib/keysigner/subprocess.js @@ -0,0 +1,19 @@ +const +config = require('../configuration'), +ca = require('./ca.js'); + +process.on('message', function(m) { + try { + // parse the pubkey + var pk = ca.parsePublicKey(m.pubkey); + + // same account, we certify the key + // we certify it for a day for now + var expiration = new Date(); + expiration.setTime(new Date().valueOf() + config.get('certificate_validity_ms')); + var cert = ca.certify(m.email, pk, expiration); + process.send({"success": cert}); + } catch(e) { + process.send({"error": e ? e.toString() : "unknown"}); + } +}); diff --git a/package.json b/package.json index 0e2018f3d..c4f284eb9 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ , "node-statsd": "https://github.com/mojodna/node-statsd/tarball/2584c08fad" , "connect-logger-statsd": "0.0.1" , "semver": "1.0.12" + , "compute-cluster": "0.0.2" } , "scripts": { "postinstall": "./scripts/generate_ephemeral_keys.sh", -- GitLab