mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
fix(fuse,core): count a new file's landing until its bytes are cached
A create that carries a file's bytes retired its op, then cached the bytes and gave the tree the real uid. Nothing counted that window, so `pdfs status` read empty while it ran, which B119 promises means the file reads without the network. A delete in that window found the placeholder uid in the tree and trashed the node by its real uid. `queue_trash` asked the tree whether that uid was open, found no entry, and dropped the cached bytes the open handle reads. If the cache store ran at the same moment, its tag was written after the eviction and never removed. CI caught that as a stray .meta file. The create's landing is now counted like an upload's, from the op's retirement until the tree has the real uid. The delete and the replace pass whether they found the file open. Opening the cache removes a tag whose bytes are gone, which also covers a crash between an eviction's two removals. The simulations wait for `Daemon::drained`, which counts the landing, before they go offline. B255 and B256 move to fixed: the account run checked both.
This commit is contained in:
1 parent
27201914ee
commit
639ca359fa
7 files changed
+174
-14
No files matched your search
@@ -621,10 +621,20 @@ impl ContentCache {
|
||||
for entry in rd.flatten() {
|
||||
let name = entry.file_name();
|
||||
let Some(name) = name.to_str() else { continue };
|
||||
// A `.meta` tags the blob named like it without the suffix. One
|
||||
// whose blob is gone is what an eviction leaves between its two
|
||||
// removals, by a crash or by a store of the same uid landing in
|
||||
// between, and nothing else would ever remove it.
|
||||
if let Some(blob) = name.strip_suffix(".meta") {
|
||||
if !dir.join(blob).exists() {
|
||||
let _ = std::fs::remove_file(entry.path());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// `.geom` is a block-geometry sidecar (see `GeometryMeta`), tens
|
||||
// of bytes and not a cached block; like `.meta` it is neither
|
||||
// budgeted nor evictable on its own.
|
||||
if name.ends_with(".meta") || name.ends_with(".geom") {
|
||||
if name.ends_with(".geom") {
|
||||
continue;
|
||||
}
|
||||
// A `.tmp` is a staging file from a store that never completed
|
||||
@@ -2250,6 +2260,28 @@ mod tests {
|
||||
assert!(total <= 8, "budget enforced after reconcile, total={total}");
|
||||
}
|
||||
|
||||
/// An eviction removes the blob, then its tag. A crash in between, or a
|
||||
/// store of the same uid landing in between, left the tag on its own, and
|
||||
/// nothing removed it.
|
||||
#[test]
|
||||
fn a_tag_without_its_blob_is_swept_at_open() {
|
||||
let dir = TempDir::new();
|
||||
let content = dir.path().join("content");
|
||||
let pins = dir.path().join("pins.json");
|
||||
let db = Arc::new(Db::open_in_memory().unwrap());
|
||||
{
|
||||
let c = ContentCache::open(content.clone(), pins.clone(), 0, db.clone()).unwrap();
|
||||
c.store(&uid("kept"), 1, 4, b"kept").unwrap();
|
||||
c.store(&uid("gone"), 1, 4, b"gone").unwrap();
|
||||
std::fs::remove_file(c.blob_path(&uid("gone"))).unwrap();
|
||||
}
|
||||
|
||||
let c = ContentCache::open(content, pins, 0, db).unwrap();
|
||||
assert!(!c.meta_path(&uid("gone")).exists());
|
||||
assert!(c.meta_path(&uid("kept")).exists());
|
||||
assert_eq!(c.read_range(&uid("kept"), 1, 4, 0, 4).unwrap(), b"kept");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_metadata_is_a_miss() {
|
||||
let (c, _d) = cache();
|
||||
|
||||
Reference in new issue
Block a user