diff --git a/Services/BackupEngine.swift b/Services/BackupEngine.swift index 32b3616..5902b00 100644 --- a/Services/BackupEngine.swift +++ b/Services/BackupEngine.swift @@ -366,7 +366,18 @@ final class BackupEngine: ObservableObject { signposter.endInterval("UploadLoop", spUpload, "\(uploaded) uploaded, \(failed) failed") - // Keep directoryCount current without a full rescan. + // Update local manifest cache immediately with every completed entry + // (uploaded + collision-skipped + fileExists-skipped). + // writeManifest() will persist this to the NAS, but even if that network + // write fails the next fast-path reconcile reads the correct counts here. + // Without this, a failed NAS write leaves the cache at 1 entry and the + // next reconcile computes alreadySafe = 1 instead of the real count. + if !manifestEntries.isEmpty { + var localMerged = baseManifest + localMerged.merge(entries: manifestEntries) + await NASManifestCache.shared.update(localMerged) + logger.info("Local cache updated — \(localMerged.entries.count) total entries") + } if uploaded > 0 { await NASManifestCache.shared.addToDirectoryCount(uploaded) } diff --git a/Services/BackupStatusService.swift b/Services/BackupStatusService.swift index 6178193..54cb897 100644 --- a/Services/BackupStatusService.swift +++ b/Services/BackupStatusService.swift @@ -432,11 +432,13 @@ final class BackupStatusService: NSObject, ObservableObject { guard let (encoded, mergedManifest) = result else { return } - try await transfer.writeData(encoded, to: manifestPath) - - // Update local cache so fast-path reconcile reads fresh data. + // Update local cache BEFORE the NAS write — decouples reconcile correctness + // from NAS write success. If the write fails/times out, the next fast-path + // reconcile still reads the correct merged manifest from local cache. await NASManifestCache.shared.update(mergedManifest) lastManifest = mergedManifest + + try await transfer.writeData(encoded, to: manifestPath) log.info("writeManifest: done — \(entries.count) new, total=\(mergedManifest.entries.count)") } catch { log.error("writeManifest failed (non-fatal): \(error.localizedDescription)")