fix(web): throttle workbench session list refresh pressure (#1914)

This commit is contained in:
Lyon
2026-06-22 19:30:42 +08:00
committed by GitHub
parent 6d05f20f82
commit d871ccd7d4
+15 -6
View File
@@ -27,8 +27,9 @@ const ACTIVE_TURN_GAP_MAX_DELAY_MS = 5_000;
const SESSION_LIST_PAGE_LIMIT = 20;
const WORKBENCH_READ_HYDRATION_CONCURRENCY = 3;
const WORKBENCH_READ_FAILURE_COOLDOWN_MS = 5_000;
const SESSION_LIST_REALTIME_REFRESH_DELAY_MS = 1_200;
const SESSION_LIST_TERMINAL_REFRESH_DELAY_MS = 400;
const SESSION_LIST_REALTIME_REFRESH_DELAY_MS = 5_000;
const SESSION_LIST_TERMINAL_REFRESH_DELAY_MS = 1_500;
const SESSION_LIST_MIN_REFRESH_INTERVAL_MS = 15_000;
interface HydrateOptions {
sessionId?: string | null;
@@ -80,6 +81,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
let activeTurnGapTimer: number | null = null;
const sessionListRefreshInFlight = new Map<string, Promise<void>>();
const sessionListRefreshTimers = new Map<string, number>();
const sessionListLastRefreshAtByKey = new Map<string, number>();
let workbenchReadHydrationActive = 0;
const workbenchReadHydrationQueue: Array<() => void> = [];
const workbenchReadCooldownUntilByKey = new Map<string, number>();
@@ -239,7 +241,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
recordWorkbenchLoadingState({ scope: "session_detail", active: false, reason: "select_session", sessionId: selected.sessionId });
recordWorkbenchLoadingState({ scope: "workbench", active: false, reason: "select_session", sessionId: selected.sessionId });
error.value = null;
await refreshSessions(selected.sessionId);
await refreshSessions(selected.sessionId, { force: true });
finishWorkbenchSessionSwitchFullLoad(selected.sessionId, "ok");
return activeSessionId.value === selected.sessionId;
}
@@ -264,7 +266,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
return;
}
forgetSession(sessionId);
await refreshSessions(null);
await refreshSessions(null, { force: true });
const next = sessions.value.find((session) => !isArchivedSession(session)) ?? null;
replaceActiveSessionSelection(next?.sessionId ?? null);
currentRequest.value = null;
@@ -275,12 +277,16 @@ export const useWorkbenchStore = defineStore("workbench", () => {
restartRealtime("delete-current-session");
}
async function refreshSessions(includeSessionId: string | null = activeSessionId.value): Promise<void> {
async function refreshSessions(includeSessionId: string | null = activeSessionId.value, options: { force?: boolean } = {}): Promise<void> {
const requestIncludeSessionId = firstNonEmptyString(includeSessionId);
const requestLimit = currentSessionListLimit();
const requestKey = sessionListRefreshKey(requestIncludeSessionId, requestLimit);
const existing = sessionListRefreshInFlight.get(requestKey);
if (existing) return existing;
const now = Date.now();
const lastRefreshAt = sessionListLastRefreshAtByKey.get(requestKey) ?? 0;
if (options.force !== true && lastRefreshAt > 0 && now - lastRefreshAt < SESSION_LIST_MIN_REFRESH_INTERVAL_MS) return;
sessionListLastRefreshAtByKey.set(requestKey, now);
const refresh = refreshSessionsNow(requestIncludeSessionId, requestLimit).finally(() => {
if (sessionListRefreshInFlight.get(requestKey) === refresh) sessionListRefreshInFlight.delete(requestKey);
});
@@ -315,10 +321,13 @@ export const useWorkbenchStore = defineStore("workbench", () => {
}
const existing = sessionListRefreshTimers.get(requestKey);
if (existing) window.clearTimeout(existing);
const lastRefreshAt = sessionListLastRefreshAtByKey.get(requestKey) ?? 0;
const cooldownMs = lastRefreshAt > 0 ? Math.max(0, SESSION_LIST_MIN_REFRESH_INTERVAL_MS - (Date.now() - lastRefreshAt)) : 0;
const boundedDelayMs = Math.max(0, Math.trunc(delayMs), cooldownMs);
const timer = window.setTimeout(() => {
sessionListRefreshTimers.delete(requestKey);
void refreshSessions(requestIncludeSessionId);
}, Math.max(0, Math.trunc(delayMs)));
}, boundedDelayMs);
sessionListRefreshTimers.set(requestKey, timer);
}