perf: prevent import hanging with timeouts and batching
CI / check (push) Successful in 31s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m1s

- verifyAndFixInteractionModesCount: paginated DB queries (500/batch)
  instead of loading all items into memory at once
- withFurniDataLock: add 60s chain timeout to prevent deadlocks
  when a lock holder stalls or crashes
- withGamedataLock: same timeout protection for gamedata locks
- conversion-pool: add 60s per-job timeout, fall back to main thread
- furni/batch: abort signal + post_import progress events + skip
  post-import steps when client disconnects
- furni/batch-regen: abort signal + early exit when disconnected
- clone/sync-all: pre-fetch furnidata once per source instead of
  per item (eliminates 2000+ redundant fetches)
- sse-client: add 60s idle timeout to prevent infinite hangs
This commit is contained in:
openhands committed 2026-08-31 18:25:32 +02:00
1 parent f70d96b81c
commit 7556b1f3fa
5 files changed
+222 -93

No files matched your search

+29
View File
@@ -87,6 +87,8 @@ function pump() {
}
}
const CONVERSION_TIMEOUT_MS = 60_000;
function run(req: Omit<WorkerRequest, "id">): Promise<WorkerResponse> {
if (!ensurePool()) {
try {
@@ -118,6 +120,33 @@ function run(req: Omit<WorkerRequest, "id">): Promise<WorkerResponse> {
};
queue.push(job);
pump();
// Timeout: if a job takes too long, remove it from the queue
// and reject so the caller can fall back to main-thread conversion.
const timer = setTimeout(() => {
const idx = queue.findIndex((j) => j.req.id === job.req.id);
if (idx !== -1) {
queue.splice(idx, 1);
pending.delete(job.req.id);
reject(new Error("Conversion timed out"));
} else {
// Already dispatched to a worker — the worker is
// responsible for timing out its own task.
reject(new Error("Conversion timed out"));
}
}, CONVERSION_TIMEOUT_MS);
// Clear the timer once the job resolves/rejects
const origResolve = resolve;
const origReject = reject;
job.resolve = (r) => {
clearTimeout(timer);
origResolve(r);
};
job.reject = (e) => {
clearTimeout(timer);
origReject(e);
};
});
}