perf: dedupe the overview poll and index the jobs rollup

Measured on the deployed console: the page shell and every module land in 39ms,
so the 1.4s was entirely /admin/api/ops/overview, fetched twice.

Twice because the sidebar and the overview view each called usePoll on the same
url, each with its own timer. usePoll now keeps one store per url, so any number
of subscribers share a single request and a single interval, and a request
already on the wire is joined rather than duplicated.

The endpoint itself was dominated by the jobs rollup: a full scan of
autonomy_jobs, 521k rows and growing about nine thousand a day. A covering index
on (job_type, lane, status, created_at) takes it from 548ms to 110ms.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnNxwxfXSbeNtjvtz5gayb
This commit is contained in:
ImBenji
2026-09-01 20:34:55 +01:00
co-authored by Claude Opus 5
parent 40f098129c
commit 9fc443579f
2 changed files with 61 additions and 30 deletions
+55 -30
View File
@@ -46,40 +46,65 @@ export async function api(path, options = {}) {
return parsed;
}
// Poll without the spinner flash: keep showing the previous payload while the next
// one is in flight, and never let a slow response overwrite a newer one.
export function usePoll(path, intervalMs = 5000) {
const [state, setState] = useState({ data: null, error: null, loading: true, at: null });
const seq = useRef(0);
const alive = useRef(true);
// One store per url, shared by every component asking for it. The sidebar and the
// overview both want /ops/overview, and without this they each opened their own
// request on their own timer -- the same payload fetched twice, forever.
const stores = new Map();
const refresh = useCallback(async () => {
const mine = ++seq.current;
try {
const data = await api(path);
if (!alive.current || mine !== seq.current) return;
setState({ data, error: null, loading: false, at: Date.now() });
} catch (error) {
if (!alive.current || mine !== seq.current) return;
console.error('[ops] poll failed for', path, error.message);
setState((prev) => ({ ...prev, error: error.message, loading: false }));
}
}, [path]);
function storeFor(path) {
if (stores.has(path)) return stores.get(path);
const store = {
state: { data: null, error: null, loading: true, at: null },
listeners: new Set(),
inflight: null,
timer: null,
emit() { for (const fn of this.listeners) fn(this.state); },
async load() {
// a request already on the wire is shared rather than duplicated
if (this.inflight) return this.inflight;
this.inflight = api(path)
.then((data) => { this.state = { data, error: null, loading: false, at: Date.now() }; })
.catch((error) => {
console.error('[ops] poll failed for', path, error.message);
this.state = { ...this.state, error: error.message, loading: false };
})
.finally(() => { this.inflight = null; this.emit(); });
return this.inflight;
},
};
stores.set(path, store);
return store;
}
export function usePoll(path, intervalMs = 5000) {
const store = storeFor(path);
const [state, setState] = useState(store.state);
useEffect(() => {
alive.current = true;
refresh();
const timer = setInterval(refresh, intervalMs);
const onVisible = () => { if (!document.hidden) refresh(); };
document.addEventListener('visibilitychange', onVisible);
return () => {
alive.current = false;
clearInterval(timer);
document.removeEventListener('visibilitychange', onVisible);
};
}, [refresh, intervalMs]);
const listener = (next) => setState(next);
store.listeners.add(listener);
setState(store.state);
store.load();
return { ...state, refresh };
// the interval belongs to the store, not the component, so N subscribers still
// produce exactly one request per tick
if (!store.timer) {
store.timer = setInterval(() => { if (!document.hidden) store.load(); }, intervalMs);
}
const onVisible = () => { if (!document.hidden) store.load(); };
document.addEventListener('visibilitychange', onVisible);
return () => {
store.listeners.delete(listener);
document.removeEventListener('visibilitychange', onVisible);
if (!store.listeners.size && store.timer) {
clearInterval(store.timer);
store.timer = null;
}
};
}, [store, intervalMs]);
return { ...state, refresh: () => store.load() };
}
export function useHashRoute(fallback) {