perf(meta-push): use cached api.config instead of deprecated loadConfig()
`pushMetaToMonitor` and `resolveAgentId` were both calling `api.runtime?.config?.loadConfig?.()` to read the agent list. That deprecated path (openclaw warns at gateway start: "plugin runtime config.loadConfig() is deprecated; use config.current()") synchronously rebuilds the full plugin-metadata snapshot — realpathSync walks every plugin's package.json + manifest + source up the directory tree, hashWatchedFiles fingerprints every watched plugin file, and discoverInDirectory re-scans every `dist/extensions/<plugin>` (~100 of them on prod t2). Each rebuild costs ~6-7s of gateway CPU. `pushMetaToMonitor` fires every `reportIntervalSec` (default 30s) from `hooks/gateway-start.js`. With 100 plugins that put the gateway into a chronic ~22-30% CPU baseline even with zero agent activity. V8 profile 2026-05-27 08:14:00 60s window (0 turns, 2 metadata pushes during): lstat 44.2%, statSync(buildInstalledManifestRegistryIndexKey) 6.9%, hashWatchedFiles via memo key 1.7%, all routed through `readPersistedInstalledPluginIndexInstallRecordsSync` -> per-plugin `discoverInDirectory`. Switching to `(api as any).config ?? api.runtime?.config?.loadConfig?.()` reads from the snapshot cache the gateway already maintains — the same pattern already used elsewhere in this file (e.g. the calendar wakeAgent dispatcher at line 284). Same change applied to `resolveAgentId` (only runs once at start, but same anti-pattern). This is a plugin-side perf workaround. The underlying openclaw bug is that `loadConfig()` rebuilds the snapshot rather than returning the cached one — a chronic 'all sync cache validity checks pay the full discovery cost' design issue worth pushing upstream separately (the walks per-call cost we measured here is unrelated to and amplifies any agent-turn-triggered walk path). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -128,7 +128,9 @@ function register(api: PluginAPI): void {
|
|||||||
/** Resolve agent ID from env, config, or fallback. */
|
/** Resolve agent ID from env, config, or fallback. */
|
||||||
function resolveAgentId(): string {
|
function resolveAgentId(): string {
|
||||||
if (process.env.AGENT_ID) return process.env.AGENT_ID;
|
if (process.env.AGENT_ID) return process.env.AGENT_ID;
|
||||||
const cfg = api.runtime?.config?.loadConfig?.();
|
// Read from cached `api.config` first — see pushMetaToMonitor for why
|
||||||
|
// the deprecated `api.runtime?.config?.loadConfig?.()` path is heavy.
|
||||||
|
const cfg = (api as any).config ?? api.runtime?.config?.loadConfig?.();
|
||||||
return cfg?.agents?.list?.[0]?.id ?? cfg?.agents?.defaults?.id ?? 'unknown';
|
return cfg?.agents?.list?.[0]?.id ?? cfg?.agents?.defaults?.id ?? 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +186,25 @@ function register(api: PluginAPI): void {
|
|||||||
* Push OpenClaw metadata to the Monitor bridge.
|
* Push OpenClaw metadata to the Monitor bridge.
|
||||||
* This enriches Monitor heartbeats with OpenClaw version/plugin/agent info.
|
* This enriches Monitor heartbeats with OpenClaw version/plugin/agent info.
|
||||||
* Failures are non-fatal — Monitor continues to work without this data.
|
* Failures are non-fatal — Monitor continues to work without this data.
|
||||||
|
*
|
||||||
|
* IMPORTANT — read config from the cached `api.config` surface, NOT from
|
||||||
|
* the deprecated `api.runtime?.config?.loadConfig?.()` path. The
|
||||||
|
* deprecated path triggers a full plugin-metadata-snapshot rebuild on
|
||||||
|
* every call: realpathSync walks every plugin's package.json + manifest
|
||||||
|
* + source paths (lstats up the directory tree), `hashWatchedFiles`
|
||||||
|
* fingerprints all watched plugin files, and `discoverInDirectory`
|
||||||
|
* re-scans every `dist/extensions/<plugin>` dir. On t2 with ~100 plugins
|
||||||
|
* each rebuild costs ~6-7s of CPU; with this push firing every 30s
|
||||||
|
* (default reportIntervalSec) the chronic baseline was ~22-25% gateway
|
||||||
|
* CPU even with zero agent activity (V8 profile 2026-05-27 08:14:00 60s:
|
||||||
|
* lstat 44.2%, statSync 6.9%, hashWatchedFiles via memo key 1.7%, all
|
||||||
|
* routed through readPersistedInstalledPluginIndexInstallRecordsSync ->
|
||||||
|
* discoverInDirectory). Switching to `api.config` reads from the
|
||||||
|
* already-loaded snapshot cache; the elsewhere-in-this-file pattern was
|
||||||
|
* already `api.config ?? api.runtime?.config?.loadConfig?.()`.
|
||||||
|
*
|
||||||
|
* Same fix is applied to `resolveAgentId` below — that's read once at
|
||||||
|
* gateway start so the impact is smaller, but it's the same anti-pattern.
|
||||||
*/
|
*/
|
||||||
async function pushMetaToMonitor() {
|
async function pushMetaToMonitor() {
|
||||||
const bridgeClient = getBridgeClient();
|
const bridgeClient = getBridgeClient();
|
||||||
@@ -191,7 +212,7 @@ function register(api: PluginAPI): void {
|
|||||||
|
|
||||||
let agentNames: string[] = [];
|
let agentNames: string[] = [];
|
||||||
try {
|
try {
|
||||||
const cfg = api.runtime?.config?.loadConfig?.();
|
const cfg = (api as any).config ?? api.runtime?.config?.loadConfig?.();
|
||||||
const agentsList = cfg?.agents?.list;
|
const agentsList = cfg?.agents?.list;
|
||||||
if (Array.isArray(agentsList)) {
|
if (Array.isArray(agentsList)) {
|
||||||
agentNames = agentsList
|
agentNames = agentsList
|
||||||
|
|||||||
Reference in New Issue
Block a user