DLC-Import Hang gefixt: kein sync-FS Log-I/O mehr pro Link

Symptom: Nutzer zieht DLC mit vielen Paketen rein, App haengt 1-2 min.

Ursache: addPackages() rief logPackageForItem() pro Link auf. Jede
dieser Calls triggerte ~10 synchrone FS-Operationen:
  - ensurePackageLog: mkdirSync + existsSync
  - ensureItemLog: mkdirSync + existsSync + writeFileSync (first-time)
    + 2× appendFileSync (first-time header)
  - logPackage + writeItemLogEvent (appendFileSync, batched)

Bei einer DLC mit 60 Paketen × 25 Links = 1500 Items → ~9.000-15.000
sync FS-Calls. Auf langsamen Disks / Netzwerk-Shares: 60-120 Sekunden
Event-Loop-Blockade. UI eingefroren.

Fix: per-Item-Logs waehrend Bulk-Add nicht mehr initialisieren. Sie
werden lazy beim ersten echten Lifecycle-Event (Download-Start, Fehler)
angelegt. Stattdessen EINE zusammengefasste "Links registriert (N)"
Zeile ins Package-Log pro Paket — bei >50 Links mit gekuerzter
Vorschau (erste 20 + "+N more") damit die Log-Zeile nicht riesig wird.

Neuer Test "bulk-adds large DLC containers without initializing per-item
logs" verifiziert: 1500 Items werden in <5s hinzugefuegt (lokal unter
300ms), keine Item-Log-Dateien entstehen, pro Paket existiert genau ein
Package-Log.
This commit is contained in:
Sucukdeluxe
2026-04-21 21:36:19 +02:00
parent 56656dfef9
commit 75036edbd1
2 changed files with 83 additions and 7 deletions
+22 -7
View File
@@ -2746,6 +2746,11 @@ export class DownloadManager extends EventEmitter {
linkCount: links.length
});
// Collect per-link summary for ONE batched package-log entry after the
// loop, instead of ~10 sync FS calls per link. A DLC with many packages
// was freezing the UI for 1-2 minutes because logPackageForItem called
// ensurePackageLog + ensureItemLog + appendFileSync×multiple per link.
const registeredLinkSummary: string[] = [];
for (let linkIdx = 0; linkIdx < links.length; linkIdx += 1) {
const link = links[linkIdx];
const itemId = uuidv4();
@@ -2772,13 +2777,7 @@ export class DownloadManager extends EventEmitter {
updatedAt: nowMs()
};
this.assignItemTargetPath(item, path.join(outputDir, fileName));
this.logPackageForItem(item, "INFO", "Link registriert", {
index: linkIdx + 1,
totalLinks: links.length,
url: link,
hintedName: hintName || "",
initialTargetPath: item.targetPath
});
registeredLinkSummary.push(`#${linkIdx + 1} ${fileName} <- ${link}`);
packageEntry.itemIds.push(itemId);
this.session.items[itemId] = item;
this.itemCount += 1;
@@ -2795,6 +2794,22 @@ export class DownloadManager extends EventEmitter {
addedLinks += 1;
}
// One batched log entry per package instead of one per link.
// Item-logs are left uninitialized here — they'll be lazily created
// the first time the item actually gets a real lifecycle event
// (download start, error, etc.). For very large packages (>50 links)
// we only log the first 20 + a "... +N more" suffix so the single log
// line doesn't grow into hundreds of KB.
if (registeredLinkSummary.length > 0) {
const PREVIEW = 20;
const linksField = registeredLinkSummary.length <= 50
? registeredLinkSummary.join(" | ")
: `${registeredLinkSummary.slice(0, PREVIEW).join(" | ")} | ... +${registeredLinkSummary.length - PREVIEW} more`;
this.logPackageForPackage(packageEntry, "INFO", `Links registriert (${registeredLinkSummary.length})`, {
links: linksField
});
}
this.session.packages[packageId] = packageEntry;
this.session.packageOrder.push(packageId);
addedPackages += 1;