Was kostet es, Historie spaeter moeglich zu halten? #22
Notifications
Due Date
No due date set.
Blocks
#19 Schreibverfahren: kein Datenverlust bei Absturz
RDietermann/dionotes-client
Reference: RDietermann/dionotes-client#22
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of #16
Question
Was kostet es, eine Historie später möglich zu halten — und schließt eine naive Schreib-Entscheidung sie aus?
Hintergrund: Beim Council zu diesem Vorhaben warnte eine Stimme, dass die Schreib-Entscheidung, einmal getroffen und ein halbes Jahr verdrahtet, Historie und Undo über Sitzungen hinweg nur noch als Aufsatz zulässt. Vorgeschlagen war ein Append-Log neben der Notiz, aus dem die
.mdals lesbare Sicht materialisiert wird.Historie ist nicht geplant. Dieses Ticket beschafft nur die Fakten, damit die Schreib-Entscheidung in #19 bewusst statt versehentlich fällt.
Konkret zu klären:
.mdvon außen ändert, ohne dass das Log es mitbekommt?Zielgröße: eine belastbare Antwort auf die letzte Frage, mit Begründung. Wenn Historie jederzeit nachrüstbar ist, ist der Einwand entkräftet und #19 kann frei entscheiden.
Antwort: Nein — Historie ist jederzeit nachruestbar. Der Einwand ist entkraeftet, #19 kann frei entscheiden.
Recherche liegt in
docs/research/historie-nachruesten.mdauf Branchresearch/historie(nicht gepusht).Die Kernfrage
Ein Schreibverfahren "temporaere Datei schreiben, dann umbenennen" schliesst eine spaetere Historie nicht aus — es ist im Gegenteil das guenstigere der beiden zur Wahl stehenden Verfahren.
Historie braucht vier Dinge: einen Ausloeser, die Vorfassung, einen Ablageort, eine Aufraeumregel. Nur die ersten beiden haengen ueberhaupt am Schreibverfahren — und beide fallen bei temp+rename ohnehin an: Der Ausloeser ist der Aufruf der Schreibfunktion, und die Vorfassung liegt im Moment vor dem Umbenennen noch unversehrt unter ihrem Pfad. Man muss sie nur lesen, bevor man umbenennt.
Ein direktes
fs::writezerstoert die Vorfassung dagegen nach dem ersten Byte. Wer dort Historie nachruestet, muss die Datei zusaetzlich vorher lesen. Also: Die Entscheidung, die #19 zu treffen hat, macht Historie nicht teurer, sondern billiger.Was Historie tatsaechlich teuer machen wuerde
Nicht das Wie des Schreibens, sondern das Wo: mehrere Schreibstellen im Code. Wenn Autosave hier schreibt, "Notiz verlassen" dort und "Fenster schliessen" an dritter Stelle, muss Historie an jeder Stelle nachgezogen werden, und jede vergessene ist ein Loch, das man erst durch einen Bug bemerkt.
Das ist die einzige echte Auflage an #19 — und sie kostet heute exakt nichts, weil es aktuell ueberhaupt keine Schreibstelle im Code gibt (verifiziert: keine Fundstelle fuer
fs::/write/File::untersrc/).Die Asymmetrie laeuft andersherum als behauptet
Wer sich die Option offenhalten will, haelt sie gerade dadurch offen, dass er den Append-Log nicht baut. Der Log ist die Einbahnstrasse, nicht das schlichte Schreiben.
Wie die Vergleichbaren es loesen (alles gegen Primaerquellen)
%APPDATA%\Obsidian\, ausserhalb des Vaultrevisionsin SQLite (es gibt keine.md)diff-match-patchlogseq/bak/<datei>/im Graph-Ordner.stversions/im Sync-OrdnerDrei Befunde stechen heraus:
.mdin einem Ordner — und die Historie ist ein nachtraeglich aufgesetztes Core-Plugin, das mit dem Schreibverfahren des Kerns nichts zu tun hat und komplett ausserhalb des Vault lebt. Genau das Modell "Historie ist eine Schicht ueber dem Schreiben", in Produktion vorgefuehrt.Der Append-Log konkret
.mdvon aussen, weiss der Log nichts davon — und ueberschreibt die Aenderung beim naechsten Materialisieren stillschweigend. Beim "Datei ist die Wahrheit"-Modell gibt es dieses Problem gar nicht: eine veraltete Sicherungskopie ist eine weniger nuetzliche Sicherungskopie, kein inkonsistenter Zustand..mdwird zur Ausgabe; eine Aenderung an ihr ist wirkungslos. Der Nutzer darf seine Dateien dann nur noch lesen, nicht mehr bearbeiten.Die billigeren Mittelwege (alle nachruestbar, keiner verlangt heute etwas)
.git/, >=3 lose Objekte pro Commitgit gc, teuergixodergit2Zu Git:
gc.auto= 6700 lose Objekte, also ~2.200 Commits bis zum ersten automatischen Repack — bei "ein Commit pro Speichern" mit Autosave eine Frage von Wochen. Bemerkenswert: Zettlr empfiehlt Git nicht, Logseq nutzt es nur lesend und setzt voraus, dass der Nutzer es selbst eingerichtet hat. Niemand richtet dem Nutzer ein Repo ein.Belastbarkeit
.mdals aufgesetzte, vom Schreibverfahren entkoppelte Schicht. Ein Werkzeug mit derselben Ausgangslage hat das Problem nachweislich nachtraeglich geloest.Randbefunde fuer #19 und #20
Fielen bei der Recherche mit an, sind im Dokument ausfuehrlich belegt:
renameals atomar — das Wort kommt in beiden Doku-Seiten nicht vor. Microsoft empfiehlt fuer "document-like data" ausdruecklichReplaceFile, nichtMoveFileEx. Kein Rust-Crate nutzt das.ReplaceFileW-Remarks). Folge: ein "erstellt am" gehoert ins Frontmatter, nicht ins Dateisystem.ERROR_SHARING_VIOLATION, wenn Defender oder ein Sync-Client die Zieldatei ohneFILE_SHARE_DELETEoffen haelt.tempfile::NamedTempFile::persistgibt die Temp-Datei imPersistErrorzurueck — genau fuer den Retry gebaut.Fazit
Der Einwand aus dem Council trifft eine reale Gefahr, aber am falschen Ort. #19 darf allein nach seinen eigenen Kriterien entscheiden — Absturzsicherheit, Sichtbarkeit der temporaeren Datei, Dateirechte, Aufraeumen. Historie legt genau eine Auflage auf, und die ist heute gratis: eine einzige Funktion, durch die alles Schreiben laeuft.
Ticket bleibt offen.
Nachtrag: Die Append-Log-Kosten sind jetzt belegt
Im ersten Kommentar stand, konkrete Groessenzahlen fuer CRDT-/Append-Log-Bibliotheken haetten sich nicht belegen lassen. Die Recherche dazu ist nachgekommen und war ergiebig. Die Kernantwort auf #22 aendert sich nicht — sie wird an einer Stelle sogar haerter. Dokument aktualisiert, Commit
66bff57aufresearch/historie.1. Die Groessenzahl: der Unterschied zwischen 1,5x und 70x heisst Compaction
Aus dem offiziellen Benchmark-Repo des Yjs-Autors Kevin Jahns (dmonad/crdt-benchmarks), B4-Trace mit 182.315 Einfuegungen, 77.463 Loeschungen, 104.852 Zeichen Endtext:
Das ist eine zweigeteilte Aussage, und beide Haelften sind wichtig:
2. Und genau dieses Zusammenfassen ist bei CRDT-Logs nicht vorgesehen
Das ist der eigentliche Nachtrag, und er kommt aus Maintainer-Mund. Peter van Hardenberg (Automerge) in Issue #799, woertlich:
"Shallow clone" und "cherry pick" nennt er nuetzlich und "not yet implemented"; nachgefasst 2024: "I don't know when it will happen." Der Automerge-2.0-Blogpost kuendigt dasselbe seit Januar 2023 an: "the best way to remove something from an Automerge document's history is to recreate the document from scratch". Bis heute nicht geliefert. (Issue #348 wurde 2026 mit Verweis auf die neue Storage-Engine
hexanegeschlossen — die ist laut offiziellem Blogpost "around 2–9x faster", liefert also Kompression, keine Truncation.)Yjs begruendet es formal in der README:
Im Code bestaetigt: beim normalen Loeschen wird nur der
contentdurchContentDeleted(len)ersetzt — das komplette Item-Struct mitid,origin,rightOrigin,parentbleibt stehen. Und Updates zusammenfassen hilft nicht: "doesn't garbage-collect deleted content". Wer Snapshots will, muss GC ganz abschalten (der Code wirft sonst).Damit ist der Punkt "der Log ist die Einbahnstrasse" nicht mehr nur mein Architektur-Argument, sondern die dokumentierte Position der Bibliotheks-Autoren selbst.
3. Das Gegenargument, das fairerweise dazugehoert
Das offizielle Automerge-Cookbook schreibt:
Das ist ernst zu nehmen — es gilt fuer Kleppmanns Messfall: ein grosses, weitgehend linear getipptes Dokument mit wenigen Actor-IDs. Eine Notiz-App hat das umgekehrte Profil: viele kleine Dokumente, ueber Jahre in vielen kurzen Sitzungen angefasst. Ob die Oekonomie dort kippt, ist nicht belegt — die 70x-Zahl legt es nahe, ist aber am B4-Trace gemessen, nicht am Notiz-Profil. Steht so im Dokument, damit uns das Zitat nicht spaeter um die Ohren fliegt.
Ebenso fair: Die Yjs-FAQ sagt "Size is very rarely a practical problem as long as you deal with human-entered text input." Deshalb steht der Ausschlussgrund weiterhin beim Zwei-Wahrheiten-Problem und nicht bei der Groesse.
4. Sidecar-Dateien in Sync-Ordnern: zwei belegte Fallstricke
Bezeichnend: Apple empfiehlt fuer mehrteilige Dokumente File Packages statt nebeneinanderliegender Dateien — fuer uns untauglich, weil die
.mddann keine eigenstaendige Datei mehr waere.Was NICHT belegt ist: dass die blosse Verdopplung der Dateizahl schadet. Die harten Limits sprechen eher dagegen (Dropbox 300.000 Dateien / 1 Mio. Writes pro 24 h; OneDrive 300.000 Items) — bei unseren Workspace-Groessen unkritisch.
Drei naheliegende Belege habe ich gegengeprueft und verworfen: Zoteros "extremely likely to corrupt your database" betrifft SQLite und File Locking; git-annex' Dropbox-Warnung beruht auf Case-Insensitivity und Symlinks; fuer DVC gibt es keine offizielle Warnung. Keiner davon taugt als Beleg gegen Sidecars.
Fazit unveraendert
Historie bleibt jederzeit nachruestbar, temp+rename schliesst sie nicht aus, und die einzige Auflage an #19 ist weiterhin: genau eine Funktion, durch die alles Schreiben laeuft. Neu ist nur, dass der Append-Log jetzt auch mit Zahlen und Maintainer-Zitaten als die teure, schwer umkehrbare Variante dasteht — statt nur mit meiner Argumentation.
Ticket bleibt offen.