Fix shelve mechanism: reset provider + circuit breaker, reduce pause to 90s

Shelve (15+ failures) now mimics manual stop/start behavior:
- Clears item.provider for fresh provider selection on retry
- Resets provider circuit breaker (providerFailures) for the old provider
- Reduces shelve duration from 5 min to 90s since the issue is stale
  provider state, not a timing problem (manual restart works instantly)

Also adds comprehensive session-load logging:
- Logs package/item count on every session file read
- Logs errors when session file parsing fails (was silent before)
- Safety net: if primary session is empty but backup has packages,
  automatically restores from backup
- Logs shutdown save with package/item counts
- Logs DownloadManager init state and cleanup policy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sucukdeluxe
2026-03-07 17:55:56 +01:00
co-authored by Claude Opus 4.6
parent 0042ce0adb
commit e00c5b5344
2 changed files with 61 additions and 9 deletions
+32 -4
View File
@@ -702,9 +702,15 @@ export function normalizeLoadedSessionTransientFields(session: SessionState): Se
function readSessionFile(filePath: string): SessionState | null {
try {
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8")) as unknown;
return normalizeLoadedSessionTransientFields(normalizeLoadedSession(parsed));
} catch {
const raw = fs.readFileSync(filePath, "utf8");
const parsed = JSON.parse(raw) as unknown;
const session = normalizeLoadedSessionTransientFields(normalizeLoadedSession(parsed));
const pkgCount = Object.keys(session.packages).length;
const itemCount = Object.keys(session.items).length;
logger.info(`Session geladen: ${filePath} (${pkgCount} Pakete, ${itemCount} Items, ${raw.length} Bytes)`);
return session;
} catch (error) {
logger.error(`Session-Datei nicht lesbar: ${filePath}: ${String(error)}`);
return null;
}
}
@@ -794,15 +800,37 @@ export function emptySession(): SessionState {
export function loadSession(paths: StoragePaths): SessionState {
ensureBaseDir(paths.baseDir);
if (!fs.existsSync(paths.sessionFile)) {
logger.info("Keine Session-Datei vorhanden, starte mit leerer Session");
return emptySession();
}
const primary = readSessionFile(paths.sessionFile);
const backupFile = sessionBackupPath(paths.sessionFile);
// If primary loaded but is empty, check if backup has packages (safety net)
if (primary) {
const primaryPkgCount = Object.keys(primary.packages).length;
if (primaryPkgCount === 0 && fs.existsSync(backupFile)) {
const backup = readSessionFile(backupFile);
if (backup) {
const backupPkgCount = Object.keys(backup.packages).length;
if (backupPkgCount > 0) {
logger.warn(`Session-Datei ist leer (0 Pakete), aber Backup hat ${backupPkgCount} Pakete — verwende Backup`);
try {
const payload = JSON.stringify({ ...backup, updatedAt: Date.now() });
const tempPath = sessionTempPath(paths.sessionFile, "sync");
fs.writeFileSync(tempPath, payload, "utf8");
syncRenameWithExdevFallback(tempPath, paths.sessionFile);
} catch {
// ignore restore write failure
}
return backup;
}
}
}
return primary;
}
const backupFile = sessionBackupPath(paths.sessionFile);
const backup = fs.existsSync(backupFile) ? readSessionFile(backupFile) : null;
if (backup) {
logger.warn("Session defekt, Backup-Datei wird verwendet");