mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:10:45 +00:00
fix: keep sessions list responsive without catalog
This commit is contained in:
@@ -103,12 +103,46 @@ import { assertValidParams } from "./validation.js";
|
||||
type SessionsRuntimeModule = typeof import("./sessions.runtime.js");
|
||||
|
||||
let sessionsRuntimeModulePromise: Promise<SessionsRuntimeModule> | undefined;
|
||||
let loggedSlowSessionsListCatalog = false;
|
||||
|
||||
const SESSIONS_LIST_MODEL_CATALOG_TIMEOUT_MS = 750;
|
||||
|
||||
function loadSessionsRuntimeModule(): Promise<SessionsRuntimeModule> {
|
||||
sessionsRuntimeModulePromise ??= import("./sessions.runtime.js");
|
||||
return sessionsRuntimeModulePromise;
|
||||
}
|
||||
|
||||
async function loadOptionalSessionsListModelCatalog(
|
||||
context: GatewayRequestContext,
|
||||
): Promise<Awaited<ReturnType<GatewayRequestContext["loadGatewayModelCatalog"]>> | undefined> {
|
||||
let timeout: NodeJS.Timeout | undefined;
|
||||
const timedOut = Symbol("sessions-list-model-catalog-timeout");
|
||||
const timeoutPromise = new Promise<typeof timedOut>((resolve) => {
|
||||
timeout = setTimeout(() => resolve(timedOut), SESSIONS_LIST_MODEL_CATALOG_TIMEOUT_MS);
|
||||
timeout.unref?.();
|
||||
});
|
||||
try {
|
||||
const result = await Promise.race([
|
||||
context.loadGatewayModelCatalog().catch(() => undefined),
|
||||
timeoutPromise,
|
||||
]);
|
||||
if (result === timedOut) {
|
||||
if (!loggedSlowSessionsListCatalog) {
|
||||
loggedSlowSessionsListCatalog = true;
|
||||
context.logGateway.debug(
|
||||
`sessions.list continuing without model catalog after ${SESSIONS_LIST_MODEL_CATALOG_TIMEOUT_MS}ms`,
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
return Array.isArray(result) ? result : undefined;
|
||||
} finally {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function requireSessionKey(key: unknown, respond: RespondFn): string | null {
|
||||
const raw =
|
||||
typeof key === "string"
|
||||
@@ -613,8 +647,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
|
||||
const p = params;
|
||||
const cfg = context.getRuntimeConfig();
|
||||
const { storePath, store } = loadCombinedSessionStoreForGateway(cfg);
|
||||
const loadedCatalog = await context.loadGatewayModelCatalog().catch(() => undefined);
|
||||
const modelCatalog = Array.isArray(loadedCatalog) ? loadedCatalog : undefined;
|
||||
const modelCatalog = await loadOptionalSessionsListModelCatalog(context);
|
||||
const result = listSessionsFromStore({
|
||||
cfg,
|
||||
storePath,
|
||||
|
||||
@@ -46,6 +46,16 @@ async function getSessionsHandlers() {
|
||||
return (await import("./server-methods/sessions.js")).sessionsHandlers;
|
||||
}
|
||||
|
||||
function createDeferred<T>() {
|
||||
let resolve!: (value: T | PromiseLike<T>) => void;
|
||||
let reject!: (reason?: unknown) => void;
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
const sessionCleanupMocks = vi.hoisted(() => ({
|
||||
clearSessionQueues: vi.fn((keys: Array<string | undefined>) => {
|
||||
const clearedKeys = Array.from(
|
||||
@@ -832,6 +842,58 @@ describe("gateway server sessions", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("sessions.list does not block on slow model catalog discovery", async () => {
|
||||
await createSessionStoreDir();
|
||||
await writeSessionStore({
|
||||
entries: {
|
||||
main: {
|
||||
sessionId: "sess-main",
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const deferredCatalog = createDeferred<never>();
|
||||
const respond = vi.fn();
|
||||
const sessionsHandlers = await getSessionsHandlers();
|
||||
const { getRuntimeConfig } = await getGatewayConfigModule();
|
||||
const request = sessionsHandlers["sessions.list"]({
|
||||
req: {
|
||||
type: "req",
|
||||
id: "req-sessions-list-slow-catalog",
|
||||
method: "sessions.list",
|
||||
params: {},
|
||||
},
|
||||
params: {},
|
||||
respond,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
context: {
|
||||
getRuntimeConfig,
|
||||
loadGatewayModelCatalog: vi.fn(() => deferredCatalog.promise),
|
||||
logGateway: {
|
||||
debug: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(800);
|
||||
await request;
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
true,
|
||||
expect.objectContaining({
|
||||
sessions: expect.arrayContaining([expect.objectContaining({ key: "agent:main:main" })]),
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
test("sessions.changed mutation events include live usage metadata", async () => {
|
||||
const { dir } = await createSessionStoreDir();
|
||||
await fs.writeFile(
|
||||
|
||||
Reference in New Issue
Block a user