From 9ca578962700f5a84e1c671e6b5c64e1fb4dbc8e Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Mon, 31 Aug 2026 15:33:48 +0200 Subject: [PATCH] Raise proxy connection limit to 40 --- CHANGELOG.md | 4 ++++ PROJECT_MEMORY.md | 15 ++++++++++++--- src/main/constants.ts | 2 +- src/main/proxy-segmented-download.ts | 17 ++++++++++++----- src/main/storage.ts | 2 +- src/renderer/App.tsx | 4 ++-- src/renderer/views/settings/settings-model.ts | 4 ++-- tests/proxy-segmented-download.test.ts | 9 ++++++++- tests/settings-view.test.tsx | 2 +- tests/storage.test.ts | 6 +++--- tests/visual/fixtures.ts | 2 +- 11 files changed, 47 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f610095..4092667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to Multi-Debrid Downloader are documented in this file. ## [Unreleased] +### Proxy limit tuning + +- Increase the default global proxy connection budget for new settings from 20 to 32 and raise the configurable maximum from 32 to 40 while retaining fair sharing and adaptive 429/503 backoff. + ## [2.0.77] - 2026-08-31 ### Proxy throughput and fairness diff --git a/PROJECT_MEMORY.md b/PROJECT_MEMORY.md index d8b1152..b886da4 100644 --- a/PROJECT_MEMORY.md +++ b/PROJECT_MEMORY.md @@ -10,10 +10,10 @@ Diese Datei hält den verifizierten technischen Arbeitsstand fest. Sie enthält - Verifiziert am: 31. August 2026, Europe/Berlin - Lokaler Pfad: `C:\Users\Sascha\Desktop\Claude & ChatGPT Projekte\Multi-Debrid-Downloader` -- Arbeitsbranch: `release/v2.0.77` +- Arbeitsbranch: `tune/proxy-limit-40` - Quellbasis: `release/v2.0.76` - Release-Tag: `v2.0.77` -- Baseline-Commit: `61e10034ba4f75c6f7cbe25b5639d52a18d90e37` +- Baseline-Commit: `eb68db88d08739f5f4ff23bf5b1133a132c2d8db` - Paketversion: `2.0.77` - Letztes Release: `Multi-Debrid-Downloader v2.0.77`, veröffentlicht am 31. August 2026 auf GitHub und Forgejo - Runtime-Voraussetzung: Node.js `>=20`; lokal verifiziert mit Node.js `24.19.0` und npm `11.17.0` @@ -138,6 +138,12 @@ Diese Datei hält den verifizierten technischen Arbeitsstand fest. Sie enthält - Der Paketfortschritt verwendet bei bekannten Größen dieselben heruntergeladenen und gesamten Bytes wie die Größenanzeige. Dadurch stimmen Prozentwert und `heruntergeladen / gesamt` auch bei unterschiedlich großen Dateien überein. Wenn Größen vollständig fehlen, bleibt die bisherige dateibasierte Berechnung als Rückfall erhalten. - Die Entpacklogik und ihre Produktionspfade wurden nicht verändert. Eine Serverinstallation oder ein produktiver Neustart ist nicht Bestandteil dieses Releases. +## Unveröffentlichte Anpassung nach v2.0.77 + +- Der Standardwert für neue beziehungsweise noch nicht gesetzte Konfigurationen steigt von 20 auf 32 globale Proxy-Verbindungen. Bereits gespeicherte Benutzerwerte bleiben erhalten. +- Das einstellbare Maximum steigt von 32 auf 40 globale Proxy-Verbindungen. Faire Aufteilung, Proxy-Bewertung, Cooldowns und adaptives 429/503-Backoff gelten unverändert auch oberhalb von 32. +- Die Entpacklogik und ihre Produktionspfade wurden nicht verändert. Diese Anpassung ist noch nicht veröffentlicht, installiert oder produktiv neu gestartet. + ## Start-, Build- und Testbefehle ```powershell @@ -169,6 +175,9 @@ npm exec -- tsc --noEmit ## Verifizierungen vom 31. August 2026 +- Unveröffentlichte 32/40-Limitanpassung: Proxy-Scheduler, Storage, Einstellungen, Renderer-Projektion und visuelle Fixtures 229 von 229 Tests erfolgreich. Die Main-Normalisierung deckt Standard 32, Minimum 2, Maximum 40 und Werte oberhalb des Maximums direkt ab. +- Vollständiger Client-Lauf nach der 32/40-Anpassung ohne die zwei lokal nicht ausführbaren Symlink-Fixtures: 140 Testdateien erfolgreich, 1 JVM-Testdatei übersprungen; 2.658 Tests erfolgreich und 4 übersprungen. Die übrigen 22 Public-Release-Metadatentests liefen separat erfolgreich. Alle 84 Entpacktests sind unverändert grün. +- Nach der 32/40-Anpassung erfolgreich: TypeScript, vollständiger Main-/Renderer-Build, Self-Check und 16 von 16 Backup-API-Tests. Die bekannte Vite-Warnung zum rund 576 KiB großen Renderer-Chunk bleibt bestehen. - Proxy-Scheduler-Regressionen für `v2.0.77`: 11 von 11 erfolgreich. Abgedeckt sind 32 rollierend vergebene Chunks, deutlich häufigere Chunk-Übernahme durch den schnellen von zwei unterschiedlich verzögerten Proxys, zwei überlappende Downloads mit je höchstens der Hälfte eines gemeinsamen Viererlimits, Ausschluss des festen API-Proxys, Ersatz ausgefallener Proxys, exakte Dateiinhalte und Fortschrittsrückrechnung sowie eine erfolgreiche Fortsetzung nach transientem Origin-HTTP-503 mit Reduktion neuer paralleler Requests von 6 auf höchstens 4. - Paketfortschritt, Einstellungen und Proxy-Scheduler gezielt: 232 von 232 Tests erfolgreich. Ein Paket mit 2,25 von 18,60 Größeneinheiten zeigt nun 12 Prozent statt einer dateibasierten Abweichung. - Storage einschließlich neuem Standardwert 20: 126 von 126 Tests erfolgreich. @@ -234,7 +243,7 @@ npm exec -- tsc --noEmit ## Nächste sinnvolle Schritte -1. Nach separater Installationsfreigabe `v2.0.77` mit Gesamtlimit 20 und zwei echten parallelen Real-Debrid-Dateien prüfen. Erfolgsquote, HTTP-Status, Chunk-Wechsel, Zusatzverkehr, Festplattenlast und Netto-MB/s beobachten, bevor das Maximum von 24 erwogen wird. +1. Die unveröffentlichte Anpassung auf Standard 32 und Maximum 40 erst nach separater Release-Freigabe paketieren. Danach mit zwei echten parallelen Real-Debrid-Dateien zunächst 32 und anschließend optional 40 prüfen sowie 429/503, Zusatzverkehr, Festplattenlast und Netto-MB/s beobachten. 2. Nach einer getrennt freigegebenen Serverinstallation den Ablauf Real-Debrid-Web-Download, Hauptfenster schließen und unmittelbar neu starten am echten Zielsystem verifizieren; vor jedem Eingriff Prozessbaum und Logtail sichern. 3. Sicherheitsabhängigkeiten in einem separaten Upgrade-Branch aktualisieren, Electron-/Vite-/Vitest-Major-Wechsel einzeln testen und danach den vollständigen Windows-Paketpfad prüfen. 4. Eine nichtdestruktive Strategie zur Bereinigung der divergierenden `main`-Branches abstimmen; kein Force-Push ohne ausdrückliche Freigabe. diff --git a/src/main/constants.ts b/src/main/constants.ts index 2fce66d..37e0d9d 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -109,7 +109,7 @@ export function defaultSettings(): AppSettings { proxyDownloadEnabled: false, proxyListPath: "", proxyApiProxyIndex: 1, - proxyConnectionsPerDownload: 20, + proxyConnectionsPerDownload: 32, updateRepo: DEFAULT_UPDATE_REPO, autoUpdateCheck: true, clipboardWatch: false, diff --git a/src/main/proxy-segmented-download.ts b/src/main/proxy-segmented-download.ts index 975c0df..40de8ea 100644 --- a/src/main/proxy-segmented-download.ts +++ b/src/main/proxy-segmented-download.ts @@ -12,6 +12,8 @@ const DEFAULT_MIN_SEGMENT_BYTES = 8 * 1024 * 1024; const DEFAULT_MAX_CHUNK_BYTES = 16 * 1024 * 1024; const DEFAULT_CHUNKS_PER_CONNECTION = 8; const DEFAULT_PROXY_ATTEMPTS = 3; +const DEFAULT_CONNECTION_LIMIT = 32; +const MAX_CONNECTION_LIMIT = 40; const PROXY_PROBE_END = 1; const MAX_REDIRECTS = 5; const MAX_SEGMENTS = 4_096; @@ -86,6 +88,11 @@ export type ProxyFallbackReason = const proxyFileCache = new Map(); +export function normalizeProxyConnectionLimit(value: number, fallback = DEFAULT_CONNECTION_LIMIT): number { + const candidate = Number.isFinite(value) && value !== 0 ? value : fallback; + return Math.max(2, Math.min(MAX_CONNECTION_LIMIT, Math.floor(candidate))); +} + function proxyEndpointKey(proxy: ProxyEndpoint): string { return `${proxy.url}\0${proxy.authorization}`; } @@ -124,8 +131,8 @@ interface ProxyPerformance { } class SharedProxyCoordinator { - private configuredConnectionLimit = 16; - private adaptiveConnectionLimit = 16; + private configuredConnectionLimit = DEFAULT_CONNECTION_LIMIT; + private adaptiveConnectionLimit = DEFAULT_CONNECTION_LIMIT; private activeConnections = 0; private readonly activeProxies = new Set(); private readonly activeConnectionsByGroup = new Map(); @@ -139,7 +146,7 @@ class SharedProxyCoordinator { private successfulTransfersSinceThrottle = 0; public setConnectionLimit(limit: number): void { - const normalized = Math.max(2, Math.min(32, Math.floor(limit || 16))); + const normalized = normalizeProxyConnectionLimit(limit); if (normalized !== this.configuredConnectionLimit) { this.configuredConnectionLimit = normalized; this.adaptiveConnectionLimit = normalized; @@ -842,8 +849,8 @@ export async function downloadWithProxySegments(options: ProxySegmentedDownloadO return { status: "fallback", reason: "no_valid_proxies" }; } const poolKey = path.resolve(proxyPath); - const requestedConnections = Math.max(2, Math.min(32, Math.floor(options.connections || 16))); - const totalConnectionLimit = Math.max(2, Math.min(32, Math.floor(options.totalConnectionLimit ?? requestedConnections))); + const requestedConnections = normalizeProxyConnectionLimit(options.connections); + const totalConnectionLimit = normalizeProxyConnectionLimit(options.totalConnectionLimit ?? requestedConnections, requestedConnections); sharedProxyCoordinator.setConnectionLimit(totalConnectionLimit); const reservedProxyIndex = Math.max(0, Math.floor(options.reservedProxyIndex || 0)); const reservedProxy = reservedProxyIndex > 0 ? loaded.proxies[reservedProxyIndex - 1] : null; diff --git a/src/main/storage.ts b/src/main/storage.ts index cc4dec5..968449a 100644 --- a/src/main/storage.ts +++ b/src/main/storage.ts @@ -615,7 +615,7 @@ export function normalizeSettings(settings: AppSettings): AppSettings { proxyDownloadEnabled: Boolean(settings.proxyDownloadEnabled), proxyListPath: asText(settings.proxyListPath).slice(0, 4096), proxyApiProxyIndex: clampNumber(settings.proxyApiProxyIndex, defaults.proxyApiProxyIndex, 1, 100000), - proxyConnectionsPerDownload: clampNumber(settings.proxyConnectionsPerDownload, defaults.proxyConnectionsPerDownload, 2, 32), + proxyConnectionsPerDownload: clampNumber(settings.proxyConnectionsPerDownload, defaults.proxyConnectionsPerDownload, 2, 40), autoUpdateCheck: Boolean(settings.autoUpdateCheck), updateRepo: migrateUpdateRepo(asText(settings.updateRepo), defaults.updateRepo), clipboardWatch: Boolean(settings.clipboardWatch), diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 32f9ae0..25c9432 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -953,7 +953,7 @@ const emptySnapshot = (): UiSnapshot => ({ removeSamplesAfterExtract: false, enableIntegrityCheck: true, autoResumeOnStart: true, autoReconnect: false, reconnectWaitSeconds: 45, completedCleanupPolicy: "never", maxParallel: 4, maxParallelExtract: 2, extractCpuPriority: "high", retryLimit: 0, speedLimitEnabled: false, speedLimitKbps: 0, speedLimitMode: "global", - proxyDownloadEnabled: false, proxyListPath: "", proxyApiProxyIndex: 1, proxyConnectionsPerDownload: 20, + proxyDownloadEnabled: false, proxyListPath: "", proxyApiProxyIndex: 1, proxyConnectionsPerDownload: 32, updateRepo: "", autoUpdateCheck: true, clipboardWatch: false, minimizeToTray: false, theme: "dark", themePreference: "dark", logStorageLocation: "appdata", collapseNewPackages: true, animatePackageDisclosure: true, historyRetentionMode: "permanent", historyMaxEntries: 500, historyMaxAgeDays: 0, autoSortPackagesByProgress: false, autoSkipExtracted: false, hideExtractedItems: true, confirmDeleteSelection: true, backupIncludeDownloads: false, backupIncludeRemoteDiagnostics: false, notifyMention: "", notifyOnPackageCompleted: false, notifyOnPackageFailed: false, notifyOnRunFinished: false, @@ -6067,7 +6067,7 @@ export function App(): ReactElement { maxParallelExtract: [1, 8, 2], reconnectWaitSeconds: [10, 600, 45], proxyApiProxyIndex: [1, 100000, 1], - proxyConnectionsPerDownload: [2, 32, 20] + proxyConnectionsPerDownload: [2, 40, 32] }; const bounds = numericLimits[fieldId as keyof RendererSettingsDraft]; if (bounds) { diff --git a/src/renderer/views/settings/settings-model.ts b/src/renderer/views/settings/settings-model.ts index 407f04b..f4c2d7a 100644 --- a/src/renderer/views/settings/settings-model.ts +++ b/src/renderer/views/settings/settings-model.ts @@ -460,9 +460,9 @@ export function buildSettingsFormViewModel({ label: "Proxy-Verbindungen insgesamt", value: String(settings.proxyConnectionsPerDownload), min: 2, - max: 32, + max: 40, disabled: !settings.proxyDownloadEnabled, - help: `Dieses Gesamtlimit wird fair zwischen allen gleichzeitigen Segmentdownloads geteilt. Bei ${settings.maxParallel} aktiven Downloads laufen zusammen höchstens ${settings.proxyConnectionsPerDownload} Segmentverbindungen; 20 ist der empfohlene Startwert. Langsame und ausgefallene Proxys werden automatisch zurückgestuft. Kleine Dateien, Teil-Downloads und aktive Geschwindigkeitslimits laufen über den festen API-Proxy.` + help: `Dieses Gesamtlimit wird fair zwischen allen gleichzeitigen Segmentdownloads geteilt. Bei ${settings.maxParallel} aktiven Downloads laufen zusammen höchstens ${settings.proxyConnectionsPerDownload} Segmentverbindungen; 32 ist der Standardwert, 40 das Maximum. Langsame und ausgefallene Proxys werden automatisch zurückgestuft. Kleine Dateien, Teil-Downloads und aktive Geschwindigkeitslimits laufen über den festen API-Proxy.` } ] }, diff --git a/tests/proxy-segmented-download.test.ts b/tests/proxy-segmented-download.test.ts index 17d653c..ee4dc34 100644 --- a/tests/proxy-segmented-download.test.ts +++ b/tests/proxy-segmented-download.test.ts @@ -5,7 +5,7 @@ import os from "node:os"; import path from "node:path"; import { Transform } from "node:stream"; import { afterEach, describe, expect, it } from "vitest"; -import { downloadWithProxySegments, parseProxyList, selectFixedProxy } from "../src/main/proxy-segmented-download"; +import { downloadWithProxySegments, normalizeProxyConnectionLimit, parseProxyList, selectFixedProxy } from "../src/main/proxy-segmented-download"; interface RunningServer { port: number; @@ -128,6 +128,13 @@ async function createTempDirectory(): Promise { } describe("proxy segmented download", () => { + it("uses 32 connections by default and clamps the configurable maximum to 40", () => { + expect(normalizeProxyConnectionLimit(0)).toBe(32); + expect(normalizeProxyConnectionLimit(40)).toBe(40); + expect(normalizeProxyConnectionLimit(999)).toBe(40); + expect(normalizeProxyConnectionLimit(1)).toBe(2); + }); + it("accepts authenticated premium proxy list formats without exposing entries", () => { const secret = "private-password"; const count = parseProxyList([ diff --git a/tests/settings-view.test.tsx b/tests/settings-view.test.tsx index 8c2130c..1d3c576 100644 --- a/tests/settings-view.test.tsx +++ b/tests/settings-view.test.tsx @@ -683,7 +683,7 @@ describe("settings views", () => { label: "Proxy-Verbindungen insgesamt", value: "16", min: 2, - max: 32, + max: 40, help: expect.stringContaining("zusammen höchstens 16 Segmentverbindungen") })); }); diff --git a/tests/storage.test.ts b/tests/storage.test.ts index feceda8..5e8a03c 100644 --- a/tests/storage.test.ts +++ b/tests/storage.test.ts @@ -606,15 +606,15 @@ describe("settings storage", () => { expect(normalized.proxyDownloadEnabled).toBe(true); expect(normalized.proxyListPath).toBe("C:\\proxies.txt"); expect(normalized.proxyApiProxyIndex).toBe(100000); - expect(normalized.proxyConnectionsPerDownload).toBe(32); + expect(normalized.proxyConnectionsPerDownload).toBe(40); expect(normalized.outputDir).toBe(defaultSettings().outputDir); expect(normalized.extractDir).toBe(defaultSettings().extractDir); expect(normalized.mkvLibraryDir).toBe(defaultSettings().mkvLibraryDir); expect(normalized.updateRepo).toBe(defaultSettings().updateRepo); }); - it("uses 20 total proxy connections as the default for new settings", () => { - expect(defaultSettings().proxyConnectionsPerDownload).toBe(20); + it("uses 32 total proxy connections as the default for new settings", () => { + expect(defaultSettings().proxyConnectionsPerDownload).toBe(32); }); it("migrates the previous private update repository to the public release repository", () => { diff --git a/tests/visual/fixtures.ts b/tests/visual/fixtures.ts index 01c1f60..efbd4c7 100644 --- a/tests/visual/fixtures.ts +++ b/tests/visual/fixtures.ts @@ -120,7 +120,7 @@ function createSettings(): AppSettings { proxyDownloadEnabled: false, proxyListPath: "", proxyApiProxyIndex: 1, - proxyConnectionsPerDownload: 20, + proxyConnectionsPerDownload: 32, updateRepo: "Sucukdeluxe/Multi-Debrid-Downloader", autoUpdateCheck: true, clipboardWatch: true,