From 7a7056d72b4fc01112ec7934a7951e87b2d38dbf Mon Sep 17 00:00:00 2001 From: Bot Date: Sun, 1 Mar 2026 21:29:53 +0100 Subject: [PATCH] Perf: Paralleler Norm-Cache-Sync mit Worker-Pool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sequentieller Sync ersetzt durch parallele Worker: - Standard: 4 gleichzeitige ffmpeg-Prozesse (NORM_CONCURRENCY=4) - Konfigurierbar per NORM_CONCURRENCY env var - Sync-Zeit bei vielen Sounds 4x schneller (linear mit Parallelität) - Logging gibt Parallelität und Dauer aus Co-Authored-By: Claude Opus 4.6 --- server/src/index.ts | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/server/src/index.ts b/server/src/index.ts index b77b970..101cfbc 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -193,9 +193,14 @@ function normalizeToCache(filePath: string): Promise { }); } +// Wie viele ffmpeg-Prozesse parallel beim Cache-Sync laufen dürfen. +// Standard: 4 (gut für 4+ Kern CPUs ohne Discord-Wiedergabe zu stören). +// Über NORM_CONCURRENCY=8 o.ä. erhöhbar. +const NORM_CONCURRENCY = Math.max(1, Number(process.env.NORM_CONCURRENCY ?? 4)); + /** * Vollständige Cache-Synchronisation: - * 1. Alle Sound-Dateien normalisieren, die noch nicht im Cache sind + * 1. Alle Sound-Dateien normalisieren, die noch nicht im Cache sind (parallel) * 2. Verwaiste Cache-Dateien löschen (Sound wurde gelöscht/umbenannt) * Läuft im Hintergrund, blockiert nicht den Server. */ @@ -206,24 +211,37 @@ async function syncNormCache(): Promise { // Set aller erwarteten Cache-Keys const expectedKeys = new Set(); - let created = 0; - let skipped = 0; - let failed = 0; + const toProcess: string[] = []; for (const s of allSounds) { const fp = path.join(SOUNDS_DIR, s.relativePath); const key = normCacheKey(fp); expectedKeys.add(key); if (!fs.existsSync(fp)) continue; - if (getNormCachePath(fp)) { skipped++; continue; } // bereits gecacht & gültig - try { - await normalizeToCache(fp); - created++; - } catch (e) { - failed++; - console.warn(`Norm-cache failed: ${s.relativePath}`, e); + if (getNormCachePath(fp)) continue; // bereits gecacht & gültig + toProcess.push(fp); + } + + let created = 0; + let failed = 0; + const skipped = allSounds.length - toProcess.length; + + // Parallele Worker-Pool: NORM_CONCURRENCY ffmpeg-Prozesse gleichzeitig + const queue = [...toProcess]; + async function worker(): Promise { + while (queue.length > 0) { + const fp = queue.shift()!; + try { + await normalizeToCache(fp); + created++; + } catch (e) { + failed++; + console.warn(`Norm-cache failed: ${path.relative(SOUNDS_DIR, fp)}`, e); + } } } + const workers = Array.from({ length: Math.min(NORM_CONCURRENCY, toProcess.length || 1) }, worker); + await Promise.all(workers); // Verwaiste Cache-Dateien aufräumen let cleaned = 0; @@ -237,7 +255,7 @@ async function syncNormCache(): Promise { const dt = ((Date.now() - t0) / 1000).toFixed(1); console.log( - `Norm-Cache sync (${dt}s): ${created} neu, ${skipped} vorhanden, ${failed} fehlgeschlagen, ${cleaned} verwaist entfernt (${allSounds.length} Sounds gesamt)` + `Norm-Cache sync (${dt}s, ${NORM_CONCURRENCY} parallel): ${created} neu, ${skipped} vorhanden, ${failed} fehlgeschlagen, ${cleaned} verwaist entfernt (${allSounds.length} Sounds gesamt)` ); }