perf: offload per-import localized patch to worker + cache FurnitureData parse
CI / check (push) Successful in 41s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m0s

Per single furni import, patchLocalizedFurniDataEntries ran for every
language and did a full 100k-entry JSON.parse + scan + JSON.stringify on the
main thread. That CPU spike is now offloaded to the translation worker
(init/prepare/finalize reuses the same pure core helpers), so importing
never blocks the event loop. The main thread only does async file I/O and the
network LibreTranslate calls. Falls back to the same helpers on the main
thread if no worker is available.

Also cache readFurniData() by file mtime+size so the 100k-entry JSON is
parsed once per change instead of on every import/fix/reconcile read
(invalidated on write).
This commit is contained in:
openhands committed 2026-08-24 19:29:21 +02:00
1 parent 6ed1b03e24
commit 6ae7c09682
5 files changed
+502 -226

No files matched your search

+28 -1
View File
@@ -199,10 +199,34 @@ export async function withFurniDataLock<T>(fn: () => Promise<T>): Promise<T> {
}
}
let cachedFurniData: {
path: string;
mtimeMs: number;
size: number;
data: Record<string, unknown>;
} | null = null;
export async function readFurniData(): Promise<Record<string, unknown>> {
const target = await getFurnitureDataReadPath();
const stat = await fs.stat(target);
if (
cachedFurniData &&
cachedFurniData.path === target &&
cachedFurniData.mtimeMs === stat.mtimeMs &&
cachedFurniData.size === stat.size
) {
// File unchanged since last parse: skip the (expensive) full JSON.parse.
return cachedFurniData.data;
}
const raw = await fs.readFile(/*turbopackIgnore: true*/ target, "utf-8");
return JSON.parse(raw);
const data = JSON.parse(raw) as Record<string, unknown>;
cachedFurniData = {
path: target,
mtimeMs: stat.mtimeMs,
size: stat.size,
data,
};
return data;
}
export async function writeFurniData(
@@ -216,6 +240,9 @@ export async function writeFurniData(
await fs.writeFile(/*turbopackIgnore: true*/ tmp, payload, "utf-8");
await fs.rename(/*turbopackIgnore: true*/ tmp, target);
}
// The file just changed: force the next read to reparse instead of serving
// a possibly-mutated cached object.
cachedFurniData = null;
}
export function buildFurniEntry(params: {