fix: use NAS directory filenames for already-safe matching when manifest is sparse
When Kisani's manifest has 0–1 entries but the NAS directory contains thousands of files uploaded by other tools, reconcile previously showed Already Safe = 1 because countSafe only matched against the tiny manifest. The validity check in buildManifestIndex now: - Stores the filtered NAS filenames in NASManifestCache (directoryFilenames field) - Returns a ManifestIndex(nasListing:) for filename-based matching against all NAS files The fast-path reconcile now: - Detects a sparse manifest (entries ≤ 1, dirCount > 1) - Rebuilds ManifestIndex(nasFilenames:) from the cached filenames for O(1) filename matching - Falls back to full NAS reconcile if cached filenames aren't available yet Adds ManifestIndex.init(nasFilenames:) for fast-path reconstruction from cached strings. Co-Authored-By: Kutesir <kutesir@provoc.ug> Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
@@ -26,6 +26,10 @@ actor NASManifestCache {
|
||||
/// Stored independently from manifest.entries.count — the folder may contain
|
||||
/// files uploaded by other tools that are not in Kisani's manifest.
|
||||
var directoryCount: Int?
|
||||
/// Filtered NAS filenames from the last validity-check or bootstrap scan.
|
||||
/// Present only when the manifest was found sparse vs. actual NAS contents.
|
||||
/// Used by the fast-path reconcile to do filename matching without hitting NAS.
|
||||
var directoryFilenames: [String]?
|
||||
}
|
||||
|
||||
private init() {
|
||||
@@ -45,6 +49,10 @@ actor NASManifestCache {
|
||||
/// nil until the first full reconcile completes.
|
||||
var directoryCount: Int? { cached?.directoryCount }
|
||||
|
||||
/// Cached NAS filenames from the last validity-check scan.
|
||||
/// nil until the first validity-check reconcile completes with a sparse manifest.
|
||||
var directoryFilenames: [String]? { cached?.directoryFilenames }
|
||||
|
||||
/// True when the cache is absent or older than the stale threshold.
|
||||
var isStale: Bool {
|
||||
guard let c = cached else { return true }
|
||||
@@ -70,6 +78,22 @@ actor NASManifestCache {
|
||||
saveToDisk()
|
||||
}
|
||||
|
||||
/// Store filtered NAS filenames from a validity-check or bootstrap scan.
|
||||
/// Also updates directoryCount atomically from the filename array length.
|
||||
func setDirectoryFilenames(_ filenames: [String]) {
|
||||
let count = filenames.count
|
||||
if var entry = cached {
|
||||
entry.directoryCount = count
|
||||
entry.directoryFilenames = filenames
|
||||
cached = entry
|
||||
} else {
|
||||
cached = CachedEntry(manifest: BackupManifest(), savedAt: .distantPast,
|
||||
directoryCount: count, directoryFilenames: filenames)
|
||||
}
|
||||
log.info("ManifestCache: directoryFilenames=\(count)")
|
||||
saveToDisk()
|
||||
}
|
||||
|
||||
/// Increment the stored directory count after a successful upload batch.
|
||||
func addToDirectoryCount(_ delta: Int) {
|
||||
guard delta > 0 else { return }
|
||||
|
||||
Reference in New Issue
Block a user