From d57a4d993d6810147ab0b9b89caafdd9549ba9cf Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Fri, 27 Mar 2026 06:20:26 +0000 Subject: [PATCH] studio: fix chat CPU spike (#4632) Inline querier identity changed every render, forcing useLiveQuery to resubscribe continuously causing CPU spikes. Store querier in a ref and only re-subscribe when explicit deps change. --- studio/frontend/src/features/chat/db.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/studio/frontend/src/features/chat/db.ts b/studio/frontend/src/features/chat/db.ts index 9d530b96df..f1f83e4b2f 100644 --- a/studio/frontend/src/features/chat/db.ts +++ b/studio/frontend/src/features/chat/db.ts @@ -2,7 +2,7 @@ // Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 import Dexie, { type EntityTable, liveQuery } from "dexie"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import type { MessageRecord, ThreadRecord } from "./types"; const db = new Dexie("unsloth-chat") as Dexie & { @@ -38,18 +38,29 @@ db.version(3) export { db }; +/** + * Wraps Dexie liveQuery for React state updates. + * + * Important: include every semantic query input in `deps` (filters, sort keys, + * IDs, etc). `querier` identity is intentionally ignored to avoid re-subscribing + * on every render when callers pass inline functions. + */ export function useLiveQuery( querier: () => Promise, deps: unknown[] = [], ): T | undefined { const [value, setValue] = useState(); + const querierRef = useRef(querier); + querierRef.current = querier; + useEffect(() => { - const sub = liveQuery(querier).subscribe({ + const sub = liveQuery(() => querierRef.current()).subscribe({ next: setValue, error: (err) => console.error("useLiveQuery:", err), }); return () => sub.unsubscribe(); + // Intentionally omit `querier` from deps: inline functions would re-subscribe every render. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [querier, ...deps]); + }, deps); return value; }