ARCHITECTURE.md described the 2.x write path, where namespace changes
waited for Drive and every mount had its own inode space. Rewrite it
around the local tree (lid, inode = lid + 1, local~<lid> stand-ins), the
queue and its 16 drain workers, sync issues, and the remaining split
between the mount and the mirror engine. Drop the pins.json claim; pins
moved into the database at schema v5.
TESTING.md gains the simulation runs and loses the per-case narrative
that --list and BUGS.md already carry. ROADMAP.md takes over the
post-3.0.0 items from the milestone status file.
Remove MILESTONE-3.0.0-STATUS.md (a working log) and UI_UX_PLAN.md (the
2.x design audit). Code comments and CI that cited the untracked
docs/MILESTONE-3.0.0.md now point at TESTING.md, ARCHITECTURE.md and
ROADMAP.md. Threat-model links follow the section to §10.
The confirmation run on 812eb3d (PDFS_SIM_KNOWN=fail, twelve seeds)
passed every profile, so no open bug is tolerated any more: the call,
hang and damage matchers for B125 to B134 go, and any of them now fails
the seed. B126, B128 and B133 were fixed with B138, B162 and B137;
B134 stays open with local_first off only, which the runs do not mount.
A retried create now adopts the file its lost answer made when Drive's
SHA-1 matches the staged blob, not only when the file is empty. A delete
after a sent create keeps the op as a trash that finds and trashes the
node that create may have made; a new create of that name waits for it.
The simulation's settle now reads the log, so known damage is explained.
one_client_on_a_slow_link runs the Wi-Fi faults without outages and has
to drain within each settle. a_thousand_files_drain_as_fast_on_wifi_as_on_lan
times 100 folders and 1,000 files on LAN and Wi-Fi; it only prints for
now and runs when PDFS_SIM_MEASURE is set.
Found by the seeded simulation runs (docs/BUGS.md).
- B122: a rename across folders goes to Drive as one move-multiple
request, so a sibling holding the new name in the old folder no longer
fails it with EIO. The drain uses the same request.
- B123: a second write open on a file just written no longer fails with
EIO. An empty file counts as complete, our own renames, moves and
online creates record their echo, a duplicated event is skipped, a
rename keeps the file's mtime while its revision stays, and a landed
upload is cached before open handles are rebased onto it.
- B124: a node put back after a failed replacing rename leaves `hidden`
and its folder is listed again, so it shows in the mount.
- B131: a landed create is read back with the same short retry as an
online create, and its bytes are cached under the real uid before an
open handle is repointed at it.
- B135: an online rename settles the tree through the state guard, so
the database has both folders unlisted before the reply. A lookup
right after it no longer rebuilds the destination without the file.
The simulation runs no longer tolerate B122 or B131.
The runs did not find B105 with its fix reverted. Drive lists a trash
later than the round trips take, so the drain never saw its own
client's delete while it still held the write.
The FakeDrive can now hold the next read of one node on its way to
Drive. A new profile, one-client-deletes, writes a file Drive holds,
waits for the drain to read the node back, deletes the file, and lets
the read go on once Drive shows the trash. A conflict the drain keeps
because a client's own file was trashed now fails the run as a false
conflict. With B105's fix reverted, seeds 1 and 2 fail on it.
Outside the step, such a conflict is counted as B134 when the copy is
named after the file: the drain looked after an online unlink had
trashed the file on Drive but before it dropped the write. Seed 3 of
three-clients came up with it once.
Half the time the step makes the file again before the read goes on.
That is B133: the new file's write takes the op id the delete freed,
and B105's check finds it. The runs count it.
The runs also count B132, an ENOENT after a restart that restored
queued ops, in a seed that lost the link. Seed 4 of one-client-flaky
failed on it. The known-bug matchers now also see what was logged
before the call.
The second half of the simulation harness in docs/MILESTONE-3.0.0.md
section 8.2. A run starts one or more real FUSE daemons on a shared
FakeDrive. Each client owns a folder `c<n>` that only it writes. A seed
then draws syscalls through the mounts, link flaps, restarts, pauses and
settles. Every syscall is checked against a model of the client's tree as
it happens. After the last step every link comes up and the run checks
no loss, POSIX behaviour, no false conflicts, convergence and liveness.
- sim::model is the POSIX model: the tree, what each call should answer,
and how it changes the tree.
- sim::run has the profiles: one client on a good link, one on a flaky
link with flaps and restarts, and three clients. PDFS_SIM_SEEDS sets
how many seeds each runs, and PDFS_SIM_SEED replays one.
- sim::watchdog catches a call that never returns. It takes every
thread's stack in the process, since no tracer can stop a thread
blocked on a FUSE request. It then aborts the run's FUSE connections
and writes the stacks next to the run's state.
- The daemons' logs at INFO and above are kept, so a known open bug is
told apart from a new failure. Known bugs are counted, and the seed ends
where the mount leaves the model behind. PDFS_SIM_KNOWN=fail fails on
them instead.
- A restart retries opening the database while the last run still holds
it.
- The FakeDrive restamps a node's modification time on a rename or move,
as Drive does (B113).
The runs mount FUSE, so they are ignored by default. Run them with
`cargo test -p pdfs-fuse --lib sim:: -- --ignored --test-threads=1`.
Test-only; no behaviour change.