perf: offload per-import localized patch to worker + cache FurnitureData parse
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:
1 parent
6ed1b03e24
commit
6ae7c09682
5 files changed
+502
-226
No files matched your search
@@ -3,8 +3,12 @@ import type { HabboGamedataHotel } from "@/lib/habbo-gamedata-hotel";
|
||||
import {
|
||||
type FurniDataMaster,
|
||||
finalizeLangFile,
|
||||
finalizePatch,
|
||||
type PatchEntry,
|
||||
type PreparedLang,
|
||||
type PreparedPatch,
|
||||
prepareLangFile,
|
||||
preparePatch,
|
||||
type Translation,
|
||||
} from "./furni-data-i18n-core";
|
||||
|
||||
@@ -26,19 +30,40 @@ interface FinalizeMsg {
|
||||
lang: string;
|
||||
translations: Map<string, string>;
|
||||
}
|
||||
type Msg = InitMsg | PrepareMsg | FinalizeMsg;
|
||||
interface PatchPrepareMsg {
|
||||
id: number;
|
||||
type: "patchPrepare";
|
||||
lang: string;
|
||||
hotel: string;
|
||||
fileContent: string | null;
|
||||
entries: PatchEntry[];
|
||||
}
|
||||
interface PatchFinalizeMsg {
|
||||
id: number;
|
||||
type: "patchFinalize";
|
||||
lang: string;
|
||||
translations: Map<string, string>;
|
||||
}
|
||||
type Msg =
|
||||
| InitMsg
|
||||
| PrepareMsg
|
||||
| FinalizeMsg
|
||||
| PatchPrepareMsg
|
||||
| PatchFinalizeMsg;
|
||||
|
||||
let master: FurniDataMaster | null = null;
|
||||
let hotelMaps: Record<string, Map<string, Translation>> = {};
|
||||
const prepared = new Map<string, PreparedLang>();
|
||||
const preparedPatches = new Map<string, PreparedPatch>();
|
||||
|
||||
if (parentPort) {
|
||||
parentPort.on("message", (msg: Msg) => {
|
||||
const port = parentPort;
|
||||
port.on("message", (msg: Msg) => {
|
||||
try {
|
||||
if (msg.type === "init") {
|
||||
master = JSON.parse(msg.masterJson);
|
||||
hotelMaps = msg.hotelMaps ?? {};
|
||||
parentPort!.postMessage({ id: msg.id, ok: true });
|
||||
port.postMessage({ id: msg.id, ok: true });
|
||||
return;
|
||||
}
|
||||
if (msg.type === "prepare") {
|
||||
@@ -52,21 +77,49 @@ if (parentPort) {
|
||||
officialMap,
|
||||
);
|
||||
prepared.set(msg.lang, r);
|
||||
parentPort!.postMessage({
|
||||
port.postMessage({
|
||||
id: msg.id,
|
||||
ok: true,
|
||||
pendingTexts: r.pendingTexts,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (msg.type === "patchPrepare") {
|
||||
if (!master) throw new Error("translation worker not initialized");
|
||||
const officialMap =
|
||||
hotelMaps[msg.hotel] ?? new Map<string, Translation>();
|
||||
const r = preparePatch(
|
||||
msg.fileContent,
|
||||
master,
|
||||
msg.lang,
|
||||
msg.hotel as HabboGamedataHotel,
|
||||
officialMap,
|
||||
msg.entries,
|
||||
);
|
||||
preparedPatches.set(msg.lang, r);
|
||||
port.postMessage({
|
||||
id: msg.id,
|
||||
ok: true,
|
||||
pendingTexts: [...r.pendingTexts],
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (msg.type === "patchFinalize") {
|
||||
const r = preparedPatches.get(msg.lang);
|
||||
if (!r) throw new Error(`no prepared patch for ${msg.lang}`);
|
||||
const res = finalizePatch(r, msg.translations);
|
||||
preparedPatches.delete(msg.lang);
|
||||
port.postMessage({ id: msg.id, ok: true, ...res });
|
||||
return;
|
||||
}
|
||||
// finalize
|
||||
const r = prepared.get(msg.lang);
|
||||
if (!r) throw new Error(`no prepared data for ${msg.lang}`);
|
||||
const res = finalizeLangFile(r.cloned, r.pendingRefs, msg.translations);
|
||||
prepared.delete(msg.lang);
|
||||
parentPort!.postMessage({ id: msg.id, ok: true, ...res });
|
||||
port.postMessage({ id: msg.id, ok: true, ...res });
|
||||
} catch (err) {
|
||||
parentPort!.postMessage({
|
||||
port.postMessage({
|
||||
id: msg.id,
|
||||
ok: false,
|
||||
error: (err as Error).message,
|
||||
|
||||
Reference in new issue
Block a user