Skip to content

Trwały fsync (rclone mount2 + syncfs w punchfs) i czyste zatrzymanie VM (vm-hold) - #58

Merged
mbeczynski merged 6 commits into
mainfrom
fix/trwaly-fsync
Nov 12, 2026
Merged

mbeczynski merged 6 commits into
mainfrom
fix/trwaly-fsync

Conversation

@mbeczynski

Copy link
Copy Markdown
Contributor

Przyczyna

Obraz Time Machine uszkodzony po wyłączeniu Maca (12.11, wcześniej 03.11): VM cmsmb ginie twardo razem z Makiem, a łańcuch fsync TM -> SMB -> Samba -> punchfs -> rclone nigdy nie był trwały:

  • rclone mount ignoruje fsync (cmd/mount/file.go Fsync = return nil),
  • metadane cache (vfsMeta, w tym Rs = zakresy obecne w cache) zapisuje bez fsync, a po restarcie rclone dla pozycji Dirty dociąga brakujące zakresy z Drive i nadpisuje nimi nowe dane.

Zmiany

A. Trwały fsync

  • rclone-tm.service: rclone mount -> rclone mount2 (wszystkie obecne flagi przyjmowane w v1.75.1, sprawdzone w VM).
  • punchfs: opcja -o syncfs=<katalog>; lo_fsync i lo_fsyncdir po udanym fsync wołają syncfs() na katalogu (otwartym raz przy starcie), błąd -> errno. Bez opcji bez zmian. punchfs.service: syncfs=/var/cache/rclone.

B. Czyste zatrzymanie VM

  • Lima 2.3.0: hostagent łapie SIGINT i SIGTERM jednakowo; driver.Stop (vz) prosi gościa o zatrzymanie (RequestStop), czeka do 30 s, nigdy nie wymusza (źródło: lima-vm/lima v2.3.0, pkg/hostagent/hostagent.go, cmd/limactl/hostagent.go, pkg/driver/vz/vz_driver_darwin.go, pkg/instance/stop.go). limactl stop wysyła SIGINT do hostagenta i czeka do ok. 3 min.
  • launchd: ExitTimeOut = czas między SIGTERM a SIGKILL (man launchd.plist), domyślnie zależny od systemu.
  • Nowe cloudmachine-agent vm-hold: job vm-start startuje maszynę i zostaje przy życiu; na SIGTERM/SIGINT/SIGHUP robi limactl stop (limit 100 s), plist ma ExitTimeOut 120. Bez tego nic nie zatrzymywało VM przy wylogowaniu.

Dowód C (jednorazowa VM cmtest, usunięta)

Zapis częściowy (8 MiB od offsetu 8 MiB w istniejącym, niezbuforowanym pliku 64 MiB) przez punchfs + jeden fsync, natychmiast kill -9 hostagenta i procesu VZ, start, rclone ładuje cache, SHA-256 pliku.

konfiguracja Dirty po fsync Rs na dysku po zabiciu SHA
stara: mount, bez syncfs 8116 kB brak pliku vfsMeta rozjazd (dane z Drive, nowe 8 MiB utracone)
mount2, bez syncfs 660 kB 1 MiB z 8 MiB (nieaktualne) rozjazd od bajtu 1048577
nowa: mount2 + syncfs 252 kB 8 MiB (pełne) zgodny, dane całe

vm/punchfs/test-punchfs.sh (nowa sekcja): stara konfiguracja = 2 FAIL (Dirty 8152 kB, Rs 64 KiB zamiast 8 MiB), nowa = wszystko ok (Dirty 12 kB).

Nie zrobiono / uwagi

  • vm-hold nie był uruchomiony na żywo (logika pokryta testami jednostkowymi ze sztucznymi krokami; żywy test wymagałby zatrzymania cmsmb). Przy zamykaniu systemu macOS może skrócić czas launchd, więc to obrona w głąb, nie gwarancja; sedno to poprawka A.
  • Po scaleniu: install-launchd (nowy plist) i vm-update (nowe szablony i przebudowa punchfs, restart punchfs/rclone gdy bezczynne) trzeba wykonać ręcznie, nie zrobiono tu.
  • swift test: 420 testów zielonych.

🤖 Generated with Claude Code

mbeczynski and others added 2 commits November 12, 2026 10:38
…ymanie VM

Przyczyna uszkodzenia obrazu Time Machine po wyłączeniu Maca: rclone mount
ignoruje fsync, a metadane cache (vfsMeta/Rs) są zapisywane bez fsync, więc
twarde zabicie VM gubiło zapisy, które APFS uznał za trwałe.

- rclone-tm.service: rclone mount -> mount2 (Fsync dociera do Item.Sync)
- punchfs: opcja -o syncfs=<katalog>; lo_fsync/lo_fsyncdir wołają syncfs()
  na katalogu cache rclone, błąd -> errno
- vm-hold: job launchd vm-start trzyma się przy życiu i na SIGTERM robi
  limactl stop (ExitTimeOut 120 s)
- testy: VMDurabilityTests, VMHoldTests, rozszerzony test-punchfs.sh

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Po nieudanym limactl start (12.11: "did not receive an event with the running
status" po 10 min, a VM wstała) vm-hold dalej pyta Limę o stan, do 60 min,
bez zatrzymywania ani restartu maszyny. Błąd kończy dopiero stan
Stopped/Broken/brak instancji albo upływ limitu. SIGTERM przerywa czekanie.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mbeczynski

Copy link
Copy Markdown
Contributor Author

Dodano: vm-hold toleruje wolny start (Mac pod obciążeniem). Po nieudanym limactl start pyta Limę o stan co 30 s do 60 min i nie zatrzymuje ani nie restartuje maszyny; porażka dopiero przy Stopped/Broken/braku instancji lub po limicie. SIGTERM przerywa czekanie. 4 nowe testy, 424 zielone.

🤖 Generated with Claude Code

mbeczynski and others added 2 commits November 12, 2026 10:45
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ty, limit Limy

- punchfs: xattr kluczowane sciezka wzgledem source (mount2 daje inode z licznika procesu);
  unlink/rmdir kasuja, rename przenosi, stary uklad usuwany przy starcie
- punchfs.service: stara binarka bez -o syncfs konczy sie raz kodem 78, bez petli restartow
- test-punchfs.sh: wykrywa brak syncfs (ftrace ext4_sync_fs, brudne strony pliku danych),
  xattr po restarcie rclone/rename/unlink, bez zaleznosci od globalnego Dirty
- vm-hold: nieudany start nie konczy joba, SIGTERM anuluje start, budzet czasu w ExitTimeOut,
  przerywalne czekanie, prawdziwe statusy Limy, quiesce smbd+sync w gosciu, log o poprzednim
  zamknieciu, prawdziwy handler sygnalu zamiast SIG_IGN, eskalacja do SIGKILL przy anulowaniu
- install-launchd nie przeladowuje dzialajacego vm-start
- limactl list --json pyta o jedna instancje; zrodla punchfs w jednym archiwum (limit 64 KiB
  JSON Limy przy dzialajacej maszynie)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mbeczynski

Copy link
Copy Markdown
Contributor Author

Poprawki po ocenie (071f1b5):

  • xattr: klucz = sciezka wzgledem source (mount2 daje inode z licznika procesu - potwierdzone pomiarem), unlink/rmdir kasuja, rename przenosi (rowniez nadpisanie), stary uklad inode usuwany przy starcie; test "attribute survives a restart of rclone".
  • vm-hold: nieudany start nie konczy joba, SIGTERM anuluje limactl start (SIGKILL po 5 s), budzet po SIGTERM 115 s w ExitTimeOut 120 s, przerywalne czekanie, prawdziwe statusy Limy (zatrzymuje wszystko poza Stopped), systemctl stop smbd; sync w gosciu przed limactl stop, log o poprzednim zamknieciu (sprawdzone na jednorazowej VM: czysty/twardy), sigaction zamiast SIG_IGN, testy TerminationSignals.
  • install-launchd nie przeladowuje dzialajacego vm-start.
  • test-punchfs.sh: wykrywa brak syncfs (ftrace ext4_sync_fs + brudne strony pliku danych; sprawdzone 3 konfiguracjami), bez zaleznosci od globalnego Dirty, find -print -quit.
  • punchfs.service: stara binarka konczy sie raz kodem 78, bez petli restartow.
  • Nowe znalezisko: limactl list --json dzialajacej maszyny pada ("bufio.Scanner: token too long") gdy JSON przekroczy 64 KiB; ten PR bez zmian przekroczylby to po vm-update. Zrodla punchfs jada teraz jednym archiwum (vm/punchfs/pack.sh, test pilnuje zgodnosci i limitu), a limactl list pyta o jedna instancje.
  • Pomiar fsync przez punchfs (jednorazowa VM, backend lokalny): mediana 1,0 -> 10,2 ms bez/z syncfs, p95 1,5 -> 61,6 ms; z 8 MB brudnych w tle p95 195 ms, max 460 ms; przepustowosc 256 MiB 638 -> 587 MiB/s (fsync co 16 MiB).

swift test 448 zielone, swift-format --strict czysty. Nie scalone, nie wdrozone; petla awaryjna APFS x20 to etap 3.

… vm/README.md

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mbeczynski

Copy link
Copy Markdown
Contributor Author

Etap 3: dowod twardego zabicia VM (a372047, skrypt vm/tests/crash-loop.sh, wyniki w vm/README.md).

Przebieg: sparsebundle APFS (pasma 32 MB) na SMB z jednorazowej VM, zapis z F_FULLFSYNC po kazdej operacji (tworzenie, nadpisanie, kasowanie), kill -9 hostagenta i procesu VZ w losowym momencie (5-40 s), start, ponowne podpiecie, fsck_apfs -n, porownanie kazdego pliku z ksiega zapisana na Macu.

konfiguracja iteracje fsck czysty pliki zgodne z ksiega
stara (rclone mount, bez syncfs) 8 8 0 (kazdy przebieg gubil zapisane trwale pliki, do ponad 100 plikow, zera w srodku)
nowa (mount2 + syncfs) 39 39 38
  • Stara konfiguracja: APFS strukturalnie spojny, ale tracil dane, ktore F_FULLFSYNC uznal za trwale (Dirty 90-175 MB przed zabiciem).
  • Nowa: jeden przebieg z 39 (iter. 13 pierwszej serii) mial 2 pliki z blednym skrotem przy czystym fsck; w kolejnych 25 przebiegach (z diagnostyka fragmentow) nie powtorzylo sie, przyczyny nie ustalilem.
  • punchfs -d podczas 25 s zapisu: 1000 zadan FSYNC na 14 zapisanych pasm (okolo 7 na F_FULLFSYNC), kazde zapisane pasmo mialo FSYNC po ostatnim WRITE, FLUSH tylko 1 (zamkniecie). Bariera "fsync wszystkich fd" nie jest potrzebna, nic nie zmieniam.
  • Ograniczenia: backend lokalny w VM (nie Drive), Mac pod obciazeniem, detach po zabiciu VM wisi do limitu 120 s (kazdy raz).

…jna iteracja

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mbeczynski
mbeczynski merged commit aca535e into main Nov 12, 2026
5 checks passed
@mbeczynski
mbeczynski deleted the fix/trwaly-fsync branch November 12, 2026 13:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant