From 0312a3b099787dbce46979362a3d0e17dcc5f700 Mon Sep 17 00:00:00 2001 From: bbassie <17990055+bbassie@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:29:42 +0000 Subject: [PATCH] Start the controller and host CLIs faster Like ctl, the controller and host now compile schema validators on first use instead of at import, which cost about 120 ms per invocation. The Controller and Host classes are only imported by the commands that start them, so config, bootstrap and the other commands no longer load express, ws and the rest of the servers. clusteriocontroller config set goes from 413 ms to 231 ms and clusteriohost config set from 412 ms to 225 ms. The integration tests run the bins, like npx does, instead of the package directories. Co-Authored-By: Claude Opus 5.5 --- packages/controller/controller.ts | 7 ++++++- packages/controller/src/set_lazy_schema_compilation.ts | 3 +++ packages/host/host.ts | 6 +++++- packages/host/src/set_lazy_schema_compilation.ts | 3 +++ test/integration/index.js | 6 +++--- 5 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 packages/controller/src/set_lazy_schema_compilation.ts create mode 100644 packages/host/src/set_lazy_schema_compilation.ts diff --git a/packages/controller/controller.ts b/packages/controller/controller.ts index 46b04cb0..9e154c47 100644 --- a/packages/controller/controller.ts +++ b/packages/controller/controller.ts @@ -26,10 +26,12 @@ import DailyRotateFile from "winston-daily-rotate-file"; import jwt from "jsonwebtoken"; // homebrew modules +// Reduce startup time by lazy compiling schemas. +import "./src/set_lazy_schema_compilation.js"; import * as lib from "@clusterio/lib"; import { ConsoleTransport, levels, logger } from "@clusterio/lib"; -import Controller from "./src/Controller.js"; +import type Controller from "./src/Controller.js"; import UserManager from "./src/UserManager.js"; import UserRecord from "./src/UserRecord.js"; import packageConfig from "@clusterio/controller/package.json" with { type: "json" }; @@ -203,6 +205,7 @@ async function handleBootstrapCommand( } let ctlConfig = new lib.CtlConfig("control"); + const { default: Controller } = await import("./src/Controller.js"); ctlConfig.set("ctl.controller_url", Controller.calculateControllerUrl(controllerConfig)); ctlConfig.set( "ctl.controller_token", @@ -475,6 +478,8 @@ async function startup() { return; } + // Loaded here as the server is not needed by the other commands + const { default: Controller } = await import("./src/Controller.js"); controller = await Controller.fromConfig( controllerConfig, pluginInfos, diff --git a/packages/controller/src/set_lazy_schema_compilation.ts b/packages/controller/src/set_lazy_schema_compilation.ts new file mode 100644 index 00000000..f5ff3418 --- /dev/null +++ b/packages/controller/src/set_lazy_schema_compilation.ts @@ -0,0 +1,3 @@ +// This needs to be a separate file due to ECMAScript Modules evaluating code in +// imported modules before any code in the file that imports it. +(global as any).lazySchemaCompilation = true; diff --git a/packages/host/host.ts b/packages/host/host.ts index 11ea2ba8..e9af8f0b 100755 --- a/packages/host/host.ts +++ b/packages/host/host.ts @@ -20,10 +20,12 @@ import winston from "winston"; import "winston-daily-rotate-file"; // internal libraries +// Reduce startup time by lazy compiling schemas. +import "./src/set_lazy_schema_compilation.js"; import * as lib from "@clusterio/lib"; import { ConsoleTransport, levels, logger } from "@clusterio/lib"; -import Host from "./src/Host.js"; +import type Host from "./src/Host.js"; import { createScenarioCommand, handleCreateScenarioCommand } from "./src/create_scenario.js"; let host: Host | undefined; @@ -233,6 +235,8 @@ async function startHost() { return; } + // Loaded here as the server is not needed by the other commands + const { default: Host } = await import("./src/Host.js"); let hostConnector = new HostConnector(hostConfig, pluginInfos); host = await Host.fromConfig( hostConfig, diff --git a/packages/host/src/set_lazy_schema_compilation.ts b/packages/host/src/set_lazy_schema_compilation.ts new file mode 100644 index 00000000..f5ff3418 --- /dev/null +++ b/packages/host/src/set_lazy_schema_compilation.ts @@ -0,0 +1,3 @@ +// This needs to be a separate file due to ECMAScript Modules evaluating code in +// imported modules before any code in the file that imports it. +(global as any).lazySchemaCompilation = true; diff --git a/test/integration/index.js b/test/integration/index.js index ccb977e7..158bbd2a 100644 --- a/test/integration/index.js +++ b/test/integration/index.js @@ -205,17 +205,17 @@ export function exec(command, options = {}) { } export function execController(...args) { - args[0] = `node --enable-source-maps ../../packages/controller ${args[0]}`; + args[0] = `node --enable-source-maps ../../packages/controller/dist/node/controller.js ${args[0]}`; return exec(...args); } export function execHost(...args) { - args[0] = `node --enable-source-maps ../../packages/host ${args[0]}`; + args[0] = `node --enable-source-maps ../../packages/host/dist/node/host.js ${args[0]}`; return exec(...args); } export function execCtlProcess(...args) { - args[0] = `node --enable-source-maps ../../packages/ctl ${args[0]}`; + args[0] = `node --enable-source-maps ../../packages/ctl/dist/node/ctl.js ${args[0]}`; return exec(...args); }