fix(fuse): wait for a create on the wire that holds the name (B154); a write whose answer was lost is not a conflict (B155)

B154 is a regression from the B153 fix: after a failure on a held name the
op woke the ops waiting for it, but the database cannot see a create on the
wire, so the op was due again at once and spent its waits in milliseconds.
No wake while another create on the wire holds the name.

B155: a revision upload that reached Drive but lost its answer left the
file at a revision this daemon never heard of, and the retry kept the write
as a conflict copy of itself. A complete write whose bytes the remote holds,
by size and SHA-1, is sent again instead.

The fake Drive gains hold_reply_to_next_create and
lose_reply_to_next_revision for the two new sim tests.
This commit is contained in:
nvrl committed 2026-10-04 12:19:06 +02:00
1 parent 7269cb5118
commit 3f74b95b34
6 files changed
+204 -12

No files matched your search

+40
View File
@@ -12,6 +12,46 @@ Conventions:
---
## B155 — A write whose upload lost its answer lands as a conflict copy of itself
**Status:** Fixed (unverified).
**Found:** 2026-10-04, by the simulation's Wi-Fi profile (`one_client_on_a_slow_link`, seed 2,
second replay on 7269cb5). `x/z/y/a.txt` was written after its create landed. The revision upload
reached Drive, but the answer was lost and the call timed out. On the retry, Drive held
`a (sync-conflict …).txt` beside the file, with the same bytes.
**Where:** `Core::revision_conflict` in `crates/pdfs-fuse/src/drain.rs`.
**Cause.** A queued write remembers the revision it was made against. The retry found the file one
revision further, sealed by the lost upload, which this daemon never heard of, so it was not
recorded as our own (`own_sealed_rev`). The write was taken for a change from elsewhere and kept as
a conflict copy.
**Fix.** A complete write whose bytes the remote already holds, by size and SHA-1, is not a
conflict: it is sent again as a revision of the same bytes. Sim test
`a_write_whose_answer_was_lost_lands_once`, with the fake Drive's new
`lose_reply_to_next_revision`.
## B154 — A file made under the name of a create on the wire lands as a conflict copy
**Status:** Fixed (unverified).
**Found:** 2026-10-04, by CI on 7269cb5 (`one_client_on_a_good_link`, seed 9, every run). `c.bin`
was renamed to `d` while its create was on the wire, and a new `c.bin` was made. The new file landed
as `c (sync-conflict …).bin`, the mount showed that name, and `unlink c.bin` answered `ENOENT`.
**Where:** the failure path of `Core::run_pending_drain` in `crates/pdfs-fuse/src/drain.rs`.
**Cause.** A regression from the B153 fix. The new file's create found `c.bin` held by the first
create, which was still on the wire, and failed as a wait. The B153 fix then woke the ops waiting
for the name. The database cannot see a create on the wire, only queued trashes and renames, so
it found the name free and the op was due again at once. Four waits went in 70 ms, and the create
took a conflict name.
**Fix.** No wake while another create on the wire holds the name. It frees the name when it
lands, or hands it to the rename queued behind it, which wakes the waiters when it lands. Sim
test `a_file_made_under_the_name_of_a_create_on_the_wire_waits_for_it`, with the fake Drive's
new `hold_reply_to_next_create`.
## B153 — An op waiting for a name sat out its backoff after the name was free
**Status:** Fixed (unverified).
+4
View File
@@ -33,6 +33,10 @@ folder by local id too.
for this release.
### Fixed
- **A file made under the name of a file whose upload is still going out keeps its name.** It
no longer lands as a conflict copy, and the mount no longer shows that copy's name (B154).
- **A change to a file no longer lands as a conflict copy of itself when the link loses Drive's
answer to its upload** (B155).
- **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
+6 -4
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 B153.
- Fixes from the simulation and account runs since then: B127 and B141 to B155.
- **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`.
@@ -49,9 +49,11 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
- [ ] Simulation passes on the Wi-Fi profile: `one_client_on_a_slow_link` failed in CI on
seeds 2 and 3. Two bugs (B127, B151) and a harness gap: a settle did not read the log, so
it could not explain a known bug's damage. All three fixed; not yet confirmed in CI.
Replays then found two races on the LAN profile too (B152, B153), both fixed. One
slow-link run of seed 2 failed once with a rename answering `ENOENT` as a folder move
landed. Not reproduced in 8 runs since; it may be B152.
Replays then found two races on the LAN profile too (B152, B153), both fixed. CI on
7269cb5 failed LAN seed 9, a regression from the B153 fix (B154), and Wi-Fi seed 2,
a write whose answer was lost (B155). Both fixed. One slow-link run of seed 2 failed
once with a rename answering `ENOENT` as a folder move landed. Not reproduced since;
it may be B152.
- [ ] Known-bug list in `sim/run.rs` is empty: B125 and B130 open; B127, B129 and B132 fixed
but unconfirmed.
- [ ] A clean account run. The one on a6f36d5 (2026-10-04, LAN: 427 passed, 12 failed)