Skip to content

Commit bf34b61

Browse files
fix(Perf): better microservices launch
1 parent 153ff83 commit bf34b61

4 files changed

Lines changed: 100 additions & 49 deletions

File tree

app/stores/back.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,11 @@ export const useBackStore = defineStore("back", {
9292
additionalProperties: true,
9393
};
9494
const params = { COMMAND_BACK, NUXT_ROOT_PATH, args };
95-
const start = Date.now()
96-
console.log("[GEODE] PERF test begin launch", start)
9795
console.log("[GEODE] params", params);
9896
return appStore.request(
9997
{ schema, params },
10098
{
10199
response_function: (response) => {
102-
const end = Date.now()
103-
console.log("[GEODE] PERF test end launch", end)
104-
console.log("[GEODE] PERF test diff", end - start)
105-
106100
console.log(`[GEODE] Back launched on port ${response.port}`);
107101
this.default_local_port = response.port;
108102
},

app/stores/viewer.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,10 @@ export const useViewerStore = defineStore(
143143

144144
const params = { COMMAND_VIEWER, NUXT_ROOT_PATH, args };
145145
console.log("[VIEWER] params", params);
146-
const start = Date.now()
147-
console.log("[VIEWER] PERF test begin launch", start)
148146
return appStore.request(
149147
{ schema, params },
150148
{
151149
response_function: (response) => {
152-
const end = Date.now()
153-
console.log("[VIEWER] PERF end launch", end)
154-
console.log("[VIEWER] PERF test diff", end - start)
155-
156150
console.log(`[VIEWER] Viewer launched on port ${response.port}`);
157151
default_local_port.value = response.port;
158152
},

app/utils/local/microservices.js

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.jso
88
import { getPort } from "get-port-please";
99
import pTimeout from "p-timeout";
1010

11-
// Local imports
11+
// Local imports
1212
import { commandExistsSync, waitForReady } from "./scripts.js";
1313
import { microservicesMetadatasPath, projectMicroservices } from "./cleanup.js";
1414
import { executablePath } from "./path.js";
1515

16-
const DEFAULT_TIMEOUT_SECONDS = 60;
1716
const MILLISECONDS_PER_SECOND = 1000;
17+
const DEFAULT_TIMEOUT_SECONDS = 30;
18+
const MAX_ERROR_BUFFER_BYTES = 64 * 1024;
1819

1920
function getAvailablePort() {
2021
return getPort({
@@ -23,40 +24,46 @@ function getAvailablePort() {
2324
});
2425
}
2526

27+
function resolveCommand(execPath, execName) {
28+
const command = commandExistsSync(execName)
29+
? execName
30+
: executablePath(execPath, execName);
31+
return command;
32+
}
33+
2634
async function runScript(
2735
execPath,
2836
execName,
2937
args,
3038
expectedResponse,
3139
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
3240
) {
33-
let command = "";
34-
if (commandExistsSync(execName)) {
35-
command = execName;
36-
} else {
37-
command = path.join(executablePath(execPath, execName));
38-
}
41+
const command = resolveCommand(execPath, execName);
3942
console.log("runScript", command, args);
4043

41-
const child = child_process.spawn(process.platform === "win32" ? command : `"${command}"`, args, {
42-
encoding: "utf8",
43-
shell: true,
44+
const child = child_process.spawn(command, args, {
45+
stdio: ["ignore", "pipe", "pipe"],
4446
});
45-
child.stdout.on("data", (data) => console.log(`[${execName}] ${data.toString()}`));
46-
child.stderr.on("data", (data) => console.log(`[${execName}] ${data.toString()}`));
4747

48-
child.on("close", (code) => console.log(`[${execName}] exited with code ${code}`));
49-
child.on("kill", () => {
50-
console.log(`[${execName}] process killed`);
51-
});
5248
child.name = command.replace(/^.*[\\/]/u, "");
5349

50+
child.on("spawn", () => {
51+
console.log(`[${child.name}] spawned, pid=${child.pid}`);
52+
});
53+
54+
const controller = new AbortController();
55+
const timer = setTimeout(
56+
() => controller.abort(),
57+
timeoutSeconds * MILLISECONDS_PER_SECOND,
58+
);
59+
if (typeof timer.unref === "function") timer.unref();
60+
5461
try {
55-
return await pTimeout(waitForReady(child, expectedResponse), {
56-
milliseconds: timeoutSeconds * MILLISECONDS_PER_SECOND,
57-
message: `Timed out after ${timeoutSeconds} seconds`,
58-
});
62+
const result = await waitForReady(child, expectedResponse, controller.signal);
63+
clearTimeout(timer);
64+
return result;
5965
} catch (error) {
66+
clearTimeout(timer);
6067
child.kill();
6168
throw error;
6269
}
@@ -73,11 +80,11 @@ async function runBack(execName, execPath, args = {}) {
7380
}
7481
const port = await getAvailablePort();
7582
const backArgs = [
76-
`--port ${port}`,
77-
`--data_folder_path ${projectFolderPath}`,
78-
`--upload_folder_path ${uploadFolderPath}`,
79-
`--allowed_origin http://localhost:*`,
80-
`--timeout ${0}`,
83+
"--port", String(port),
84+
"--data_folder_path", projectFolderPath,
85+
"--upload_folder_path", uploadFolderPath,
86+
"--allowed_origin", "http://localhost:*",
87+
"--timeout", "0",
8188
];
8289
if (process.env.NODE_ENV === "development" || !process.env.NODE_ENV) {
8390
backArgs.push("--debug");
@@ -94,9 +101,9 @@ async function runViewer(execName, execPath, args = {}) {
94101
}
95102
const port = await getAvailablePort();
96103
const viewerArgs = [
97-
`--port ${port}`,
98-
`--data_folder_path ${projectFolderPath}`,
99-
`--timeout ${0}`,
104+
"--port", String(port),
105+
"--data_folder_path", projectFolderPath,
106+
"--timeout", "0",
100107
];
101108
console.log("runViewer", execPath, execName, viewerArgs);
102109
await runScript(execPath, execName, viewerArgs, "Starting factory");

app/utils/local/scripts.js

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// Node imports
2-
import child_process from "node:child_process";
32
import fs from "node:fs";
43
import { on } from "node:events";
54
import path from "node:path";
5+
import readline from "node:readline";
66

77
import { appMode } from "./app_mode.js";
88

9+
const MAX_ERROR_BUFFER_BYTES = 64 * 1024;
10+
911
function commandExistsSync(execName) {
1012
const envPath = process.env.PATH || "";
1113
return envPath.split(path.delimiter).some((dir) => {
@@ -14,13 +16,67 @@ function commandExistsSync(execName) {
1416
});
1517
}
1618

17-
async function waitForReady(child, expectedResponse) {
18-
for await (const [data] of on(child.stdout, "data")) {
19-
if (data.toString().includes(expectedResponse)) {
20-
return child;
21-
}
22-
}
23-
throw new Error("Process closed before signal");
19+
function waitForReady(child, expectedResponse, signal) {
20+
return new Promise((resolve, reject) => {
21+
const readlineStdout = readline.createInterface({ input: child.stdout });
22+
const readlineStderr = readline.createInterface({ input: child.stderr });
23+
24+
let recentOutput = "";
25+
const recordOutput = (line) => {
26+
recentOutput = (recentOutput + line + "\n").slice(-MAX_ERROR_BUFFER_BYTES);
27+
};
28+
29+
const cleanup = () => {
30+
readlineStdout.removeAllListeners();
31+
readlineStdout.close();
32+
readlineStderr.removeAllListeners();
33+
readlineStderr.close();
34+
child.removeListener("error", onError);
35+
child.removeListener("close", onClose);
36+
if (signal) signal.removeEventListener("abort", onAbort);
37+
};
38+
39+
const onLine = (line) => {
40+
console.log(`[${child.name}] ${line}`);
41+
recordOutput(line);
42+
if (line.includes(expectedResponse)) {
43+
cleanup();
44+
resolve(child);
45+
}
46+
};
47+
48+
const onErrLine = (line) => {
49+
console.log(`[${child.name}] ${line}`);
50+
recordOutput(line);
51+
};
52+
53+
const onError = (err) => {
54+
cleanup();
55+
reject(err);
56+
};
57+
58+
const onClose = (code) => {
59+
console.log(`[${child.name}] exited with code ${code}`);
60+
cleanup();
61+
reject(
62+
new Error(
63+
`[${child.name}] exited with code ${code} before becoming ready.` +
64+
(recentOutput ? `\nRecent output:\n${recentOutput}` : ""),
65+
),
66+
);
67+
};
68+
69+
const onAbort = () => {
70+
cleanup();
71+
reject(new Error(`[${child.name}] timed out waiting for "${expectedResponse}"`));
72+
};
73+
74+
readlineStdout.on("line", onLine);
75+
readlineStderr.on("line", onErrLine);
76+
child.once("error", onError);
77+
child.once("close", onClose);
78+
if (signal) signal.addEventListener("abort", onAbort, { once: true });
79+
})
2480
}
2581

2682
async function waitNuxt(nuxtProcess) {

0 commit comments

Comments
 (0)