diff --git a/tasks/entscheidungen-offen.md b/tasks/entscheidungen-offen.md index 7a72ceb..3982693 100644 --- a/tasks/entscheidungen-offen.md +++ b/tasks/entscheidungen-offen.md @@ -26,10 +26,12 @@ trifft in der Produktion derzeit selten. Deshalb dokumentiert statt dringend gef **Deine Entscheidung — zwei Richtungen (gleiche Spannung, andere Seite):** -- **A) Pro-Provider-Timeout:** Jeder Provider bekommt sein eigenes frisches 60s-Budget. +- **A) Pro-Provider-Timeout:** Jeder Provider bekommt sein eigenes frisches Budget. Failover bekommt IMMER einen echten Versuch. - *Kosten:* Worst-Case-Wartezeit pro Item steigt (3 Provider × 60s = bis zu 180s, bevor ein - Item aufgibt). Du akzeptierst langsameres Worst-Case-pro-Item für vollständigeres Failover. + *Kosten:* Worst-Case-Wartezeit pro Item steigt. Das Budget ist dabei ein DREHregler, kein + fixer Wert: 60s/Provider = bis 180s Worst-Case (3 Provider), 30s/Provider = bis 90s usw. + Du akzeptierst langsameres Worst-Case-pro-Item für vollständigeres Failover — und stellst + über den Pro-Provider-Wert ein, wie viel langsamer. - **B) Globales Budget behalten, aber Slice für Failover reservieren:** Provider 1 wird auf z.B. 35s gedeckelt, damit garantiert Zeit für Provider 2 bleibt. diff --git a/tests/download-manager.test.ts b/tests/download-manager.test.ts index 4caa7fb..a8fbb8f 100644 --- a/tests/download-manager.test.ts +++ b/tests/download-manager.test.ts @@ -229,6 +229,34 @@ describe("download manager", () => { expect(historyEntries[0]?.downloadedBytes).toBe(90 * 1024 * 1024); }); + it("findFallbackProviderNotInCooldown routes around cooled providers and returns null only when all are cooled", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-fallback-route-")); + tempDirs.push(root); + const manager = new DownloadManager( + { + ...defaultSettings(), + token: "rd-token", + debridLinkApiKeys: "dl-token", + providerOrder: ["realdebrid", "debridlink"], + providerPrimary: "realdebrid", + providerSecondary: "debridlink", + providerTertiary: "none", + autoProviderFallback: true + }, + emptySession(), + createStoragePaths(path.join(root, "state")) + ); + const item = { id: "fb-item", url: "https://hoster.example/file.bin", provider: "realdebrid" } as any; + + expect((manager as any).findFallbackProviderNotInCooldown(item)).toBe("realdebrid"); + + (manager as any).applyProviderBusyBackoff("realdebrid", 60000); + expect((manager as any).findFallbackProviderNotInCooldown(item)).toBe("debridlink"); + + (manager as any).applyProviderBusyBackoff("debridlink", 60000); + expect((manager as any).findFallbackProviderNotInCooldown(item)).toBeNull(); + }); + it("keeps the quick post-process requeue once the final package items are finished", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-postprocess-final-")); tempDirs.push(root);