Skip to content

Commit 73de597

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

4 files changed

Lines changed: 100 additions & 48 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 & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import child_process from "node:child_process";
33
import fs from "node:fs";
44
import { on } from "node:events";
55
import path from "node:path";
6+
import readline from "node:readline";
67

78
import { appMode } from "./app_mode.js";
89

10+
const MAX_ERROR_BUFFER_BYTES = 64 * 1024;
11+
912
function commandExistsSync(execName) {
1013
const envPath = process.env.PATH || "";
1114
return envPath.split(path.delimiter).some((dir) => {
@@ -14,13 +17,67 @@ function commandExistsSync(execName) {
1417
});
1518
}
1619

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

2683
async function waitNuxt(nuxtProcess) {

0 commit comments

Comments
 (0)