fix(gateway): reduce session group discovery CPU overhead (#147909)

This commit is contained in:
Vincent Koc 2026-09-14 13:19:04 +08:00 • committed by GitHub
parent b5b7ca44ac
commit 15f63bf749
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 89 additions and 1 deletions

View file

@ -1,7 +1,8 @@
import fs from "node:fs";
import { performance } from "node:perf_hooks";
import { DatabaseSync } from "node:sqlite";
import { expect, test, vi } from "vitest";
import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js";
import { loadSessionEntry, upsertSessionEntryCore } from "../config/sessions/session-accessor.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import * as sqliteIntegrity from "../infra/sqlite-integrity.js";
import * as sqliteWal from "../infra/sqlite-wal.js";
@ -9,6 +10,7 @@ import * as agentDatabaseLeases from "../state/openclaw-agent-db-lease.js";
import {
closeOpenClawAgentDatabasesForTest,
listOpenClawAgentDatabasesForTest,
openOpenClawAgentDatabase,
} from "../state/openclaw-agent-db.js";
import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js";
import { setStateDirEnv, withStateDirEnv } from "../test-helpers/state-dir-env.js";
@ -16,6 +18,89 @@ import { resolveSessionGroupMutationTargetsByName } from "./session-groups.js";
const EXPECTED_OPEN_HANDLE_CAP = 64;
test.each([false, true])(
"discovers current group members without decoding saved prompts (cold=%s)",
async (cold) => {
await withStateDirEnv("openclaw-session-group-metadata-", async ({ stateDir }) => {
setStateDirEnv(fs.realpathSync(stateDir));
const scopes = [
{ agentId: "main", sessionKey: "agent:main:group-member" },
{ agentId: "research", sessionKey: "agent:research:matrix:group:!Room:example.org" },
] as const;
const config = {
agents: { list: [{ id: "main", default: true }, { id: "research" }] },
} satisfies OpenClawConfig;
const entry = {
sessionId: "group-member",
updatedAt: 1,
category: " Shared work ",
skillsSnapshot: { prompt: "unneeded-group-prompt".repeat(4096), skills: [] },
systemPromptReport: {
source: "run" as const,
generatedAt: 1,
systemPrompt: { chars: 1, projectContextChars: 0, nonProjectContextChars: 1 },
injectedWorkspaceFiles: [],
skills: { promptChars: 0, entries: [] },
tools: { listChars: 0, schemaChars: 0, entries: [] },
},
};
const parse = vi.spyOn(JSON, "parse");
const readTargets = () => {
parse.mockClear();
const targets = resolveSessionGroupMutationTargetsByName(config);
expect(
parse.mock.calls.filter(
([json]) => json.includes('"skillsSnapshot"') || json.includes('"systemPromptReport"'),
),
).toEqual([]);
return targets;
};
try {
for (const scope of scopes) {
await upsertSessionEntryCore(scope, entry);
}
if (cold) {
closeOpenClawAgentDatabasesForTest();
}
expect(readTargets()).toEqual(new Map([["Shared work", scopes]]));
await upsertSessionEntryCore(scopes[0], { ...entry, category: "Renamed" });
expect(readTargets()).toEqual(
new Map([
["Renamed", [scopes[0]]],
["Shared work", [scopes[1]]],
]),
);
const database = openOpenClawAgentDatabase(scopes[0]);
const external = new DatabaseSync(database.path);
try {
external
.prepare(
"UPDATE session_nodes SET entry_json = json_set(entry_json, '$.category', ?) WHERE session_key = ?",
)
.run("External", scopes[0].sessionKey);
} finally {
external.close();
}
expect(readTargets()).toEqual(
new Map([
["External", [scopes[0]]],
["Shared work", [scopes[1]]],
]),
);
parse.mockRestore();
expect(loadSessionEntry(scopes[0])).toMatchObject({
skillsSnapshot: entry.skillsSnapshot,
systemPromptReport: entry.systemPromptReport,
});
} finally {
parse.mockRestore();
closeOpenClawAgentDatabasesForTest();
closeOpenClawStateDatabaseForTest();
}
});
},
);
test("discovers groups across more than the handle cap without writable database maintenance", async () => {
await withStateDirEnv("openclaw-session-group-readonly-", async ({ stateDir }) => {
setStateDirEnv(fs.realpathSync(stateDir));

View file

@ -450,6 +450,9 @@ export function resolveSessionGroupMutationTargetsByName(
for (const { sessionKey, entry } of listSessionEntriesReadOnly({
agentId: storeTarget.agentId,
storePath: storeTarget.storePath,
// Membership only borrows category metadata; full reads decode every saved prompt.
projection: "list",
clone: false,
})) {
const groupName = normalizeOptionalString(entry.category);
if (!groupName) {