diff --git a/CHANGELOG.md b/CHANGELOG.md index 448e2800b93..a44b0914e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/channels/plugins/catalog.ts b/src/channels/plugins/catalog.ts index d4c5ade4201..4d7d0027fc9 100644 --- a/src/channels/plugins/catalog.ts +++ b/src/channels/plugins/catalog.ts @@ -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(); -const externalCatalogEntriesByPath = new Map(); +const externalCatalogEntriesByPath = new Map(); 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, - cache?: Map, + cache?: Map, ): 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; }