refactor: simplify channel catalog cache

This commit is contained in:
Peter Steinberger
2026-05-24 00:11:55 +01:00
parent d4299dcbaa
commit a1c2d093c2
2 changed files with 11 additions and 27 deletions

View File

@@ -6,7 +6,7 @@ Docs: https://docs.openclaw.ai
### Changes
- Gateway/perf: reuse stat-fingerprinted channel catalog reads, avoid repeated bundled-channel boundary checks, and rotate gateway watch CPU profiles so benchmark runs do not accumulate unbounded artifacts.
- Gateway/perf: reuse process-stable channel catalog reads, avoid repeated bundled-channel boundary checks, and rotate gateway watch CPU profiles so benchmark runs do not accumulate unbounded artifacts.
- Gateway/perf: reuse immutable plugin metadata snapshots across startup, config, model, channel, setup, and secret metadata readers so hot paths avoid repeated plugin file stats and manifest registry reloads.
- Gateway/perf: lazy-load startup-idle plugin work, core gateway method handlers, and the embedded ACPX runtime so Gateway health and ready signals no longer wait on unused handler trees or ACPX probes.
- Gateway/perf: cache plugin SDK public-surface alias maps and skip irrelevant macOS Linuxbrew PATH probes so Gateway startup avoids repeated filesystem walks and slow missing-directory stats.

View File

@@ -1,4 +1,3 @@
import fs from "node:fs";
import path from "node:path";
import { MANIFEST_KEY } from "../../compat/legacy-names.js";
import type { PluginInstallRecord } from "../../config/types.plugins.js";
@@ -77,13 +76,8 @@ type ExternalCatalogEntry = {
const ENV_CATALOG_PATHS = ["OPENCLAW_PLUGIN_CATALOG_PATHS", "OPENCLAW_MPM_CATALOG_PATHS"];
const OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH = path.join("dist", "channel-catalog.json");
type CatalogEntriesCacheEntry = {
fingerprint: string;
entries: ExternalCatalogEntry[] | null;
};
const officialCatalogEntriesByPath = new Map<string, ExternalCatalogEntry[] | null>();
const externalCatalogEntriesByPath = new Map<string, CatalogEntriesCacheEntry>();
const externalCatalogEntriesByPath = new Map<string, ExternalCatalogEntry[] | null>();
type ManifestKey = typeof MANIFEST_KEY;
@@ -143,16 +137,6 @@ function loadExternalCatalogEntries(options: CatalogOptions): ExternalCatalogEnt
return loadCatalogEntriesFromPaths(paths, externalCatalogEntriesByPath);
}
function fingerprintCatalogPath(filePath: string): string {
try {
const stat = fs.statSync(filePath, { bigint: true });
const kind = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "other";
return [kind, stat.size.toString(), stat.mtimeNs.toString(), stat.ctimeNs.toString()].join(":");
} catch {
return "missing";
}
}
function readCatalogEntriesFromPath(resolvedPath: string): ExternalCatalogEntry[] | null {
const payload = tryReadJsonSync(resolvedPath);
return payload === null ? null : parseCatalogEntries(payload);
@@ -160,19 +144,19 @@ function readCatalogEntriesFromPath(resolvedPath: string): ExternalCatalogEntry[
function loadCatalogEntriesFromPaths(
paths: Iterable<string>,
cache?: Map<string, CatalogEntriesCacheEntry>,
cache?: Map<string, ExternalCatalogEntry[] | null>,
): ExternalCatalogEntry[] {
const entries: ExternalCatalogEntry[] = [];
for (const resolvedPath of paths) {
const fingerprint = cache ? fingerprintCatalogPath(resolvedPath) : undefined;
const cached = fingerprint ? cache?.get(resolvedPath) : undefined;
const parsed =
cached && cached.fingerprint === fingerprint
? cached.entries
: readCatalogEntriesFromPath(resolvedPath);
if (cache && fingerprint) {
cache.set(resolvedPath, { fingerprint, entries: parsed });
if (cache?.has(resolvedPath)) {
const cached = cache.get(resolvedPath);
if (cached) {
entries.push(...cached);
}
continue;
}
const parsed = readCatalogEntriesFromPath(resolvedPath);
cache?.set(resolvedPath, parsed);
if (parsed === null) {
continue;
}