Skip to content

Avoid redundant cloning on fresh session store loads#58965

Closed
efww wants to merge 4 commits intoopenclaw:mainfrom
efww:perf/skipcache-no-clone
Closed

Avoid redundant cloning on fresh session store loads#58965
efww wants to merge 4 commits intoopenclaw:mainfrom
efww:perf/skipcache-no-clone

Conversation

@efww
Copy link
Copy Markdown

@efww efww commented Apr 1, 2026

Summary

loadSessionStore(..., { skipCache: true }) already forces a fresh disk read, but it still returns structuredClone(store) afterward.

On this path, the parsed store is not shared with the cache, so the extra clone is unnecessary. This change returns the parsed store directly when skipCache is true, while preserving the defensive clone behavior for normal cached reads.

Why

Session store update paths currently re-read the full store from disk with skipCache: true, mutate one entry, and then write the full JSON back out. The full rewrite is still the dominant cost, but the extra clone adds avoidable O(n) overhead on the read side of that hot path.

Benchmarks

Median local microbench results:

  • loadSessionStore(skipCache)

    • 1,000 entries: 2.735ms -> 1.502ms (-45%)
    • 5,000 entries: 17.209ms -> 6.561ms (-62%)
    • 10,000 entries: 35.020ms -> 13.652ms (-61%)
  • single-entry update path

    • 5,000 entries: 41.628ms -> 35.904ms (-14%)
    • 10,000 entries: 76.008ms -> 63.889ms (-16%)

Tests

  • pnpm test -- src/config/sessions.test.ts
  • pnpm test -- src/commands/agent/session-store.test.ts
  • pnpm test -- src/gateway/server-chat.agent-events.test.ts

Also added a regression test covering the skipCache path to ensure the returned mutable object does not taint cached reads.

Note

I also re-ran src/gateway/openresponses-http.test.ts and reproduced two existing failures on origin/main. I am tracking that separately in #58964.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 1, 2026

Greptile Summary

This PR eliminates a redundant structuredClone on the skipCache: true path of loadSessionStore. When callers pass skipCache: true (e.g., updateSessionStore, updateSessionStoreEntry) they force a fresh disk read and intend to mutate the result before saving — cloning was wasteful because the parsed store object is never placed into the shared object cache on that code path.

  • src/config/sessions/store.ts: Adds an early return of the raw parsed store when opts.skipCache is true, bypassing the defensive structuredClone that was protecting callers only from a shared cache reference (which doesn't exist on this path).
  • src/config/sessions/store-cache.ts (read for context): writeSessionStoreCache already clones internally via structuredClone(params.store), and it is skipped entirely when skipCache: true, confirming the cache is never aliased to the returned object.
  • src/config/sessions.test.ts: Adds a regression test verifying that mutating the value returned from skipCache: true does not taint subsequent cached or uncached reads.

Confidence Score: 5/5

Safe to merge — the optimization is logically sound, no shared object aliases are possible on the skipCache path, and the new regression test covers the key invariant.

The invariant is solid: writeSessionStoreCache (which is the only way a store object enters the shared cache) is explicitly skipped when opts.skipCache is true. The freshly-parsed store is therefore never aliased into the cache, so returning it directly cannot taint any subsequent reads. Both primary call-sites (updateSessionStore, updateSessionStoreEntry) hold a write lock, mutate the returned store intentionally, and then save — exactly the pattern the optimization is designed for. No P1 or P0 issues found.

No files require special attention.

Reviews (1): Last reviewed commit: "Avoid redundant cloning on fresh session..." | Re-trigger Greptile

@efww
Copy link
Copy Markdown
Author

efww commented Apr 3, 2026

The remaining CI failures are pre-existing issues unrelated to this PR:

  • extension-fast (whatsapp) and checks-node-channels-3: same test failing — extensions/whatsapp/src/action-runtime.test.ts > applies default account allowFrom when accountId is omitted (ToolAuthorizationError assertion mismatch). This PR only touches src/config/sessions/.
  • macos-swift: Swift formatting lint failure on a file this PR does not modify.

The original checks-windows-node-test-5 timeout was also a pre-existing flaky runner issue — it passed in 4m51s on the rerun today.

@openclaw-barnacle openclaw-barnacle bot added the agents Agent runtime and tooling label Apr 3, 2026
@efww efww force-pushed the perf/skipcache-no-clone branch from e27fb48 to d2848c1 Compare April 3, 2026 15:25
@openclaw-barnacle openclaw-barnacle bot added channel: telegram Channel integration: telegram channel: whatsapp-web Channel integration: whatsapp-web size: S and removed size: XS labels Apr 3, 2026
@efww efww force-pushed the perf/skipcache-no-clone branch from 005aba8 to e64d577 Compare April 3, 2026 16:02
@openclaw-barnacle openclaw-barnacle bot added size: XS and removed channel: telegram Channel integration: telegram size: S labels Apr 3, 2026
Copy link
Copy Markdown
Author

efww commented Apr 4, 2026

Superseded by #60595. Closing this older broader variant in favor of the latest clean 2-file session-store PR on top of the latest main.

@efww efww closed this Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: whatsapp-web Channel integration: whatsapp-web size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant