Performance: prune long-lived caches, hoist regexes, idle chart redraws

Three low-risk optimizations that reduce CPU/memory footprint without
changing user-visible behavior:

1. Periodic cleanup of unbounded module-level Maps (24/7 stability):
   - debridLinkKeyCooldowns, debridLinkKeyCooldownDetails,
     debridLinkKeyRuntimeStatuses (debrid.ts)
   - megaDebridAccountCooldowns (debrid.ts)
   - allDebridHostInfoCache (download-manager.ts)
   - All pruned every 10 min via existing resetStaleRetryState() with a
     1h grace window for debugging
   - Without this, modules accumulated entries indefinitely over days
     of continuous server operation

2. Hoist regex literals in resolveArchiveItemsFromList() to module scope.
   Avoids 5 RegExp constructions per call. The function is called per
   archive set during extraction discovery — adds up on packages with
   many archives.

3. BandwidthChart: skip the 250ms redraw interval while the session is
   stopped or paused. The chart renders once on state change to show the
   latest history, then sleeps until downloads resume. Saves 4 canvas
   redraws per second when idle (renderer CPU).

No functional changes. All 565 tests green.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe
2026-04-19 12:32:38 +02:00
co-authored by Claude Opus 4.6
parent 26edc79784
commit bece2f3e85
3 changed files with 85 additions and 14 deletions
+35
View File
@@ -59,6 +59,28 @@ export function resetDebridLinkRuntimeStateForTests(): void {
debridLinkKeyRuntimeStatuses.clear();
}
/** Periodic cleanup of expired Debrid-Link cooldown/runtime entries.
* Without this, module-level Maps grow unbounded over 24/7 operation.
* Removes entries whose cooldown expired more than 1 hour ago. */
export function pruneExpiredDebridLinkRuntimeState(now = Date.now()): number {
let removed = 0;
const grace = 60 * 60 * 1000; // keep 1h grace for debugging
for (const [keyId, until] of debridLinkKeyCooldowns) {
if (until + grace < now) {
debridLinkKeyCooldowns.delete(keyId);
debridLinkKeyCooldownDetails.delete(keyId);
removed += 1;
}
}
for (const [keyId, status] of debridLinkKeyRuntimeStatuses) {
if (!debridLinkKeyCooldowns.has(keyId) && now - status.updatedAt > grace) {
debridLinkKeyRuntimeStatuses.delete(keyId);
removed += 1;
}
}
return removed;
}
export function primeDebridLinkRuntimeCooldownForTests(keyId: string, cooldownMs: number, message = "Debrid-Link Key im Cooldown"): void {
setDebridLinkKeyCooldownState(keyId, cooldownMs, message, "temporary");
}
@@ -140,6 +162,19 @@ export function resetMegaDebridRuntimeStateForTests(): void {
megaDebridAccountCooldowns.clear();
}
/** Periodic cleanup of expired Mega-Debrid cooldown entries. */
export function pruneExpiredMegaDebridRuntimeState(now = Date.now()): number {
let removed = 0;
const grace = 60 * 60 * 1000;
for (const [id, detail] of megaDebridAccountCooldowns) {
if (detail.until + grace < now) {
megaDebridAccountCooldowns.delete(id);
removed += 1;
}
}
return removed;
}
export function primeMegaDebridRuntimeCooldownForTests(accountId: string, cooldownMs: number, message = "Mega-Debrid Account im Cooldown"): void {
setMegaDebridAccountCooldownState(accountId, cooldownMs, message, "temporary");
}