performance: parallelize container and openwhisk tasks at startup
diff --git a/src/agentmgr.js b/src/agentmgr.js
index 452e49a..e12e9f7 100644
--- a/src/agentmgr.js
+++ b/src/agentmgr.js
@@ -28,7 +28,6 @@
const fs = require('fs-extra');
const sleep = require('util').promisify(setTimeout);
const debug = require('./debug');
-const prettyBytes = require('pretty-bytes');
const clone = require('clone');
function getAnnotation(action, key) {
@@ -118,10 +117,12 @@
try {
const backup = await getWskActionWithoutCode(this.wsk, backupName);
- if (isAgent(backup)) {
+ if (!backup) {
// backup is also an agent (should not happen)
- // backup is useless, delete it
- // await this.wsk.actions.delete(backupName);
+ throw new Error(`Dang! Agent is already installed and action backup is missing.\n\nPlease redeploy your action first before running wskdebug again.`);
+
+ } else if (isAgent(backup)) {
+ // backup is also an agent (should not happen)
throw new Error(`Dang! Agent is already installed and action backup is broken (${backupName}).\n\nPlease redeploy your action first before running wskdebug again.`);
} else {
@@ -157,8 +158,6 @@
this.actionWithCode = await this.restoreAction();
} else {
this.actionWithCode = await this.wsk.actions.get(this.actionName);
- debug(`fetched action code from openwhisk (${prettyBytes(this.actionWithCode.exec.code.length)})`);
-
}
// extra sanity check
if (isAgent(this.actionWithCode)) {
@@ -168,7 +167,7 @@
return this.actionWithCode;
}
- async installAgent(invoker) {
+ async installAgent(invoker, debugTask) {
this.agentInstalled = true;
let agentName;
@@ -190,7 +189,7 @@
// agent using ngrok for forwarding
agentName = "ngrok";
agentCode = await this.ngrokAgent.getAgent(agentAction);
- debug("started local ngrok proxy");
+ debugTask("started local ngrok proxy");
} else {
this.concurrency = await this.openwhiskSupports("concurrency");
@@ -216,11 +215,13 @@
// create copy in case wskdebug gets killed hard
// do async as this can be slow for larger actions and this is part of the critical startup path
this.createBackup = (async () => {
+ const debugTask = debug.task();
+
await this.wsk.actions.update({
name: backupName,
action: agentAction
});
- debug(`created action backup ${backupName}`);
+ debugTask(`created action backup ${backupName}`);
})();
if (this.argv.verbose) {
@@ -245,7 +246,7 @@
await this.pushAgent(agentAction, agentCode, backupName);
}
}
- debug(`installed agent (${agentName}) in place of ${this.actionName}`);
+ debugTask(`installed agent '${agentName}' in place of ${this.actionName}`);
if (this.argv.verbose) {
console.log(`Agent installed.`);
diff --git a/src/debug.js b/src/debug.js
index 6e4cc49..bbe46f8 100644
--- a/src/debug.js
+++ b/src/debug.js
@@ -19,3 +19,13 @@
// common debug() instance for shared time spent measurments (+millis)
module.exports = require('debug')('wskdebug');
+
+// start a sub debug instance for logging times in parallel promises
+module.exports.task = () => {
+ const debug = require('debug')('wskdebug')
+ // trick to start time measurement from now on without logging an extra line
+ debug.log = () => {};
+ debug();
+ delete debug.log;
+ return debug;
+}
\ No newline at end of file
diff --git a/src/debugger.js b/src/debugger.js
index 91d8d79..2cce15f 100644
--- a/src/debugger.js
+++ b/src/debugger.js
@@ -25,6 +25,7 @@
const { spawnSync } = require('child_process');
const sleep = require('util').promisify(setTimeout);
const debug = require('./debug');
+const prettyBytes = require('pretty-bytes');
const prettyMilliseconds = require('pretty-ms');
/**
@@ -74,26 +75,57 @@
this.invoker = new OpenWhiskInvoker(this.actionName, actionMetadata, this.argv, this.wskProps, this.wsk);
try {
- // run build initially (would be required by starting container)
- if (this.argv.onBuild) {
- console.info("=> Build:", this.argv.onBuild);
- spawnSync(this.argv.onBuild, {shell: true, stdio: "inherit"});
- }
+ // parallelize slower work using promises
- // start container - get it up fast for VSCode to connect within its 10 seconds timeout
- await this.invoker.startContainer();
+ // task 1 - start local container
+ const containerTask = (async () => {
+ const debugTask = debug.task();
+ // run build initially (would be required by starting container)
+ if (this.argv.onBuild) {
+ console.info("=> Build:", this.argv.onBuild);
+ spawnSync(this.argv.onBuild, {shell: true, stdio: "inherit"});
+ }
- debug(`started container: ${this.invoker.name()}`);
+ // start container - get it up fast for VSCode to connect within its 10 seconds timeout
+ await this.invoker.startContainer();
- // get code and /init local container
- const actionWithCode = await this.agentMgr.readActionWithCode();
+ debugTask(`started container: ${this.invoker.name()}`);
+ })();
- await this.invoker.init(actionWithCode);
+ // task 2 - fetch action code from openwhisk
+ const openwhiskTask = (async () => {
+ const debugTask = debug.task();
+ const actionWithCode = await this.agentMgr.readActionWithCode();
- debug("installed action on container (/init)");
+ debugTask(`got action code (${prettyBytes(actionWithCode.exec.code.length)})`);
+ return actionWithCode;
+ })();
- // setup agent in openwhisk
- await this.agentMgr.installAgent(this.invoker);
+ // wait for both tasks 1 & 2
+ const results = await Promise.all([containerTask, openwhiskTask]);
+ const actionWithCode = results[1];
+
+ // parallelize slower work using promises again
+
+ // task 3 - initialize local container with code
+ const initTask = (async () => {
+ const debugTask = debug.task();
+
+ // /init local container
+ await this.invoker.init(actionWithCode);
+
+ debugTask("installed action on container");
+ })();
+
+ // task 4 - install agent in openwhisk
+ const agentTask = (async () => {
+ const debugTask = debug.task();
+
+ // setup agent in openwhisk
+ await this.agentMgr.installAgent(this.invoker, debugTask);
+ })();
+
+ await Promise.all([initTask, agentTask]);
if (this.argv.onStart) {
console.log("On start:", this.argv.onStart);
@@ -115,9 +147,8 @@
if (this.argv.condition) {
console.info(`Condition : ${this.argv.condition}`);
}
- console.info(`Startup : ${prettyMilliseconds(Date.now() - this.startTime)}`)
console.log();
- console.info(`Ready, waiting for activations! Use CTRL+C to exit`);
+ console.info(`Ready for activations. Started in ${prettyMilliseconds(Date.now() - this.startTime)}. Use CTRL+C to exit`);
this.ready = true;