perf(code-queue): optimize first-load latency with skipTrace and caching

- Frontend: initial task list load passes skipTrace=1 to skip trace stats,
  then fetches full data in background and merges into state
- Code-queue: respect skipTrace=1 param to skip traceStatsForTasks call
- Code-queue: add 8s in-memory cache for OA trace stats (per scope ID)
- Code-queue: add 10s in-memory cache for database statistics summary

These changes reduce first-paint time by eliminating the blocking trace
stats HTTP call on initial load. Trace stats populate asynchronously
after the task list renders.
This commit is contained in:
UniDesk
2026-05-15 18:16:38 +00:00
parent 8b636e2c11
commit 354660c797
3 changed files with 69 additions and 11 deletions
+17 -4
View File
@@ -317,14 +317,14 @@ function queueRunnableTaskId(queueRows: any[], queueId: string, rows: any[]): st
async function loadTaskList(apiBaseUrl: string, queueId = allQueuesId, searchQuery = ""): Promise<any> {
return requestJson(codexApi(
apiBaseUrl,
`/api/tasks/overview?limit=${codexInitialTaskLimit}&transcriptLimit=1&compact=1&selected=0${taskListQuerySuffix(queueId, searchQuery)}`,
`/api/tasks/overview?limit=${codexInitialTaskLimit}&transcriptLimit=1&compact=1&selected=0&skipTrace=1${taskListQuerySuffix(queueId, searchQuery)}`,
), codexNoCacheOptions());
}
async function loadTaskOverview(apiBaseUrl: string, preferId: string, afterSeq = 0, queueId = allQueuesId, searchQuery = ""): Promise<any> {
async function loadTaskOverview(apiBaseUrl: string, preferId: string, afterSeq = 0, queueId = allQueuesId, searchQuery = "", skipTrace = false): Promise<any> {
return requestJson(codexApi(
apiBaseUrl,
`/api/tasks/overview?limit=${codexInitialTaskLimit}&transcriptLimit=3&compact=1&afterSeq=${encodeURIComponent(String(Math.max(0, afterSeq)))}&preferId=${encodeURIComponent(preferId)}${taskListQuerySuffix(queueId, searchQuery)}`,
`/api/tasks/overview?limit=${codexInitialTaskLimit}&transcriptLimit=3&compact=1&afterSeq=${encodeURIComponent(String(Math.max(0, afterSeq)))}&preferId=${encodeURIComponent(preferId)}${skipTrace ? "&skipTrace=1" : ""}${taskListQuerySuffix(queueId, searchQuery)}`,
), codexNoCacheOptions());
}
@@ -2528,7 +2528,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
const requestedTranscript = Array.isArray(requestedCached?.task?.transcript) ? requestedCached.task.transcript : [];
const overviewAfterSeq = transcriptResumeSeq(requestedTranscript);
let tasksResult = null;
tasksResult = await loadTaskOverview(apiBaseUrl, requestedId, overviewAfterSeq, queueFilterId);
tasksResult = await loadTaskOverview(apiBaseUrl, requestedId, overviewAfterSeq, queueFilterId, "", true);
if (token !== queueLoadTokenRef.current) {
if (trackLoad) trackedLoadInFlightRef.current = false;
return;
@@ -2608,6 +2608,19 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi
// cards are shown for historical multi-attempt sessions too.
void ensureTraceSummary(nextId, false, trackLoad ? startedAt : undefined, trackLoad ? queueMs : undefined)
.catch((err) => setError(errorText(err, "加载 Codex Trace Summary 失败")));
// Background refresh with trace stats to populate stepCount in task list
void loadTaskOverview(apiBaseUrl, requestedId, overviewAfterSeq, queueFilterId, "", false)
.then((full: any) => {
if (token !== queueLoadTokenRef.current) return;
const fullRows = taskRows(full);
if (fullRows.length > 0) {
setTasksData((previous: any) => {
const mergedRows = mergeTaskRowsPreferLatest([taskRows(previous), fullRows], activeSortId);
return { ...previous, tasks: applyLocalReadStateToRows(mergedRows) };
});
}
})
.catch(() => {});
return;
}
if (trackLoad) {