Skip to content

Commit b74cd69

Browse files
committed
perf(gateway): defer scheduled service imports
1 parent 0126aba commit b74cd69

3 files changed

Lines changed: 101 additions & 67 deletions

File tree

src/gateway/server-runtime-services.ts

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
22
import { isVitestRuntimeEnv } from "../infra/env.js";
33
import { startHeartbeatRunner, type HeartbeatRunner } from "../infra/heartbeat-runner.js";
44
import type { PluginMetadataRegistryView } from "../plugins/plugin-metadata-snapshot.types.js";
5-
import type { ChannelHealthMonitor } from "./channel-health-monitor.js";
6-
import { startChannelHealthMonitor } from "./channel-health-monitor.js";
75
import { isGatewayModelPricingEnabled } from "./model-pricing-config.js";
86
import type { startGatewayMaintenanceTimers } from "./server-maintenance.js";
7+
export {
8+
startGatewayChannelHealthMonitor,
9+
startGatewayRuntimeServices,
10+
type GatewayChannelManager,
11+
} from "./server-runtime-startup-services.js";
912

1013
type GatewayRuntimeServiceLogger = {
1114
child: (name: string) => {
@@ -22,37 +25,13 @@ export type GatewayMaintenanceHandles = NonNullable<
2225
Awaited<ReturnType<typeof startGatewayMaintenanceTimers>>
2326
>;
2427

25-
export type GatewayChannelManager = Parameters<
26-
typeof startChannelHealthMonitor
27-
>[0]["channelManager"];
28-
2928
function createNoopHeartbeatRunner(): HeartbeatRunner {
3029
return {
3130
stop: () => {},
3231
updateConfig: (_cfg: OpenClawConfig) => {},
3332
};
3433
}
3534

36-
export function startGatewayChannelHealthMonitor(params: {
37-
cfg: OpenClawConfig;
38-
channelManager: GatewayChannelManager;
39-
}): ChannelHealthMonitor | null {
40-
const healthCheckMinutes = params.cfg.gateway?.channelHealthCheckMinutes;
41-
if (healthCheckMinutes === 0) {
42-
return null;
43-
}
44-
const staleEventThresholdMinutes = params.cfg.gateway?.channelStaleEventThresholdMinutes;
45-
const maxRestartsPerHour = params.cfg.gateway?.channelMaxRestartsPerHour;
46-
return startChannelHealthMonitor({
47-
channelManager: params.channelManager,
48-
checkIntervalMs: (healthCheckMinutes ?? 5) * 60_000,
49-
...(staleEventThresholdMinutes != null && {
50-
staleEventThresholdMs: staleEventThresholdMinutes * 60_000,
51-
}),
52-
...(maxRestartsPerHour != null && { maxRestartsPerHour }),
53-
});
54-
}
55-
5635
export function startGatewayCronWithLogging(params: {
5736
cron: { start: () => Promise<void> };
5837
logCron: { error: (message: string) => void };
@@ -220,28 +199,6 @@ function startGatewayModelPricingRefreshOnDemand(params: {
220199
};
221200
}
222201

223-
export function startGatewayRuntimeServices(params: {
224-
minimalTestGateway: boolean;
225-
cfgAtStart: OpenClawConfig;
226-
channelManager: GatewayChannelManager;
227-
log: GatewayRuntimeServiceLogger;
228-
}): {
229-
heartbeatRunner: HeartbeatRunner;
230-
channelHealthMonitor: ChannelHealthMonitor | null;
231-
stopModelPricingRefresh: () => void;
232-
} {
233-
const channelHealthMonitor = startGatewayChannelHealthMonitor({
234-
cfg: params.cfgAtStart,
235-
channelManager: params.channelManager,
236-
});
237-
238-
return {
239-
heartbeatRunner: createNoopHeartbeatRunner(),
240-
channelHealthMonitor,
241-
stopModelPricingRefresh: () => {},
242-
};
243-
}
244-
245202
export function activateGatewayScheduledServices(params: {
246203
minimalTestGateway: boolean;
247204
cfgAtStart: OpenClawConfig;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type { OpenClawConfig } from "../config/types.openclaw.js";
2+
import type { ChannelHealthMonitor } from "./channel-health-monitor.js";
3+
import { startChannelHealthMonitor } from "./channel-health-monitor.js";
4+
5+
type GatewayRuntimeServiceLogger = {
6+
child: (name: string) => {
7+
info: (message: string) => void;
8+
warn: (message: string) => void;
9+
error: (message: string) => void;
10+
};
11+
error: (message: string) => void;
12+
};
13+
14+
export type GatewayChannelManager = Parameters<
15+
typeof startChannelHealthMonitor
16+
>[0]["channelManager"];
17+
18+
function createNoopHeartbeatRunner() {
19+
return {
20+
stop: () => {},
21+
updateConfig: (_cfg: OpenClawConfig) => {},
22+
};
23+
}
24+
25+
export function startGatewayChannelHealthMonitor(params: {
26+
cfg: OpenClawConfig;
27+
channelManager: GatewayChannelManager;
28+
}): ChannelHealthMonitor | null {
29+
const healthCheckMinutes = params.cfg.gateway?.channelHealthCheckMinutes;
30+
if (healthCheckMinutes === 0) {
31+
return null;
32+
}
33+
const staleEventThresholdMinutes = params.cfg.gateway?.channelStaleEventThresholdMinutes;
34+
const maxRestartsPerHour = params.cfg.gateway?.channelMaxRestartsPerHour;
35+
return startChannelHealthMonitor({
36+
channelManager: params.channelManager,
37+
checkIntervalMs: (healthCheckMinutes ?? 5) * 60_000,
38+
...(staleEventThresholdMinutes != null && {
39+
staleEventThresholdMs: staleEventThresholdMinutes * 60_000,
40+
}),
41+
...(maxRestartsPerHour != null && { maxRestartsPerHour }),
42+
});
43+
}
44+
45+
export function startGatewayRuntimeServices(params: {
46+
minimalTestGateway: boolean;
47+
cfgAtStart: OpenClawConfig;
48+
channelManager: GatewayChannelManager;
49+
log: GatewayRuntimeServiceLogger;
50+
}): {
51+
heartbeatRunner: ReturnType<typeof createNoopHeartbeatRunner>;
52+
channelHealthMonitor: ChannelHealthMonitor | null;
53+
stopModelPricingRefresh: () => void;
54+
} {
55+
const channelHealthMonitor = startGatewayChannelHealthMonitor({
56+
cfg: params.cfgAtStart,
57+
channelManager: params.channelManager,
58+
});
59+
60+
return {
61+
heartbeatRunner: createNoopHeartbeatRunner(),
62+
channelHealthMonitor,
63+
stopModelPricingRefresh: () => {},
64+
};
65+
}

src/gateway/server.impl.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,14 +1114,13 @@ export async function startGatewayServer(
11141114
getActiveTaskCount = earlyRuntime.getActiveTaskCount;
11151115
runtimeState.skillsChangeUnsub = earlyRuntime.skillsChangeUnsub;
11161116

1117-
const [{ startGatewayEventSubscriptions }, gatewayRuntimeServices] = await startupTrace.measure(
1118-
"runtime.post-early-imports",
1119-
() =>
1117+
const [{ startGatewayEventSubscriptions }, { startGatewayRuntimeServices }] =
1118+
await startupTrace.measure("runtime.post-early-imports", () =>
11201119
Promise.all([
11211120
import("./server-runtime-subscriptions.js"),
1122-
import("./server-runtime-services.js"),
1121+
import("./server-runtime-startup-services.js"),
11231122
]),
1124-
);
1123+
);
11251124
const runtimeSubscriptions = await startupTrace.measure("runtime.subscriptions", () =>
11261125
startGatewayEventSubscriptions({
11271126
broadcast,
@@ -1138,7 +1137,7 @@ export async function startGatewayServer(
11381137
Object.assign(runtimeState, runtimeSubscriptions);
11391138

11401139
const runtimeServices = await startupTrace.measure("runtime.services", () =>
1141-
gatewayRuntimeServices.startGatewayRuntimeServices({
1140+
startGatewayRuntimeServices({
11421141
minimalTestGateway,
11431142
cfgAtStart,
11441143
channelManager,
@@ -1516,6 +1515,13 @@ export async function startGatewayServer(
15161515
const sessionDeliveryRecoveryMaxEnqueuedAt = Date.now();
15171516
let postAttachRuntimeReturned = false;
15181517
let scheduledServicesActivated = false;
1518+
let scheduledServicesModulePromise: Promise<
1519+
typeof import("./server-runtime-services.js")
1520+
> | null = null;
1521+
const loadScheduledServicesModule = () => {
1522+
scheduledServicesModulePromise ??= import("./server-runtime-services.js");
1523+
return scheduledServicesModulePromise;
1524+
};
15191525
const activateScheduledServicesWhenReady = () => {
15201526
if (
15211527
closePreludeStarted ||
@@ -1525,20 +1531,25 @@ export async function startGatewayServer(
15251531
) {
15261532
return;
15271533
}
1528-
const activated = gatewayRuntimeServices.activateGatewayScheduledServices({
1529-
minimalTestGateway,
1530-
cfgAtStart,
1531-
deps,
1532-
sessionDeliveryRecoveryMaxEnqueuedAt,
1533-
cron: runtimeState.cronState.cron,
1534-
startCron: false,
1535-
logCron,
1536-
log,
1537-
pluginLookUpTable,
1538-
});
15391534
scheduledServicesActivated = true;
1540-
runtimeState.heartbeatRunner = activated.heartbeatRunner;
1541-
runtimeState.stopModelPricingRefresh = activated.stopModelPricingRefresh;
1535+
void loadScheduledServicesModule().then((gatewayRuntimeServices) => {
1536+
if (closePreludeStarted) {
1537+
return;
1538+
}
1539+
const activated = gatewayRuntimeServices.activateGatewayScheduledServices({
1540+
minimalTestGateway,
1541+
cfgAtStart,
1542+
deps,
1543+
sessionDeliveryRecoveryMaxEnqueuedAt,
1544+
cron: runtimeState.cronState.cron,
1545+
startCron: false,
1546+
logCron,
1547+
log,
1548+
pluginLookUpTable,
1549+
});
1550+
runtimeState.heartbeatRunner = activated.heartbeatRunner;
1551+
runtimeState.stopModelPricingRefresh = activated.stopModelPricingRefresh;
1552+
});
15421553
};
15431554
({
15441555
stopGatewayUpdateCheck: runtimeState.stopGatewayUpdateCheck,
@@ -1693,6 +1704,7 @@ export async function startGatewayServer(
16931704
log.warn(`gateway: failed to promote config last-known-good backup: ${String(err)}`);
16941705
});
16951706
if (!minimalTestGateway) {
1707+
const gatewayRuntimeServices = await loadScheduledServicesModule();
16961708
postReadyMaintenanceTimer = gatewayRuntimeServices.scheduleGatewayPostReadyMaintenance({
16971709
delayMs: POST_READY_MAINTENANCE_DELAY_MS,
16981710
isClosing: () => closePreludeStarted,

0 commit comments

Comments
 (0)