From c2e61dc324b671b12354a49e5c45723f6e0d020e Mon Sep 17 00:00:00 2001 From: openhands Date: Sun, 23 Aug 2026 19:51:04 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20reduce=20import=20concurrency=20to=20pre?= =?UTF-8?q?vent=20100%=20CPU=20during=20SWF=E2=86=92Nitro=20conversion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Default batch concurrency: 3→1 (max 5→3) - Default batch-regen concurrency: 3→1 (max 5→3) - Default IMPORT_BATCH_CONCURRENCY: 12→3 - Added setImmediate yields between items to prevent event loop blocking Fixes gatje and other complex furniture imports consuming 100% CPU and stalling the catalog studio. --- src/app/api/admin/import/furni/batch-regen/route.ts | 3 ++- src/app/api/admin/import/furni/batch/route.ts | 3 ++- src/app/api/admin/import/furni/route.ts | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/api/admin/import/furni/batch-regen/route.ts b/src/app/api/admin/import/furni/batch-regen/route.ts index 9c69bd37..12e122e0 100644 --- a/src/app/api/admin/import/furni/batch-regen/route.ts +++ b/src/app/api/admin/import/furni/batch-regen/route.ts @@ -39,7 +39,7 @@ export const POST = withAdmin( async (request) => { const body = await request.json(); const items: RegenItem[] = body.items || []; - const concurrency = Math.min(Math.max(body.concurrency || 3, 1), 5); + const concurrency = Math.min(Math.max(body.concurrency || 1, 1), 3); if (items.length === 0) return apiError("No items to regenerate", 400); @@ -237,6 +237,7 @@ export const POST = withAdmin( }); await Promise.allSettled(promises); + await new Promise((r) => setImmediate(r)); } // Batch write FurnitureData.json diff --git a/src/app/api/admin/import/furni/batch/route.ts b/src/app/api/admin/import/furni/batch/route.ts index a4286ff5..73106117 100644 --- a/src/app/api/admin/import/furni/batch/route.ts +++ b/src/app/api/admin/import/furni/batch/route.ts @@ -42,7 +42,7 @@ export const POST = withAdmin( async (request, ctx) => { const body = await request.json(); const rawItems: BatchItem[] = body.items || []; - const concurrency = Math.min(Math.max(body.concurrency || 3, 1), 5); + const concurrency = Math.min(Math.max(body.concurrency || 1, 1), 3); const sourceId: string | undefined = body.sourceId; if (rawItems.length === 0) return apiError("No items to import", 400); @@ -179,6 +179,7 @@ export const POST = withAdmin( }); await Promise.allSettled(promises); + await new Promise((r) => setImmediate(r)); } // Batch write FurnitureData.json once at the end — BEFORE RCON so the diff --git a/src/app/api/admin/import/furni/route.ts b/src/app/api/admin/import/furni/route.ts index 4cbbc0e7..ca4fde4f 100644 --- a/src/app/api/admin/import/furni/route.ts +++ b/src/app/api/admin/import/furni/route.ts @@ -48,7 +48,7 @@ import type { ImportSingleResult } from "@/types/furni"; // How many items to import concurrently in a batch. The per-item work is // network-bound (asset downloads) and DB-bound, so a modest pool gives a large // speedup over the old fully sequential loop without overwhelming the DB. -const IMPORT_BATCH_CONCURRENCY = 12; +const IMPORT_BATCH_CONCURRENCY = 3; async function runWithConcurrency( items: T[], @@ -64,6 +64,7 @@ async function runWithConcurrency( const i = cursor++; if (i >= items.length) break; out[i] = await fn(items[i]); + await new Promise((r) => setImmediate(r)); } }, ); @@ -849,6 +850,7 @@ export const PATCH = withAdmin( warning: (err as Error).message, }); } + await new Promise((r) => setImmediate(r)); } const succeeded = results.filter((r) => r.ok).length;