Skip to content

perf: performance sweep#674

Merged
buremba merged 1 commit into
mainfrom
sweep/perf
May 13, 2026
Merged

perf: performance sweep#674
buremba merged 1 commit into
mainfrom
sweep/perf

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented May 13, 2026

Behavior-preserving performance fixes from the parallel performance-analysis sweep. Caching is bounded (immutable-per-process or short TTL) and credential-affecting paths invalidate correctly.

What landed (per area, with expected win)

Area Change Win
core/encryption.ts Memoize the derived ENCRYPTION_KEY buffer (was base64-decode + regex on every call). Removes fixed CPU from every verifyWorkerToken / decrypt (per worker→gateway RPC, per config-value load).
core/logger.ts Pass log info to Sentry by reference instead of {...info, message}. No unbounded shallow-clone-then-serialize on every prod error/warn.
core/sanitize.ts Single compiled regex for sensitive-key matching + maxDepth cap; drop dead env branch. Bounds the per-key array scan + deep clone on large config objects logged.
agent-worker/worker.ts Promise.all the output/ dir clearing (was serial awaited unlinks). Fewer serial syscalls on the per-message critical path.
agent-worker/gateway-integration.ts Drop JSON.stringify(payload).substring(0,500) log on the stream-delta POST; log identifying fields only. One full serialize-then-discard removed per delta on a long answer.
embeddings/embeddings.ts batchGenerateLocalEmbeddings passes the whole batch to transformers.js (single padded vectorized ONNX pass). ~2–5× CPU throughput on embedding backfill (the WASM backend is single-threaded; the old Promise.all over per-text calls ran serially).
connector-worker/executor.ts embed_backfill now batches embeddings instead of one round-trip per event. O(1) embedding call instead of O(events) on the "lots of events" path.
server/gateway/auth/api-auth-middleware.ts Run the local worker-token check before the remote OIDC userinfo fetch. Removes a remote round-trip from every worker-token request to /api/v1/agents/*.
server/gateway/.../message-handler-bridge.ts, unified-thread-consumer.ts Stop calling flushTracing() per inbound message (SimpleSpanProcessor already exports on span end). One outbound HTTP / connection-churn removed per message.
server/gateway/proxy/http-proxy.ts Pre-lowercase the env domain allow/deny lists once when building global config. Removes per-pattern .toLowerCase() from the per-request domain matcher.
server/utils/public-origin.ts Memoize getConfiguredPublicOrigin() (mirrors hasLocalFrontend). No new URL() per auth resolution.
server/workspace/multi-tenant.ts Bearer (PAT/OAuth) MCP auth now uses the shared 60s membership-role cache (was hard-bypassing it every request — the cookie path already trusts it). ~1 DB round-trip removed per steady-state bearer MCP call.
server/lobu/stores/postgres-stores.ts Hoist decrypt to a static import; fast-path decryptLegacyEncryptedConfig (no clone unless a value is enc:v1:). Removes a dynamic require + needless object clone from a per-row connection mapper.
server/sandbox/sdk-manifest.ts, tools/registry.ts Memoize enumerateSDKManifest, getAllTools, getTool over the static tool registry / method metadata. Per-sandbox-call ~545-entry walk and per-tools/list schema-flatten become Map lookups.
server/index.ts Memoize generateOpenAPISpec() by origin. Polled /openapi.json becomes a Map lookup instead of an O(tools × schema) walk.
server/server.ts Defer assertExternalDepsResolvable until after listen(); assign the env snapshot to c.env by reference instead of Object.assign per request. Lower cold-boot/readiness latency; one object-spread removed per request.
server/identity/engine.ts Batch revokeDerivationsForEvent into one = ANY(...) UPDATE. N → 1 round-trips inside the identity ingest transaction.
server/watchers/automation.ts reconcileWatcherRuns short-circuits when no active watcher run awaits a dispatched message; otherwise drives the containment join from the small side and bounds the chat_message scan to recent completions. Removes a multi-hundred-ms-to-seconds unbounded runs seq-scan that ran every 60s, forever.
server/notifications/service.ts Dedup the connections/targets fetch + service-token mint, send via Promise.allSettled, and fan bot delivery out once per createNotificationForUsers call (was once per user → N duplicate messages + N re-fetches). O(users × connections) serial HTTP → O(connections) parallel.
server/worker-api.ts, connectors/repair-agent.ts Detach the repair-agent triggers from the worker-completion ACK (fire-and-forget; the inner UPDATEs already use atomic claims); fetch loadRecentRuns lazily so the heaviest query isn't paid on every rejected failure. Repair-thread bookkeeping off the synchronous completion path.
openclaw-plugin/index.ts Memoize the last (query → recall block) so the second recall hook for a turn is a free cache hit. Cuts search_memory calls per turn in half on the latency-sensitive hook path.

Validation: make build-packages, bun run typecheck, and the affected packages' unit suites (core, agent-worker, connector-worker, embeddings, server __tests__/unit) all pass. A few tests were updated to reflect the new behavior (encryption-key cache reset hook; loadRecentRuns is now lazy; the embeddings test mock now mirrors the array-batch contract). Integration suites (Postgres-backed) were not run.

Deferred (architectural / higher-risk — needs a dedicated PR)

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 90.90909% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/core/src/logger.ts 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

Implements the low/medium-risk "TOP 3" items from the parallel
performance analyses. Behavior-preserving caching (bounded / immutable
data), batched DB + embedding work, parallelized independent awaits, and
a few hot-path micro-opts.

Areas:
- core: memoize getEncryptionKey() (per-worker-RPC / per-config-decrypt
  hot path); pass log `info` to Sentry by reference instead of spreading;
  tighten sanitizeForLogging (single compiled regex + maxDepth cap).
- agent-worker: parallelize output/ dir clearing; drop per-delta
  JSON.stringify-for-logging on the stream-delta POST path.
- connector-worker / embeddings: real array batching in
  batchGenerateLocalEmbeddings (single vectorized ONNX pass); batch
  embed_backfill embeddings instead of one round-trip per event.
- server gateway: reorder API auth middleware so the local worker-token
  check runs before the remote OIDC userinfo fetch; stop calling
  flushTracing() per inbound message (SimpleSpanProcessor already
  exports on span end); pre-lowercase the env domain allow/deny lists
  once; memoize getConfiguredPublicOrigin().
- server auth: bearer (PAT/OAuth) MCP auth now trusts the shared 60s
  membership-role cache (was hard-bypassing it every request, same as
  the cookie path).
- server stores: hoist `decrypt` to a static import + fast-path
  decryptLegacyEncryptedConfig (no clone when nothing is enc:v1:).
- server tools/sandbox: memoize enumerateSDKManifest, getAllTools, and
  getTool over the static tool registry / method metadata.
- server index: memoize generateOpenAPISpec() by origin; defer
  assertExternalDepsResolvable until after listen(); assign the env
  snapshot to c.env by reference instead of Object.assign per request.
- server identity: batch revokeDerivationsForEvent into one ANY(...)
  UPDATE inside the ingest transaction.
- server watchers: short-circuit + time-bound the unbounded `runs`
  seq-scan in reconcileWatcherRuns; skip it entirely when no active
  watcher run is awaiting a dispatched message.
- server notifications: dedup the connections/targets fetch + service
  token mint and run sends via Promise.allSettled; fan bot delivery out
  once per createNotificationForUsers call instead of once per user.
- server connectors: detach the repair-agent triggers from the
  worker-completion ACK (fire-and-forget; atomic claims make it safe);
  fetch loadRecentRuns lazily so it isn't paid on every rejected failure.
- openclaw-plugin: memoize the last (query → recall block) so the second
  recall hook for a turn is a free cache hit.
@buremba
Copy link
Copy Markdown
Member Author

buremba commented May 13, 2026

Rebased onto current main (now includes #672) and fixed integration regressions:

  • Reverted the bearer-MCP membership-cache change in workspace/multi-tenant.ts — broke mcp/member-write-access.test.ts > applies self-leave immediately (token-based MCP path needs the hard cache miss to honor immediate revocation). Moved to deferred.
  • Reverted the batched revokeDerivationsForEvent → revokeDerivationsForEvents change in identity/engine.ts — broke 4 identity/engine.test.ts cases (UC2, UC4, claim-collision dedup, multi-value namespace). Moved to deferred.
  • Also dropped the unused GatewayConfig import + 4 InvalidationEvent.resource: call sites left dangling on main by refactor: simplification sweep #672 so standalone server typecheck passes.

All other perf fixes from the PR body are unchanged.

@buremba buremba merged commit 1fbdd62 into main May 13, 2026
15 checks passed
@buremba buremba deleted the sweep/perf branch May 13, 2026 05:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants