fix(fuse): a new file closed empty reaches Drive at once (B192)

This commit is contained in:
nvrl committed 2026-10-05 04:50:10 +02:00
1 parent 57a825887c
commit 31863a1da1
5 files changed
+57 -4

No files matched your search

+7 -1
View File
@@ -3334,6 +3334,12 @@ impl Core {
if !h.dirty {
self.cache.clear_scratch_durable(&h.path);
let _ = std::fs::remove_file(&h.path);
// A new file's create waits for this release to wake the drain
// (`Core::queue_local_node`). Closed empty, it attached nothing
// that would, and sat until the idle poll (`docs/BUGS.md` B192).
if is_local_uid(&h.uid) {
self.wake_drain();
}
return Ok(());
}
preserve_on_access_denied(self.require_uid_writable(&h.uid), h.dirty, || {
@@ -3828,7 +3834,7 @@ impl Core {
debug!(%uid, %parent_uid, name, is_dir, "created node offline; queued");
// A folder is due now; without this the workers slept through it to
// the idle poll (B160). A file is not: its bytes ride on the create, and
// the release that attaches them wakes the drain.
// its release wakes the drain, with bytes or without (B192).
if is_dir {
self.wake_drain();
}
+28
View File
@@ -383,6 +383,34 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
#[ignore = "mounts FUSE: run with `cargo test -p pdfs-fuse sim:: -- --ignored`"]
fn a_file_made_empty_while_the_drain_is_idle_reaches_drive_at_once() {
// A new file's create waits for the release that attaches its bytes
// to wake the drain. A file closed without any attached none, so it
// sat until the workers' 30 s idle poll (B192).
let drive = FakeDrive::new();
let dir = scratch("idle-empty-create");
let daemon = Daemon::start(&dir, drive.client(1, Faults::lan())).unwrap();
assert!(wait_until(Duration::from_secs(30), || {
daemon.pending().is_ok_and(|items| items.is_empty())
}));
std::fs::write(daemon.mountpoint.join("e.md"), b"").unwrap();
assert!(
wait_until(Duration::from_secs(10), || {
daemon.pending().is_ok_and(|items| items.is_empty())
&& drive.lookup("e.md").is_some()
}),
"{:?} {:?}",
daemon.pending(),
drive.tree()
);
assert!(matches!(daemon.stop(), Ok(MountOutcome::Shutdown)));
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
#[ignore = "mounts FUSE: run with `cargo test -p pdfs-fuse sim:: -- --ignored`"]
fn a_file_whose_upload_was_not_read_back_takes_two_writes_at_once() {
+17
View File
@@ -12,6 +12,23 @@ Conventions:
---
## B192 — A new file closed empty reaches Drive only half a minute later
**Status:** Fixed.
**Found:** 2026-10-05, while writing the test for B193. An empty file made on an idle mount was
still queued 30 s later.
**Where:** `Core::queue_revision` in `crates/pdfs-fuse/src/lib.rs`.
**Cause.** Queueing a file's create does not wake the drain: its bytes ride on the create, and
the release that attaches them wakes it (B160). A file closed without a write (`touch`, `: > f`)
attaches nothing, so its create waited for the workers' 30 s idle poll.
**Fix.** Closing a clean handle of a file whose create is queued wakes the drain.
**Test:** simulation test `a_file_made_empty_while_the_drain_is_idle_reaches_drive_at_once`
(failed before the fix: the file was still queued after 10 s).
## B191 — A file deleted after its create failed and was written again can stay on Drive
**Status:** Fixed (unverified).
+2 -1
View File
@@ -54,7 +54,8 @@ by its local id, in the queue, the stored tree and pins alike.
(B158).
- **Opening a file right after the daemon restarts no longer fails with "Resource temporarily
unavailable"** (B159).
- **A new folder reaches Drive at once**, not up to half a minute later (B160).
- **A new folder, or a new empty file, reaches Drive at once**, not up to half a minute later
(B160, B192).
- **A file moved while it was still being uploaded keeps its new name in the mount** on a slow
link (B161).
- **A folder removed and made again at once keeps what goes into it.** It used to be created
+3 -2
View File
@@ -9,7 +9,7 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
- **Phase 2**: local ids, inode = local id, one tree for every mount (2.10.0).
- **Phase 3, first part**: mkdir, create, rename, unlink and rmdir on the mount are recorded
locally and sent from the queue; `"local_first": false` brings back the old path (5e0f30c).
- Fixes from the simulation and account runs since then: B125, B127, B130 and B141 to B191.
- Fixes from the simulation and account runs since then: B125, B127, B130 and B141 to B192.
- **Sync issues, first cut** (§6.2): refusals sorted into issues, shown at once in the app,
tray and CLI; Export button and `pdfs sync export`; `pdfs sync issues`; Discard button and
`pdfs sync discard`; a warning badge on the node in the file browser.
@@ -118,7 +118,8 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
after an outage only the drain had met (B189, fixed; older than 3.0.0), and a file
saved just before a stop whose release the stop dropped (B190, fixed; older too). The run
on 1436c5f found a file deleted after its failed create was written again left on Drive
(B191, fixed).
(B191, fixed). Its test for B193 found a new empty file waiting out the idle poll (B192,
fixed).
- [ ] A clean account run. The one on a6f36d5 (2026-10-04, LAN: 427 passed, 12 failed)
failed B113 and B121 on every on-demand mount, two mirror-to-mirror move cases, and
the journal check. B121 was a bug (B149, fixed); B113's case still expected creates