Skip to content

Commit d439b61

Browse files
committed
Revert "perf(showcase-dashboard): yield during fetch + parallelize initial pages (CopilotKit#4504)"
This reverts commit d17ea91, reversing changes made to a0770ea.
1 parent d17ea91 commit d439b61

1 file changed

Lines changed: 37 additions & 74 deletions

File tree

showcase/shell-dashboard/src/hooks/useLiveStatus.ts

Lines changed: 37 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use client";
2-
import { startTransition, useEffect, useState } from "react";
2+
import { useEffect, useState } from "react";
33
import { pb, pbIsMisconfigured, PB_MISCONFIG_MESSAGE } from "../lib/pb";
44
import type { StatusRow } from "../lib/live-status";
55
import { upsertByKey } from "../lib/live-status";
@@ -144,34 +144,27 @@ export function useLiveStatus(dimension?: string): UseLiveStatusResult {
144144
if (!alive || pendingByKey.size === 0) return;
145145
const ops = Array.from(pendingByKey);
146146
pendingByKey.clear();
147-
// SSE deltas drive a non-urgent visual update — flag the commit as a
148-
// transition so React 19 can yield to user input (scroll, click,
149-
// keyboard) while it walks the matrix tree. Without this, a large
150-
// burst still renders synchronously and noticeably stalls the page
151-
// even though the burst was coalesced into a single setRows call.
152-
startTransition(() => {
153-
setRows((prev) => {
154-
let next = prev;
155-
let mutated = false;
156-
for (const [key, op] of ops) {
157-
if (op.op === "delete") {
158-
const idx = next.findIndex((r) => r.key === key);
159-
if (idx === -1) continue;
160-
if (!mutated) {
161-
next = next.slice();
162-
mutated = true;
163-
}
164-
next.splice(idx, 1);
165-
} else {
166-
const candidate = upsertByKey(next, op.row);
167-
if (candidate !== next) {
168-
next = candidate;
169-
mutated = true;
170-
}
147+
setRows((prev) => {
148+
let next = prev;
149+
let mutated = false;
150+
for (const [key, op] of ops) {
151+
if (op.op === "delete") {
152+
const idx = next.findIndex((r) => r.key === key);
153+
if (idx === -1) continue;
154+
if (!mutated) {
155+
next = next.slice();
156+
mutated = true;
157+
}
158+
next.splice(idx, 1);
159+
} else {
160+
const candidate = upsertByKey(next, op.row);
161+
if (candidate !== next) {
162+
next = candidate;
163+
mutated = true;
171164
}
172165
}
173-
return mutated ? next : prev;
174-
});
166+
}
167+
return mutated ? next : prev;
175168
});
176169
}
177170

@@ -239,60 +232,30 @@ export function useLiveStatus(dimension?: string): UseLiveStatusResult {
239232
}
240233

241234
async function fetchInitial(): Promise<StatusRow[]> {
242-
// Pull page 1 sequentially so we learn `totalItems` before deciding
243-
// how many additional pages to issue. After that, the remaining
244-
// pages are independent reads from the same collection — fire them
245-
// in parallel so wall-clock latency tracks the slowest page rather
246-
// than the sum. With 2000-row datasets paginated at 200/page that
247-
// turns ~10 sequential round-trips into one round-trip per slot of
248-
// network parallelism, which is the difference between a
249-
// multi-second loading hitch and a single network frame.
250-
const firstResp = await pb
251-
.collection("status")
252-
.getList<StatusRow>(1, INITIAL_PAGE_SIZE, { filter });
253-
if (!firstResp.items || firstResp.items.length === 0) return [];
254-
255-
const total = Math.min(firstResp.totalItems, INITIAL_CAP);
256-
if (firstResp.items.length >= total) {
257-
return firstResp.items.slice(0, total);
258-
}
259-
260-
const lastPage = Math.ceil(total / INITIAL_PAGE_SIZE);
261-
const pageRequests: Promise<StatusRow[]>[] = [];
262-
for (let p = 2; p <= lastPage; p++) {
263-
const perPage = Math.min(
264-
INITIAL_PAGE_SIZE,
265-
total - (p - 1) * INITIAL_PAGE_SIZE,
266-
);
267-
pageRequests.push(
268-
pb
269-
.collection("status")
270-
.getList<StatusRow>(p, perPage, { filter })
271-
.then((resp) => resp.items ?? []),
272-
);
273-
}
274-
const restPages = await Promise.all(pageRequests);
275-
const collected = firstResp.items.slice();
276-
for (const items of restPages) {
277-
collected.push(...items);
278-
if (collected.length >= total) break;
235+
// Paginated fetch with a hard total cap. `getFullList({batch})`
236+
// would keep pulling every page of matching rows; we instead loop
237+
// `getList` and break once we hit INITIAL_CAP.
238+
const collected: StatusRow[] = [];
239+
let page = 1;
240+
while (collected.length < INITIAL_CAP) {
241+
const remaining = INITIAL_CAP - collected.length;
242+
const perPage = Math.min(INITIAL_PAGE_SIZE, remaining);
243+
const resp = await pb
244+
.collection("status")
245+
.getList<StatusRow>(page, perPage, { filter });
246+
if (!resp.items || resp.items.length === 0) break;
247+
collected.push(...resp.items);
248+
if (collected.length >= resp.totalItems) break;
249+
page += 1;
279250
}
280-
return collected.length > total ? collected.slice(0, total) : collected;
251+
return collected;
281252
}
282253

283254
async function connect(): Promise<void> {
284255
try {
285256
const initial = await fetchInitial();
286257
if (!alive) return;
287-
// The first time real data lands, every cell in the matrix has to
288-
// re-render (empty map → populated map invalidates per-key memo
289-
// checks). That commit is a hundreds-of-cells walk; flag it as a
290-
// transition so React can interleave user input. `setStatus`
291-
// remains urgent so the "connecting → live" indicator flips
292-
// immediately, before the heavy commit lands.
293-
startTransition(() => {
294-
setRows(initial);
295-
});
258+
setRows(initial);
296259
setStatus("live");
297260
setError(null);
298261
// Reset the reconnect counter on a SUCCESSFUL connection. This is

0 commit comments

Comments
 (0)