Zum Inhalt springen

Atomarität und Wiederherstellung

AtomicFilesystem::write_atomic() schreibt in eine temporäre Datei, leert den Puffer und führt für diesen Datei-Handle fsync aus, benennt die Datei in den Zielpfad um und versucht, auch das übergeordnete Verzeichnis mit fsync zu sichern. Bei fehlgeschlagenen Schreibvorgängen wird die jeweilige temporäre Datei entfernt.

Schreibende Store-Instanzen registrieren unter .storh/writers/ eine Markierung pro Prozess und entfernen sie beim ordnungsgemäßen Herunterfahren. Beim Öffnen eines Stores werden diese Markierungen geprüft: Solange alle zugehörigen Prozesse laufen (oder keine Markierungen vorhanden sind), ist das Öffnen in konstanter Zeit möglich. Ist der Prozess einer Markierung beendet, ist ein Schreibvorgang abgestürzt. Dann durchsucht das Öffnen die Sammlung nach verwaisten temporären Dateien und entfernt die veraltete Markierung. repair() durchsucht sie immer.

Die DocStore-Reparatur verschiebt beschädigte oder abgeschnittene Datensatzdateien unter .storh/corrupt/ in Quarantäne und erstellt die sekundären Indizes anhand der verbleibenden gültigen Datensätze neu. Bei der DocStore-Prüfung werden außerdem die Indexdateien mit einem vollständigen Scan aller Datensätze verglichen, damit fehlende oder veraltete Indexeinträge gemeldet werden, bevor sie als intakt gelten.

Append-only-Engines schreiben ihre Log-Datei-Handles mit flush und fsync auf die Festplatte, bevor sie Ereignisse im Arbeitsspeicher als übernommen betrachten. Die Haltbarkeit bei Stromausfall hängt weiterhin davon ab, ob das zugrunde liegende Dateisystem und die Mount-Optionen Datei- und Verzeichnissynchronisierungen unterstützen.

Das segmentierte Log schreibt jede Zeile im folgenden Format:

Text
<byte-length>\t<xxh32>\t<json>\n

Beim Lesen werden Byte-Länge und Prüfsumme überprüft. Bei der Wiederherstellung wird jedes Segment gescannt und die Datei nach der letzten vollständigen gültigen Zeile abgeschnitten, wodurch unvollständige Schreibvorgänge am Ende entfernt werden. Bei der Prüfung wird der aktive abgeleitete Zustandsindex mit einer unabhängigen Wiedergabe der Segmente verglichen. So werden extern geänderte oder veraltete Zustände gemeldet, bevor sie bei der Reparatur neu erstellt werden.

Bei der Queue-Prüfung werden alle dauerhaft gespeicherten Queue-Ereignisse decodiert und der aktuelle Status von ausstehenden, in Bearbeitung befindlichen und erledigten Jobs sowie Payloads und Reihenfolge der Claims mit einer unabhängigen Wiedergabe des Logs verglichen. Bei der Reparatur wird dieser Status anhand von queue.log neu erstellt und unvollständige Ereignisse am Ende werden entfernt. Claims und Abschlüsse halten die Queue-Sperre, während sie den Status mit dem dauerhaften Log synchronisieren. So können mehrere Worker-Prozesse nicht denselben ausstehenden Job beanspruchen.

Bei der Komprimierung werden neue Segmentdateien compact-* geschrieben und anschließend das Manifest aktualisiert. Wird ein Prozess beendet, bevor das Manifest ausgetauscht wurde, entfernt das erneute Öffnen nicht referenzierte Komprimierungsausgaben und fährt mit dem alten Manifest fort. Bei abgeschlossenen Komprimierungen bleiben alte versiegelte Segmente absichtlich bestehen, damit Leser, die sie bereits streamen, weiter darauf zugreifen können.