perf(desktop,host-service): enqueue workspace creation off pooled sockets and batch the host-service client
Follow-up to #6299 (terminal create-on-attach). That PR fixed the worst victim of Chromium's 6-per-origin socket pool to host-service; this one reduces the pressure at its two biggest sources. Both changes are proven with before/after CDP measurements against the real dev app.
Summary
workspaces.createno longer holds a pooled socket for the whole create. Newworkspaces.createEnqueuedvalidates, returns immediately, runs the real create in the background, and broadcasts aworkspace:create-settledeventBus event carrying what the synchronous response used to (canonical workspace id + launched terminals/agents for the pane-layout seed). The desktop renderer switches to it; CLI/MCP/SDK/automations keep the synchronouscreateuntouched.- The desktop's host-service tRPC client is batched again — with
httpBatchStreamLink, which streams each result as it completes, addressing the slowest-in-batch latency that motivated the original unbatching in #3879. - Per-case timeout bounds on the create-flow mutations (enqueue 15s, session create 30s, settled-event backstop 10min with a row-existence check), plus the persisted-row assertion in the create-on-attach test (review follow-up from #6299).
CDP evidence (real UI journeys, dev desktop app)
Create hold time — the marquee number. Same journey both times: New Workspace composer → demo-app project → prompt → submit, with all renderer→host requests timed via CDP Network events:
| request | held socket for | |
|---|---|---|
| before | POST workspaces.create | 3,710ms — the entire create |
| after | POST workspaces.createEnqueued | ≤590ms (max held across all 150 requests in the window) |
On demo-app the create is ~4s; on a monorepo it's minutes of git worktree add — all of it previously pinning 1 of 6 sockets. On relay-fronted hosts the synchronous create exceeded the relay's 30s exchange cap outright (apps/relay/src/tunnel.ts:64), so remote creates of large repos could not succeed at all; the enqueue variant fits comfortably.
End-to-end UX verified after the change: workspace appears in the sidebar instantly, navigation lands on it, the AI-generated branch name is applied, and the agent pane is seeded from the settled event and starts executing its prompt (screenshots captured). Scratch workspaces then destroyed cleanly through the same host.
Batching, dashboard route (6 pollers active), 60s windows: 72 → 66 requests/min, preflights 36 → 32, with same-tick batches visibly collapsing (terminalAgents.list,pullRequests.getByWorkspaces pairs). Honest read: the steady-state win is modest because independent poll intervals rarely share a tick — the real value is same-tick fan-outs (multi-host useQueries, boot bursts) and that it removes the per-call socket+preflight floor with no latency downside.
How It Works
- Host:
createEnqueuedreuses the entire existingcreateprocedure via a server-side caller (createCallerFactory) — zero changes to the 700-line create body. The settled event goes through a new typedEventBus.broadcastWorkspaceCreateSettled, following the existing lifecycle-event shapes. - Renderer:
useWorkspaceCreates.submitsubscribes toworkspace:create-settledfor the client-minted id before enqueueing (no missed-event race), and keeps the existing optimistic flow untouched — sidebar row, empty pane seed,failedWorkspaceCreatesrecord,workspaceTransactionspending UI all behave exactly as before, now driven by the event payload. - Degraded paths: hosts without the procedure (version skew) fall back to the legacy synchronous create; a lost settled event (host restart mid-create, WS drop) hits a 10-minute backstop that checks whether the row exists before declaring failure, so a real workspace is never torn down over a missed event.
Testing
bun run typecheck,bun run lintcd packages/host-service && bun test— 1102 pass, 0 fail, including 3 new integration tests forcreateEnqueued(immediate return + settled event with persisted row; missing-id rejection; mid-create failure broadcastsok:falsewith the error)- CDP journeys above, on the built dev app with this branch's host-service bundle
Known Limitations / Follow-ups
- The
WorkspaceCreatingStateprogress UI is still estimate-based; the settled event now provides the terminal signal, and intermediate progress events (worktree add started, agents dispatched) are a natural next step on the same channel. - Bulk create sites (
toast.promiseoverPromise.all(handles)) work unchanged — each handle'scompletednow resolves from its event — but weren't individually re-verified beyond the single-create journey. - Remaining raw-client mutations elsewhere (destroy, rename, etc.) still lack per-case bounds; incremental, per the per-case-bounds decision.
Summary by cubic
Makes workspace creation async and brings back streaming batched tRPC calls to reduce host-service socket pressure and prevent relay timeouts. Adds safer timeout recovery so slow creates and sessions don’t get marked failed.
-
New Features
- Added
workspaces.createEnqueued: validates, returns immediately, runs the real create in the background, and emitsworkspace:create-settled(canonical workspace id, terminals, agents). - Desktop switches to the enqueue flow; CLI/MCP/SDK stay synchronous. Sessions remain synchronous with a 30s timeout.
- Timeouts: enqueue 15s; session create 30s; settle backstop 10m with a bounded row-existence probe. Older hosts fall back to legacy
create. - Desktop tRPC client uses
httpBatchStreamLinkfrom@trpc/clientto batch same-tick calls and stream results, reducing preflights and socket usage. - Event bus: new
broadcastWorkspaceCreateSettled; renderer subscribes before enqueue. Types exported via@superset/workspace-client.
- Added
-
Bug Fixes
- Tri-state row probe with spaced retries for timeout recovery: avoids false “failed” creates/sessions; if only the row exists, return with an empty pane seed and rely on auto-adopt to heal panes.
- A timed-out enqueue now waits for the settled event/backstop instead of failing fast; the probe is bounded and the backstop timer is cleared once settled.
Summary by CodeRabbit
-
New Features
- Workspace creation now returns immediately while completing asynchronously in the background.
- Added completion updates for successful or failed workspace creation, including workspace, terminal, and agent details.
- Added client support for receiving workspace creation updates.
- Improved request batching while streaming individual results.
-
Bug Fixes
- Added timeout recovery and compatibility fallback for workspace creation.
-
Tests
- Expanded coverage for persisted terminal sessions and asynchronous workspace creation outcomes.