refactor(opencode): replace reconcile() with path-syntax setStore and delta coalescing for streaming updates#15309
Conversation
…g updates Replace all reconcile() calls with direct assignment and path-syntax setStore() to eliminate O(n) deep-diff overhead during streaming. - Hot path: reconcile(info) → direct assignment for message/part/session updates - Hot path: produce() delta flush → setStore path-syntax (O(depth) vs O(n)) - Bootstrap: remove reconcile() from all one-shot API response stores - Remove unused reconcile import from solid-js/store Proven from Solid.js source: reconcile() recursively traverses the entire object tree (applyState in modifiers.ts), while path-syntax navigates directly to the leaf node via updatePath (store.ts).
…type safety - Restore reconcile() for 6 dictionary/object stores (provider_default, config, mcp, mcp_resource, session_status, provider_auth) to properly remove stale keys on re-bootstrap — setStore shallow-merges objects - Replace 'as any' with 'as keyof Part' and add runtime type guard for delta handler to satisfy type safety requirements - Keep path-syntax setStore for streaming hot path and array stores
…per-token setStore calls Accumulate message.part.delta events in a plain Record buffer (zero reactive overhead), then flush to the store via a single batched setStore() call per queueMicrotask. This collapses N deltas per part per SDK flush into 1 setStore() call, reducing proxy trap invocations from 5N to 5 per flush cycle. - Add pending delta accumulator (plain Record, no proxies) - Schedule flushDeltas() via queueMicrotask after sdk.tsx batch completes - Clear stale pending deltas on message.part.updated/removed - No store shape changes, no hook changes, no new timers
|
The following comment was made by an LLM, it may be inaccurate: Based on my search results, I found one potentially related PR: Related PR FoundPR #13026: fix(opencode): coalesce stream updates and add reconnect backoff Why it's related: This PR also addresses stream update coalescing, which is a core optimization technique in the current PR. Both PRs aim to improve performance by batching streaming updates rather than processing them individually. However, this appears to be a previous fix that may have been superseded or is being refined by the current PR #15309, which takes a more comprehensive approach by also refactoring the reconcile/setStore pattern and adding delta coalescing via queueMicrotask. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
… segfault on Ctrl+C
Issue for this PR
Closes #15311
Related upstream issues
Type of change
What does this PR do?
Reduces reactive store overhead on the hot streaming path in
sync.tsxby ~10x through two changes:1. Path-syntax
setStore()instead ofreconcile()on the hot pathreconcile()does an O(n) tree walk to diff old vs new state. For single-value updates where we already know the exact key and index, path-syntaxsetStore("part", messageID, index, "content", updater)is O(depth) ≈ O(1).reconcile()is kept for the 6 dict-style stores (provider,thread,part,message,todo,session_diff) where keys can be added or removed and the diff is genuinely needed.2. Delta coalescing via
queueMicrotaskPreviously, each
message.part.deltaevent calledsetStore()individually. With 10 deltas in a batch, that's 10setStore()calls → 50 proxy traps → 10 notification passes. Now, deltas accumulate in a plainRecord(zero reactive overhead per token), and a singlequeueMicrotask(() => batch(() => flushDeltas()))applies them all in one pass:Stale buffered deltas are cleaned up on
message.part.updatedandmessage.part.removedevents.This PR addresses the application-layer trigger for the GC contention tracked in the Bun/WebKit issues above. Even after the upstream bmalloc fix lands, this coalescing is the correct architecture — streaming deltas should never trigger per-token reactive overhead.
How did you verify your code works?
turbo typecheckpasses across all 18 packagesturbo buildsucceedsreconcile()is preserved for dict stores where it's needed (keys added/removed)Screenshots / recordings
N/A — not a UI change.
Checklist