refactor(gateway): remove unused resident lifecycle registry (#128710)

* refactor(gateway): remove unused resident lifecycle registry

* chore(gateway): prune deleted registry assertion baseline
This commit is contained in:
Peter Steinberger
2026-08-24 05:57:43 -07:00
committed by GitHub
parent 5e438e9d0b
commit 1c0e02ccb0
9 changed files with 126 additions and 330 deletions
-1
View File
@@ -3052,7 +3052,6 @@ src/gateway/server-plugins-node-runtime.ts 1
src/gateway/server-plugins.ts 2
src/gateway/server-reload-restart.ts 2
src/gateway/server-reload-utils.ts 1
src/gateway/server-resident-registry.ts 1
src/gateway/server-restart-sentinel-agent-delivery.ts 2
src/gateway/server-runtime-handles.ts 11
src/gateway/server-runtime-state-prepare.ts 2
+6 -39
View File
@@ -151,8 +151,6 @@ export async function startGatewayCoreRuntime(input: {
workerEnvironmentStartup,
broadcastPluginEvent,
activateRuntimeSecrets,
residentRegistry,
shutdownRuntime,
} = runtime;
let currentPluginMetadataSnapshot = runtime.pluginMetadataSnapshot;
if (desktopSessionRegistry) {
@@ -171,11 +169,8 @@ export async function startGatewayCoreRuntime(input: {
if (secretEgressProxy) {
kernel.addGatewayLifetimeSidecar(secretEgressProxy);
}
let earlyRuntimePromise: ReturnType<
Awaited<ReturnType<typeof loadGatewayStartupEarlyModule>>["startGatewayEarlyRuntime"]
> | null = null;
const startEarlyRuntime = () => {
earlyRuntimePromise ??= loadGatewayStartupEarlyModule().then(({ startGatewayEarlyRuntime }) =>
const earlyRuntime = await startupTrace.measure("runtime.early", () =>
loadGatewayStartupEarlyModule().then(({ startGatewayEarlyRuntime }) =>
startGatewayEarlyRuntime({
minimalTestGateway,
cfgAtStart,
@@ -221,25 +216,7 @@ export async function startGatewayCoreRuntime(input: {
getRuntimeConfig,
startupTrace,
}),
);
return earlyRuntimePromise;
};
const discoveryResident = residentRegistry.register({
name: "bonjour-discovery",
start: startEarlyRuntime,
stop: async () => await kernel.swapBonjourStop(null)?.(),
});
const taskAndSkillsResident = residentRegistry.register({
name: "task-and-skills-runtime",
start: async () => await discoveryResident.start(),
stop: async () => {
const earlyRuntime = await startEarlyRuntime();
await earlyRuntime.skillsChangeUnsub();
shutdownRuntime.stopTaskRegistryMaintenance();
},
});
const earlyRuntime = await startupTrace.measure("runtime.early", () =>
taskAndSkillsResident.start(),
),
);
kernel.setEarlyRuntimeHandles(earlyRuntime);
@@ -250,9 +227,8 @@ export async function startGatewayCoreRuntime(input: {
import("./server-runtime-startup-services.js"),
]),
);
const eventSubscriptionsResident = residentRegistry.register({
name: "event-subscriptions",
start: () =>
const { sessionCompanion, sessionObserver, ...runtimeSubscriptionUnsubs } =
await startupTrace.measure("runtime.subscriptions", () =>
startGatewayEventSubscriptions({
log,
broadcast,
@@ -267,16 +243,7 @@ export async function startGatewayCoreRuntime(input: {
restartRecoveryCandidates,
terminalSessions,
}),
stop: async () => {
await runtimeState.agentUnsub?.();
runtimeState.heartbeatUnsub?.();
runtimeState.transcriptUnsub?.();
runtimeState.lifecycleUnsub?.();
runtimeState.taskUnsub?.();
},
});
const { sessionCompanion, sessionObserver, ...runtimeSubscriptionUnsubs } =
await startupTrace.measure("runtime.subscriptions", () => eventSubscriptionsResident.start());
);
Object.assign(runtimeState, runtimeSubscriptionUnsubs);
await startupTrace.measure("runtime.services", () =>
-13
View File
@@ -56,18 +56,6 @@ describe("createGatewayKernel", () => {
expect(getStartup()).toMatchObject({ ok: true, status: "started" });
expect(getReadiness()).toMatchObject({ ready: true, failing: [] });
const discoveryResident = kernel.residentRegistry
.list()
.find((resident) => resident.name === "bonjour-discovery");
if (!discoveryResident) {
throw new Error("Expected the Gateway discovery resident");
}
const residentFirstStop = vi.fn(async () => {});
kernel.kernel.swapBonjourStop(residentFirstStop);
await discoveryResident.stop();
expect(residentFirstStop).toHaveBeenCalledOnce();
expect(kernel.runtimeState.bonjourStop).toBeNull();
const closeFirstStop = vi.fn(async () => {});
kernel.kernel.swapBonjourStop(closeFirstStop);
const configReloaderStop = createDeferred();
@@ -80,7 +68,6 @@ describe("createGatewayKernel", () => {
expect(getReadiness()).toMatchObject({ ready: false, failing: ["gateway-draining"] });
configReloaderStop.resolve();
await closing;
await discoveryResident.stop();
expect(closeFirstStop).toHaveBeenCalledOnce();
expect(kernel.runtimeState.bonjourStop).toBeNull();
} finally {
+21 -29
View File
@@ -85,7 +85,6 @@ export async function prepareGatewayLifecycle(params: {
watchNodeRequestHandler,
defaultWorkspaceDir,
activeTaskCount,
residentRegistry,
desktopSessionRegistry,
nodeDesktopStreamBroker,
nodeDesktopObserveAvailable,
@@ -598,35 +597,28 @@ export async function prepareGatewayLifecycle(params: {
});
};
const diagnosticHeartbeatResident = residentRegistry.register({
name: "diagnostic-heartbeat",
start: () => {
// Gateway lifecycle owns both this existing heartbeat timer and the monitor
// it samples, so startup failure and normal close tear them down together.
startDiagnosticHeartbeat(undefined, {
getConfig: getRuntimeConfig,
startupGraceMs: 60_000,
sampleLiveness: () => {
const sample = readinessEventLoopHealth.persistentDegradationSnapshot();
if (!sample || sample.degradedSinceMs == null) {
return null;
}
return {
reasons: sample.reasons,
intervalMs: sample.intervalMs,
degradedSinceMs: sample.degradedSinceMs,
eventLoopDelayP99Ms: sample.delayP99Ms,
eventLoopDelayMaxMs: sample.delayMaxMs,
eventLoopUtilization: sample.utilization,
cpuCoreRatio: sample.cpuCoreRatio,
};
},
});
},
stop: () => stopDiagnosticHeartbeat(),
});
if (diagnosticsEnabled) {
diagnosticHeartbeatResident.start();
// Gateway lifecycle owns both this existing heartbeat timer and the monitor
// it samples, so startup failure and normal close tear them down together.
startDiagnosticHeartbeat(undefined, {
getConfig: getRuntimeConfig,
startupGraceMs: 60_000,
sampleLiveness: () => {
const sample = readinessEventLoopHealth.persistentDegradationSnapshot();
if (!sample || sample.degradedSinceMs == null) {
return null;
}
return {
reasons: sample.reasons,
intervalMs: sample.intervalMs,
degradedSinceMs: sample.degradedSinceMs,
eventLoopDelayP99Ms: sample.delayP99Ms,
eventLoopDelayMaxMs: sample.delayMaxMs,
eventLoopUtilization: sample.utilization,
cpuCoreRatio: sample.cpuCoreRatio,
};
},
});
}
return {
-35
View File
@@ -1,35 +0,0 @@
type GatewayResident<
TStart extends () => unknown = () => unknown,
TStop extends () => unknown = () => unknown,
> = {
name: string;
start: TStart;
stop: TStop;
};
export type GatewayResidentRegistry = {
register: <TStart extends () => unknown, TStop extends () => unknown>(
resident: GatewayResident<TStart, TStop>,
) => GatewayResident<TStart, TStop>;
list: () => readonly GatewayResident[];
};
/** Records resident lifecycle owners without changing when callers start or stop them. */
export function createGatewayResidentRegistry(): GatewayResidentRegistry {
const residents: GatewayResident[] = [];
const names = new Set<string>();
return {
register: <TStart extends () => unknown, TStop extends () => unknown>(
resident: GatewayResident<TStart, TStop>,
) => {
if (names.has(resident.name)) {
throw new Error(`Gateway resident already registered: ${resident.name}`);
}
names.add(resident.name);
residents.push(resident as GatewayResident);
return resident;
},
list: () => residents,
};
}
+1 -15
View File
@@ -24,7 +24,6 @@ import { createGatewayControlUiRootLifecycle } from "./server-control-ui-root.js
import type { GatewayInstanceRuntime } from "./server-instance-runtime.types.js";
import type { GatewayServerLiveState } from "./server-live-state.js";
import type { GatewayRequestContext } from "./server-methods/types.js";
import { createGatewayResidentRegistry } from "./server-resident-registry.js";
import type { SharedGatewaySessionGenerationState } from "./server-shared-auth-generation.js";
import type { prepareGatewayServerBootstrap } from "./server-startup-bootstrap.js";
import { createGatewayTransportBridge } from "./server-transport-bridge.js";
@@ -380,25 +379,13 @@ export async function prepareGatewayKernelState(params: {
const systemAgentSessions: GatewayRequestContext["systemAgentSessions"] = new Map();
const deps = createDefaultDeps();
const residentRegistry = createGatewayResidentRegistry();
const runtimeStateRef: { current: GatewayServerLiveState | null } = { current: null };
const cronStartState = { handled: false };
const gatewayTls = await startupTrace.measure("tls.runtime", () =>
loadGatewayTlsRuntime(cfgAtStart.gateway?.tls, log.child("tls")),
);
const serverStartedAt = Date.now();
const eventLoopHealthState: {
current?: ReturnType<typeof createGatewayEventLoopHealthMonitor>;
} = {};
const eventLoopHealthResident = residentRegistry.register({
name: "event-loop-health",
start: () => {
eventLoopHealthState.current ??= createGatewayEventLoopHealthMonitor();
return eventLoopHealthState.current;
},
stop: () => eventLoopHealthState.current?.stop(),
});
const readinessEventLoopHealth = eventLoopHealthResident.start();
const readinessEventLoopHealth = createGatewayEventLoopHealthMonitor();
const startupState = {
sidecarsReady: minimalTestGateway,
pendingReason: "startup-sidecars",
@@ -581,7 +568,6 @@ export async function prepareGatewayKernelState(params: {
purgeWizardSession,
systemAgentSessions,
deps,
residentRegistry,
runtimeStateRef,
cronStartState,
gatewayTls,
+77 -129
View File
@@ -129,7 +129,6 @@ export async function finishGatewayStartup(params: {
chatMetadataLifecycle,
gatewayRequestContext,
gatewayInstanceRuntime,
residentRegistry,
getPluginMetadataSnapshot,
} = runtime;
const startupPluginRuntimeClaim = kernel.pluginRuntimeGeneration.currentClaim();
@@ -184,20 +183,6 @@ export async function finishGatewayStartup(params: {
await startupTrace.measure("http.listen", () => startListening());
kernel.setDispatchReady(true);
startupTrace.mark("http.bound");
let databaseVerifierHandle: { stop: () => void | Promise<void> } | null = null;
const databaseIntegrityResident = residentRegistry.register({
name: "database-integrity-verifier",
start: async () => {
if (minimalTestGateway) {
return;
}
const { startOpenClawDatabaseIntegrityVerifier } =
await import("../state/openclaw-database-verify.js");
databaseVerifierHandle = startOpenClawDatabaseIntegrityVerifier({ env: process.env });
kernel.addGatewayLifetimeSidecar(databaseVerifierHandle);
},
stop: async () => await databaseVerifierHandle?.stop(),
});
const sessionDeliveryRecoveryMaxEnqueuedAt = Date.now();
let postAttachRuntimeReturned = false;
let scheduledServicesActivated = false;
@@ -205,43 +190,35 @@ export async function finishGatewayStartup(params: {
() => import("./server-runtime-services.js"),
{ cacheRejections: true },
);
const scheduledServicesResident = residentRegistry.register({
name: "scheduled-services",
start: () => {
if (
lifecycle.closePreludeStarted ||
!postAttachRuntimeReturned ||
!startupState.sidecarsReady ||
scheduledServicesActivated
) {
const activateScheduledServicesWhenReady = () => {
if (
lifecycle.closePreludeStarted ||
!postAttachRuntimeReturned ||
!startupState.sidecarsReady ||
scheduledServicesActivated
) {
return;
}
scheduledServicesActivated = true;
void loadScheduledServicesModule().then((gatewayRuntimeServices) => {
if (lifecycle.closePreludeStarted) {
return;
}
scheduledServicesActivated = true;
void loadScheduledServicesModule().then((gatewayRuntimeServices) => {
if (lifecycle.closePreludeStarted) {
return;
}
const activated = gatewayRuntimeServices.activateGatewayScheduledServices({
minimalTestGateway,
cfgAtStart,
deps,
sessionDeliveryRecoveryMaxEnqueuedAt,
cronState: runtimeState.cronState,
cronReconciliation,
startCron: false,
logCron,
log,
resolveGatewayContext: resolvePluginGatewayContext,
});
kernel.setScheduledServiceHandles(activated);
const activated = gatewayRuntimeServices.activateGatewayScheduledServices({
minimalTestGateway,
cfgAtStart,
deps,
sessionDeliveryRecoveryMaxEnqueuedAt,
cronState: runtimeState.cronState,
cronReconciliation,
startCron: false,
logCron,
log,
resolveGatewayContext: resolvePluginGatewayContext,
});
},
stop: async () => {
await runtimeState.stopOutboundDeliveryRecovery();
runtimeState.heartbeatRunner.stop();
},
});
const activateScheduledServicesWhenReady = scheduledServicesResident.start;
kernel.setScheduledServiceHandles(activated);
});
};
const { createGatewayServerActiveWorkInspectors } = await import("./server-active-work.js");
const postAttachHandles = await startupTrace.measure("runtime.post-attach", () =>
loadGatewayStartupPostAttachModule().then(({ startGatewayPostAttachRuntime }) =>
@@ -348,7 +325,6 @@ export async function finishGatewayStartup(params: {
sidecarStartup,
waitForPostReadyWork: params.waitForPostReadyWork,
activeWorkInspectors: createGatewayServerActiveWorkInspectors(gatewayRequestContext),
residentRegistry,
providerAuthPrewarm: {
getConfig: getRuntimeConfig,
},
@@ -363,7 +339,9 @@ export async function finishGatewayStartup(params: {
}
finishGatewayRestartTrace("restart.ready", collectGatewayProcessMemoryUsageMb());
if (!minimalTestGateway) {
await databaseIntegrityResident.start();
const { startOpenClawDatabaseIntegrityVerifier } =
await import("../state/openclaw-database-verify.js");
kernel.addGatewayLifetimeSidecar(startOpenClawDatabaseIntegrityVerifier({ env: process.env }));
}
postAttachRuntimeReturned = true;
activateScheduledServicesWhenReady();
@@ -450,98 +428,68 @@ export async function finishGatewayStartup(params: {
...(opts.hotReloadRecovery ? { requestRecoveryRestart: opts.hotReloadRecovery } : {}),
restartRecoveryAvailable: opts.hotReloadRecovery !== undefined,
};
const configReloaderResident = residentRegistry.register({
name: "config-reloader",
start: () => startManagedGatewayConfigReloader(configReloaderParams),
stop: async () => await runtimeState.configReloader.stop(),
});
kernel.setConfigReloaderHandle(configReloaderResident.start());
kernel.setConfigReloaderHandle(startManagedGatewayConfigReloader(configReloaderParams));
await promoteConfigSnapshotToLastKnownGood(startupLastGoodSnapshot).catch((err: unknown) => {
log.warn(`gateway: failed to promote config last-known-good backup: ${String(err)}`);
});
if (!minimalTestGateway) {
const gatewayRuntimeServices = await loadScheduledServicesModule();
const maintenanceResident = residentRegistry.register({
name: "post-ready-maintenance",
start: () => {
postReadyState.maintenanceTimer =
gatewayRuntimeServices.scheduleGatewayPostReadyMaintenance({
delayMs: POST_READY_MAINTENANCE_DELAY_MS,
isClosing: () => lifecycle.closePreludeStarted,
onStarted: () => {
postReadyState.maintenanceTimer = null;
},
startMaintenance: async () => {
if (lifecycle.closePreludeStarted) {
return null;
}
return earlyRuntime.startMaintenance();
},
applyMaintenance: async (maintenance) => {
if (lifecycle.closePreludeStarted) {
clearInterval(maintenance.tickInterval);
clearInterval(maintenance.healthInterval);
clearInterval(maintenance.dedupeCleanup);
await maintenance.stopMediaCleanup();
clearInterval(maintenance.worktreeCleanup);
maintenance.skillCuratorCleanup();
return;
}
// Publish the stop owner before cleanup can touch SQLite or state paths;
// shutdown may begin immediately after this synchronous handoff.
kernel.setMaintenanceHandles(maintenance);
maintenance.startMediaCleanup();
},
shouldStartCron: () => !lifecycle.closePreludeStarted && !cronStartState.handled,
markCronStartHandled: () => {
cronStartState.handled = true;
},
cronState: runtimeState.cronState,
cronReconciliation,
cronConfig: cfgAtStart,
logCron,
log,
recordPostReadyMemory: () => {
startupTrace.detail("memory.post-ready", collectGatewayProcessMemoryUsageMb());
},
});
postReadyState.maintenanceTimer = gatewayRuntimeServices.scheduleGatewayPostReadyMaintenance({
delayMs: POST_READY_MAINTENANCE_DELAY_MS,
isClosing: () => lifecycle.closePreludeStarted,
onStarted: () => {
postReadyState.maintenanceTimer = null;
},
stop: () => {
if (postReadyState.maintenanceTimer) {
clearTimeout(postReadyState.maintenanceTimer);
postReadyState.maintenanceTimer = null;
startMaintenance: async () => {
if (lifecycle.closePreludeStarted) {
return null;
}
return earlyRuntime.startMaintenance();
},
applyMaintenance: async (maintenance) => {
if (lifecycle.closePreludeStarted) {
clearInterval(maintenance.tickInterval);
clearInterval(maintenance.healthInterval);
clearInterval(maintenance.dedupeCleanup);
await maintenance.stopMediaCleanup();
clearInterval(maintenance.worktreeCleanup);
maintenance.skillCuratorCleanup();
return;
}
// Publish the stop owner before cleanup can touch SQLite or state paths;
// shutdown may begin immediately after this synchronous handoff.
kernel.setMaintenanceHandles(maintenance);
maintenance.startMediaCleanup();
},
shouldStartCron: () => !lifecycle.closePreludeStarted && !cronStartState.handled,
markCronStartHandled: () => {
cronStartState.handled = true;
},
cronState: runtimeState.cronState,
cronReconciliation,
cronConfig: cfgAtStart,
logCron,
log,
recordPostReadyMemory: () => {
startupTrace.detail("memory.post-ready", collectGatewayProcessMemoryUsageMb());
},
});
maintenanceResident.start();
// The loop closes the previous server before this generation starts, so retired
// plugin installs are safe to remove. Wait for an idle window and resolve current
// install paths at execution time so cleanup cannot remove active code or delay a turn.
const retainedPluginCleanupResident = residentRegistry.register({
name: "retained-plugin-cleanup",
start: () => {
postReadyState.retainedPluginCleanupHandle = gatewayRuntimeServices.scheduleGatewayIdleTask(
{
delayMs: RETAINED_PLUGIN_CLEANUP_DELAY_MS,
retryDelayMs: RETAINED_PLUGIN_CLEANUP_DELAY_MS,
isClosing: () => lifecycle.closePreludeStarted,
isBusy: () => getActiveGatewayRootWorkCount({ excludeCurrent: true }) > 0,
run: async () => {
const { cleanupRetainedPluginInstallGenerations } =
await import("./server-retained-plugin-cleanup.js");
await cleanupRetainedPluginInstallGenerations({ log });
},
log,
errorMessage: "retained npm generation cleanup failed",
},
);
},
stop: () => {
postReadyState.retainedPluginCleanupHandle?.stop();
postReadyState.retainedPluginCleanupHandle = null;
postReadyState.retainedPluginCleanupHandle = gatewayRuntimeServices.scheduleGatewayIdleTask({
delayMs: RETAINED_PLUGIN_CLEANUP_DELAY_MS,
retryDelayMs: RETAINED_PLUGIN_CLEANUP_DELAY_MS,
isClosing: () => lifecycle.closePreludeStarted,
isBusy: () => getActiveGatewayRootWorkCount({ excludeCurrent: true }) > 0,
run: async () => {
const { cleanupRetainedPluginInstallGenerations } =
await import("./server-retained-plugin-cleanup.js");
await cleanupRetainedPluginInstallGenerations({ log });
},
log,
errorMessage: "retained npm generation cleanup failed",
});
retainedPluginCleanupResident.start();
} else {
startupTrace.detail("memory.post-ready", collectGatewayProcessMemoryUsageMb());
}
@@ -24,7 +24,6 @@ import {
} from "../process/gateway-work-admission.js";
import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js";
import { withEnvAsync } from "../test-utils/env.js";
import { createGatewayResidentRegistry } from "./server-resident-registry.js";
import "./server-startup-outcomes.test-support.js";
const hoisted = vi.hoisted(() => {
@@ -3850,7 +3849,6 @@ function createPostAttachParams(overrides: Partial<PostAttachParams> = {}): Post
error: vi.fn(),
},
unlockStartupMethods: vi.fn(),
residentRegistry: createGatewayResidentRegistry(),
providerAuthPrewarm: { enabled: false },
unregisterGatewayLifetimeSidecar: vi.fn(),
stopRegisteredPostReadySidecars: () => stopTrackedSidecars(publishedPostReadySidecars),
+21 -67
View File
@@ -40,7 +40,6 @@ import type { GatewayControlUiRootLifecycle } from "./server-control-ui-root.js"
import type { GatewayRecoveryRuntime } from "./server-instance-runtime.types.js";
import type { GatewayClient, GatewayContextResolver } from "./server-methods/shared-types.js";
import type { GatewayPluginRuntimeClaim } from "./server-plugin-runtime-generation.js";
import type { GatewayResidentRegistry } from "./server-resident-registry.js";
import type { refreshLatestUpdateRestartSentinel } from "./server-restart-sentinel.js";
import type { GatewaySidecarStartupMode } from "./server-sidecar-startup-mode.js";
import { scheduleContextCachePrewarm } from "./server-startup-context-cache-prewarm.js";
@@ -1206,36 +1205,26 @@ export async function startGatewayPostAttachRuntime(
};
waitForPostReadyWork?: () => Promise<void>;
activeWorkInspectors?: Partial<GatewayActiveWorkInspectors>;
residentRegistry: GatewayResidentRegistry;
},
runtimeDeps: GatewayPostAttachRuntimeDeps = defaultGatewayPostAttachRuntimeDeps,
) {
const controlUiRootLifecycle = params.controlUiRootLifecycle;
const mainSessionRecoveryStartupCheckedStorePaths = new Set<string>();
let controlUiAssetsSidecar: GatewayPostReadySidecarHandle | undefined;
const controlUiAssetsResident = params.residentRegistry.register({
name: "control-ui-assets",
start: () => {
controlUiAssetsSidecar =
!params.minimalTestGateway && controlUiRootLifecycle?.state?.kind === "preparing"
? schedulePostReadySidecarTask({
name: "sidecars.control-ui-assets",
startupTrace: params.startupTrace,
log: params.log,
run: controlUiRootLifecycle.start,
stop: controlUiRootLifecycle.stop,
})
: undefined;
if (controlUiAssetsSidecar) {
// Publish before the first await: slow CA/plugin startup must not strand
// the dashboard or hide its running builder from Gateway shutdown.
params.onGatewayLifetimeSidecars?.([controlUiAssetsSidecar]);
}
return controlUiAssetsSidecar;
},
stop: async () => await controlUiAssetsSidecar?.stop(),
});
controlUiAssetsResident.start();
const controlUiAssetsSidecar =
!params.minimalTestGateway && controlUiRootLifecycle?.state?.kind === "preparing"
? schedulePostReadySidecarTask({
name: "sidecars.control-ui-assets",
startupTrace: params.startupTrace,
log: params.log,
run: controlUiRootLifecycle.start,
stop: controlUiRootLifecycle.stop,
})
: undefined;
if (controlUiAssetsSidecar) {
// Publish before the first await: slow CA/plugin startup must not strand
// the dashboard or hide its running builder from Gateway shutdown.
params.onGatewayLifetimeSidecars?.([controlUiAssetsSidecar]);
}
if (!params.minimalTestGateway) {
// The HTTP server is already attached, so keep health probes responsive while the worker
@@ -1287,12 +1276,6 @@ export async function startGatewayPostAttachRuntime(
})();
return await startupPluginsLoadPromise;
};
const startupPluginsResident = params.residentRegistry.register({
name: "startup-plugin-load",
start: loadStartupPluginsIfNeeded,
stop: () => {},
});
let startupLogPromise: Promise<void> | undefined;
const startupLogSettled = createDeferredCore();
// Tailscale and sidecar work can delay the public readiness handle past log failure.
@@ -1348,11 +1331,6 @@ export async function startGatewayPostAttachRuntime(
activeWorkInspectors: params.activeWorkInspectors,
});
const updateCheckResident = params.residentRegistry.register({
name: "update-check",
start: updateCheck.start,
stop: updateCheck.stop,
});
let pluginServicesReported = false;
let reportedPluginServices: PluginServicesHandle | null = null;
const reportPluginServices = (pluginServices: PluginServicesHandle | null) => {
@@ -1394,7 +1372,7 @@ export async function startGatewayPostAttachRuntime(
skipStartupLog();
return emptySidecarResult();
}
await startupPluginsResident.start();
await loadStartupPluginsIfNeeded();
if (params.isClosing?.()) {
skipStartupLog();
return emptySidecarResult();
@@ -1606,31 +1584,7 @@ export async function startGatewayPostAttachRuntime(
params.log.info("gateway ready");
return { ...result, postReadySidecars, gatewayLifetimeSidecars, pluginRegistry };
});
let startedSidecars: ReturnType<typeof startSidecars> | undefined;
const sidecarSequenceResident = params.residentRegistry.register({
name: "sidecar-sequence",
start: () => {
startedSidecars ??= startSidecars();
return startedSidecars;
},
stop: async () => {
const result = await startedSidecars;
for (const sidecar of result?.postReadySidecars ?? []) {
await sidecar.stop();
}
},
});
const perConfigSidecarsResident = params.residentRegistry.register({
name: "per-config-sidecars",
start: sidecarSequenceResident.start,
stop: async () => {
const result = await startedSidecars;
for (const sidecar of result?.gatewayLifetimeSidecars ?? []) {
await sidecar.stop();
}
},
});
const sidecarsPromise = perConfigSidecarsResident.start();
const sidecarsPromise = startSidecars();
void sidecarsPromise
.then(async (sidecarsResult) => {
@@ -1684,15 +1638,15 @@ export async function startGatewayPostAttachRuntime(
if (params.sidecarStartup !== "defer") {
const sidecarsResult = await sidecarsPromise;
updateCheckResident.start();
updateCheck.start();
return {
stopGatewayUpdateCheck: updateCheckResident.stop,
stopGatewayUpdateCheck: updateCheck.stop,
pluginServices: sidecarsResult.pluginServices,
startupSettled: Promise.resolve(),
};
}
updateCheckResident.start();
updateCheck.start();
const startupSettled = Promise.all([sidecarsPromise, startupLogSettled.promise]).then(
() => undefined,
);
@@ -1701,7 +1655,7 @@ export async function startGatewayPostAttachRuntime(
void startupSettled.catch(() => {});
return {
stopGatewayUpdateCheck: updateCheckResident.stop,
stopGatewayUpdateCheck: updateCheck.stop,
pluginServices: reportedPluginServices,
startupSettled,
};