fix(fuse): a write opened as an upload lands takes the landed base (B170)

An upload that landed let go of its blob before the tree had its node. A
write open in between took the base it sampled at its start, the revision
the upload replaced, found none of its bytes cached, and queued an
incomplete revision; the next write open failed with EIO.

The drain keeps the landing node aside until the tree has it, and a write
open with no queued revision takes its base from that node, or from the
tree as it is when the handle goes in. The fake Drive can hold the drain
in that window (`hold_next_landing`).
This commit is contained in:
nvrl committed 2026-10-04 21:14:09 +02:00
1 parent 4757b957d2
commit 2502af6e3c
10 files changed
+187 -22

No files matched your search

+31
View File
@@ -12,6 +12,37 @@ Conventions:
---
## B170 — A write opened as an upload lands fails the next write with `EIO`
**Status:** Fixed (unverified).
**Found:** 2026-10-04, by CI on 9bbc994 and by three parallel copies of
`one_client_on_a_good_link` (seed 4; seed 5 in the local suite). A write at an offset was
refused with `EIO`, and the daemon logged "refusing write over incomplete queued revision".
Just before, an open had retried with "queued revision changed while opening for write", and
the queued write it then made was incomplete.
**Where:** `serve_open` in `crates/pdfs-fuse/src/filesystem.rs`, and `Core::refresh_after_upload`
in `crates/pdfs-fuse/src/drain.rs`.
**Cause.** When an upload lands, the drain caches its bytes under the node as Drive has it,
rebases the open write handles onto it, lets go of the blob, and then puts the node in the
tree. A write open in between found no blob to copy and took the base it had sampled at its
start: the mtime and size the tree had before the upload. The release of its partial write
filled the gaps from the cache under that key, found nothing, and queued an incomplete
revision, and a write open over one is refused (B123). Under load, the open retried because
of the landing and met that window.
**Fix.** From before the drain lets go of the blob until the tree has the node, the landing
node is kept aside. A write open with no queued revision takes its base from that node, or
else from the tree as it is when the handle goes in, not from its first sample.
**Test:** `a_write_opened_as_an_upload_lands_takes_the_landed_revision_as_its_base` in
`sim/daemon.rs` holds the drain in that window with the fake Drive's new `hold_next_landing`,
writes at an offset, and writes again after it. Without the fix the second write fails with
`EIO`.
---
## B169 — A stop lets a mirror pass go on until the runtime ends under it
**Status:** Fixed.
+2
View File
@@ -68,6 +68,8 @@ by its local id, in the queue, the stored tree and pins alike.
to upload them to the top of your Drive instead (B168).
- **Stopping the daemon during a folder sync no longer waits for the sync.** The uploads in
progress finish, and the rest waits for the next start (B169).
- **Writing to a file again just as its upload finishes no longer fails.** A write right after
it failed with "Input/output error" (B170).
- **A file renamed just as its upload finished keeps its new name.** The mount no longer showed
the old name, and a second rename no longer failed with "No such file or directory" (B152).
- **A change that waits for a name to be freed no longer waits seconds after it is.** A rename or
+4 -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: B127 and B141 to B169.
- Fixes from the simulation and account runs since then: B127 and B141 to B170.
- **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`.
@@ -110,7 +110,9 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
Looking into it found the start spending 1.7 s rebuilding listings (B167, fixed).
Its journal check caught a file removed with its folder while uploading being
sent again, to the root (B168, fixed), and a stop that let a mirror pass go on
until the runtime ended under it (B169, fixed).
until the runtime ended under it (B169, fixed). CI on 9bbc994 failed
`one_client_on_a_good_link`: a write opened as an upload landed took the revision
it replaced as its base, and the next write failed with `EIO` (B170, fixed).
5. **Release**: 2.10.0 is tagged; the 2.9.0 tag still waits for the user's go-ahead.
## After 3.0.0