version bump + bug docs

This commit is contained in:
nvrl committed 2026-08-16 21:34:55 +02:00
1 parent 098da2e97a
commit 481c3fe0c7
4 files changed
+60 -6

No files matched your search

+54
View File
@@ -12,6 +12,60 @@ Conventions:
---
## B83 — An access-deferred op retries forever and is reported as an ordinary queued upload
**Status:** Partly fixed (unverified) 2026-08-16 — the reporting half is in and
unit-tested; the cause (an unknown node reading as `EACCES`) is still open, see
"Still open" below.
**Found:** 2026-08-16, running `scripts/fuse-acceptance.sh --live` on a
throwaway CLI-made mount. Four regression cases (B69, B70, B74, B79) failed with
`TimeoutError: daemon mutation queue did not drain` — the suite's
`wait_for_queue()` polls the *global* `status.mount.pending_uploads`, which had
been sitting at 28 for 30 days on the development machine. `pdfs status` called
them 28 queued uploads holding 18.4 GiB; `pdfs transfers` showed nothing;
`failing_ops` and `parked_uploads` were both 0.
**Where:** `run_pending_drain` / `run_authorized_drain`
(`crates/pdfs-fuse/src/drain.rs`), `require_uid_access`
(`crates/pdfs-fuse/src/lib.rs`), `Db::effective_node_access`
(`crates/pdfs-core/src/db/share_access.rs`),
`Db::defer_op_without_attempt` (`crates/pdfs-core/src/db/ops.rs`).
**Root cause.** `effective_node_access` returns `Ok(None)` when the uid has no
row in `nodes`, and `require_uid_access` maps that to `EACCES` — "I have never
heard of this node" and "you may not write this node" are the same answer. The
drain reads `EACCES` as `DrainDisposition::AccessDeferred` and calls
`defer_op_without_attempt`, which by design touches neither `attempts` nor
`last_error`. So the op was re-deferred every `DRAIN_ACCESS_RECHECK` (5s)
indefinitely, and every channel that could have surfaced it stayed empty:
`attempts` 0, `last_error` NULL, `parked` counts only `PARK_UNTIL` rows,
`failing` counts only `attempts >= FAILING_ATTEMPTS`, and the one log line was
`debug!`. On the machine that found it, all 28 uids were absent from `nodes`:
```sql
select (select count(*) from pending_op p
where exists(select 1 from nodes n where n.uid = p.uid)) as in_nodes,
(select count(*) from pending_op) as total;
-- 0 | 28
```
**Fix (this half).** Schema v27 adds `pending_op.access_deferred_since`.
`Db::defer_op_for_access` stamps it on the first deferral of a run and returns
the stamp on every one; `Core::defer_for_access` warns on the first deferral and,
once the run passes `DRAIN_ACCESS_DEFER_LIMIT` (5 min), reports it through
`record_op_failure` so it enters the ordinary backoff and shows up in
`failing_ops` and the status error text. `record_op_failure` clears the window so
each escalation re-arms it rather than firing every recheck, and an attempt the
access check admits clears it too (`clear_op_access_deferral`). The row and its
staged blob are untouched on every path — this changes only what the user can
see.
**Still open:** the conflation itself. A revision op whose node has left the
local tree should refetch the node and either proceed or fail on a real remote
answer (404/trashed), not be deferred on a local guess. Same trap applies to
`create`/`mkdir`, whose authority is the parent uid. Also unaddressed: the
acceptance runner waits on the *global* queue, so any unrelated stuck op fails
those four cases on a machine that has one.
## B82 — A `parent_uid` cycle hangs the daemon on any short search
**Status:** Fixed (unverified) 2026-08-12 — unit-tested, not driven against a