mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-08-21 06:13:26 +00:00
perf(ui): resync only after reconnects
The first connected status from a newly started OpenCode stream waited for initial hydration and then repeated the same session, pending-request, catalog, metadata, and filesystem loads. This made every normal startup pay for a reconnect recovery pass. Track only genuine error or unexpected-disconnect transitions as requiring recovery. Initial connecting-to-connected startup and deliberate workspace shutdown no longer resync, while failed streams still receive one coalesced authoritative recovery after reconnection. Validated with 10 targeted connection and readiness tests, the UI typecheck, 537 UI tests, and git diff checks.
This commit is contained in:
parent
cfb970dc56
commit
b2a97a84eb
3 changed files with 48 additions and 2 deletions
23
packages/ui/src/stores/connection-resync-gate.test.ts
Normal file
23
packages/ui/src/stores/connection-resync-gate.test.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import assert from "node:assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
import { ConnectionResyncGate } from "./connection-resync-gate.ts"
|
||||
|
||||
describe("connection resync gate", () => {
|
||||
it("skips initial connection and resyncs once after a stream failure", () => {
|
||||
const gate = new ConnectionResyncGate()
|
||||
assert.equal(gate.observe("instance", "connecting"), false)
|
||||
assert.equal(gate.observe("instance", "connected"), false)
|
||||
assert.equal(gate.observe("instance", "error"), false)
|
||||
assert.equal(gate.observe("instance", "connected"), true)
|
||||
assert.equal(gate.observe("instance", "connected"), false)
|
||||
})
|
||||
|
||||
it("does not resync a deliberately stopped or cleared workspace", () => {
|
||||
const gate = new ConnectionResyncGate()
|
||||
assert.equal(gate.observe("instance", "disconnected", "workspace stopped"), false)
|
||||
assert.equal(gate.observe("instance", "connected"), false)
|
||||
gate.observe("instance", "disconnected")
|
||||
gate.clear("instance")
|
||||
assert.equal(gate.observe("instance", "connected"), false)
|
||||
})
|
||||
})
|
||||
18
packages/ui/src/stores/connection-resync-gate.ts
Normal file
18
packages/ui/src/stores/connection-resync-gate.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import type { InstanceStreamStatus } from "../../../server/src/api-types"
|
||||
|
||||
export class ConnectionResyncGate {
|
||||
private readonly pending = new Set<string>()
|
||||
|
||||
observe(instanceId: string, status: InstanceStreamStatus, reason?: string): boolean {
|
||||
if (status === "error" || (status === "disconnected" && reason !== "workspace stopped")) {
|
||||
this.pending.add(instanceId)
|
||||
return false
|
||||
}
|
||||
if (status !== "connected") return false
|
||||
return this.pending.delete(instanceId)
|
||||
}
|
||||
|
||||
clear(instanceId: string): void {
|
||||
this.pending.delete(instanceId)
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import { getRootClient } from "./opencode-client"
|
|||
import { buildV2RequestLocations } from "./request-locations"
|
||||
import { fetchCommands, clearCommands } from "./commands"
|
||||
import { getInstanceRefreshTargets, type InstanceRefreshTarget } from "./instance-invalidation"
|
||||
import { ConnectionResyncGate } from "./connection-resync-gate"
|
||||
import { serverSettings } from "./preferences"
|
||||
import {
|
||||
reconcileSessionPendingState,
|
||||
|
|
@ -271,6 +272,7 @@ const connectionResyncs = new TrailingResyncCoordinator(
|
|||
log.warn("Failed to resync sessions after instance connection", { instanceId, error })
|
||||
},
|
||||
)
|
||||
const connectionResyncGate = new ConnectionResyncGate()
|
||||
|
||||
function resyncConnectedInstance(instanceId: string): void {
|
||||
void connectionResyncs.request(instanceId)
|
||||
|
|
@ -313,11 +315,13 @@ function refreshVolatileInstanceState(
|
|||
}
|
||||
|
||||
serverEvents.on("instance.eventStatus", (event) => {
|
||||
if (event.type !== "instance.eventStatus" || event.status !== "connected") return
|
||||
if (event.type !== "instance.eventStatus") return
|
||||
const shouldResync = connectionResyncGate.observe(event.instanceId, event.status, event.reason)
|
||||
if (event.status !== "connected") return
|
||||
if (disconnectedInstance()?.id === event.instanceId) {
|
||||
setDisconnectedInstance(null)
|
||||
}
|
||||
resyncConnectedInstance(event.instanceId)
|
||||
if (shouldResync) resyncConnectedInstance(event.instanceId)
|
||||
})
|
||||
|
||||
function createRestoreCreationRequestId(): string {
|
||||
|
|
@ -1064,6 +1068,7 @@ function updateInstance(id: string, updates: Partial<Instance>) {
|
|||
}
|
||||
|
||||
function removeInstance(id: string, options: { authoritative?: boolean } = {}) {
|
||||
connectionResyncGate.clear(id)
|
||||
const removedInstance = instances().get(id)
|
||||
const removedOccurrence = removedInstance
|
||||
? Array.from(instances().values())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue