fix: throttle workbench history hydration

This commit is contained in:
lyon
2026-06-20 01:34:34 +08:00
parent 2463334760
commit 02a4eb3ccb
+37 -5
View File
@@ -22,6 +22,7 @@ const TRACE_HYDRATION_RETRY_DELAY_MS = 700;
const ACTIVE_TURN_GAP_DELAY_MS = 1_000;
const ACTIVE_TURN_GAP_MAX_ATTEMPTS = 12;
const SESSION_LIST_PAGE_LIMIT = 20;
const WORKBENCH_READ_HYDRATION_CONCURRENCY = 3;
interface HydrateOptions {
sessionId?: string | null;
@@ -69,6 +70,8 @@ export const useWorkbenchStore = defineStore("workbench", () => {
let realtimeGapTimer: number | null = null;
let activeTurnGapTimer: number | null = null;
const sessionListRefreshInFlight = new Map<string, Promise<void>>();
let workbenchReadHydrationActive = 0;
const workbenchReadHydrationQueue: Array<() => void> = [];
const activeSession = computed(() => selectActiveSession(serverState.value, explicitSessionId.value));
const activeSessionSelectionSource = computed(() => activeSelectionSource.value);
@@ -370,6 +373,35 @@ export const useWorkbenchStore = defineStore("workbench", () => {
void source;
}
function runWorkbenchReadHydration<T>(task: () => Promise<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
const run = () => {
workbenchReadHydrationActive += 1;
Promise.resolve()
.then(task)
.then(resolve, reject)
.finally(() => {
workbenchReadHydrationActive = Math.max(0, workbenchReadHydrationActive - 1);
const next = workbenchReadHydrationQueue.shift();
if (next) next();
});
};
if (workbenchReadHydrationActive < WORKBENCH_READ_HYDRATION_CONCURRENCY) {
run();
return;
}
workbenchReadHydrationQueue.push(run);
});
}
function fetchWorkbenchTurnStatus(traceId: string): Promise<ApiResult<AgentChatResultResponse>> {
return runWorkbenchReadHydration(() => api.workbench.turn(traceId, 8000, () => activityRef.value));
}
function fetchWorkbenchTraceEvents(traceId: string, sinceSeq: number): Promise<ApiResult<AgentChatResultResponse>> {
return runWorkbenchReadHydration(() => api.workbench.traceEvents(traceId, codeAgentTimeoutMs.value, () => activityRef.value, { sinceSeq, limit: TRACE_HYDRATION_PAGE_LIMIT }));
}
async function hydrateTurnStatusAuthority(source: ChatMessage[] = messages.value): Promise<void> {
await Promise.all(uniqueTraceIds(source).slice(-12).map((traceId) => refreshTurnStatusByTraceId(traceId)));
}
@@ -377,7 +409,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
async function refreshTurnStatusByTraceId(traceId: string | null | undefined): Promise<void> {
const id = firstNonEmptyString(traceId);
if (!id) return;
const response = await api.workbench.turn(id, 8000, () => activityRef.value);
const response = await fetchWorkbenchTurnStatus(id);
if (response.ok && response.data) {
applyTurnStatusSnapshot(id, response.data);
if (response.data.terminal === true || isTerminalMessageStatus(response.data.status)) completeTrace(id, response.data);
@@ -556,7 +588,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
async function fetchTraceHydrationPage(traceId: string, sinceSeq: number): Promise<ApiResult<AgentChatResultResponse>> {
let lastResult: ApiResult<AgentChatResultResponse> | null = null;
for (let attempt = 0; attempt < TRACE_HYDRATION_MAX_ATTEMPTS; attempt += 1) {
const result = await api.workbench.traceEvents(traceId, codeAgentTimeoutMs.value, () => activityRef.value, { sinceSeq, limit: TRACE_HYDRATION_PAGE_LIMIT });
const result = await fetchWorkbenchTraceEvents(traceId, sinceSeq);
if (result.ok && result.data) return result;
lastResult = result;
if (attempt < TRACE_HYDRATION_MAX_ATTEMPTS - 1) await delayTraceHydrationRetry(TRACE_HYDRATION_RETRY_DELAY_MS * (attempt + 1));
@@ -685,7 +717,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
}
async function validateAndReattachTrace(traceId: string): Promise<void> {
const result = await api.workbench.turn(traceId, 8000, () => activityRef.value);
const result = await fetchWorkbenchTurnStatus(traceId);
if (!result.ok || !result.data) {
await clearActiveTrace(traceId, result.status === 404 ? "reattach-result-not-found" : "reattach-result-unavailable");
return;
@@ -828,7 +860,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
if (terminalRealtimeRefreshInFlight.has(traceId)) return;
terminalRealtimeRefreshInFlight.add(traceId);
try {
const result = await api.workbench.turn(traceId, 8000, () => activityRef.value);
const result = await fetchWorkbenchTurnStatus(traceId);
if (!result.ok || !result.data) return;
applyTurnStatusSnapshot(traceId, result.data);
if (result.data.terminal === true || isTerminalMessageStatus(result.data.status)) completeTrace(traceId, result.data);
@@ -967,7 +999,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
await Promise.all(targets.map(async (message) => {
const traceId = firstNonEmptyString(message.traceId, message.runnerTrace?.traceId);
if (!traceId) return;
const result = await api.workbench.turn(traceId, 8000, () => activityRef.value);
const result = await fetchWorkbenchTurnStatus(traceId);
if (!result.ok || !result.data) return;
applyTurnStatusSnapshot(traceId, result.data);
}));