syncova-backup/docs/chaos.md
Jerrit Fritzsche 610719c316
Some checks failed
CI / Backend (Go) (push) Failing after 3m7s
CI / Frontend (React/TypeScript) (push) Successful in 37s
CI / Sicherheitsprüfungen (push) Successful in 44s
Syncova Backups V1
Enterprise-Backup-, Recovery-, Verification-, Security- und
Monitoring-Plattform fuer Proxmox VE, Windows, Linux und Dateisysteme.

Der Leitsatz, der fast jede Entscheidung erklaert: Ein Backup gilt erst als
vertrauenswuerdig, wenn Integritaet geprueft und Wiederherstellbarkeit
nachgewiesen wurde. Deshalb steigt ein Wiederherstellungspunkt erst nach einem
tatsaechlich durchgefuehrten Restore-Test auf "recoverable", und Unbekanntes
geht in keine Bewertung als "gut" ein.

Umfang (Phasen 0-23):

- Repository Engine: inhaltsadressierte Bloecke, atomares Commit-Protokoll,
  Katalogaufbau allein aus den Manifesten — ohne Datenbank
- Backup Engine: inhaltsabhaengiges Chunking, Deduplizierung trotz
  Verschluesselung, zstd, AES-256-GCM, Streaming mit Gegendruck
- Agenten fuer Windows und Linux mit Auftragsabholung (Pull-Modell)
- Proxmox-Provider mit beiden Zugriffswegen auf die Sicherungsarchive
- Scheduler, Recovery Engine mit Pruefpunkt, Verification, Unveraenderlichkeit
- Weboberflaeche, Kennzahlen, Meldungen, Berichte, Security Center,
  Ransomware-Heuristik (meldet, handelt nie)
- Disaster Recovery, Haertung, Leistungsmessung, Chaos Testing
- Eingefrorene Vertraege fuer API, Migrationen, Backup-Format und Repository
- Auslieferungspaket fuer linux/amd64, linux/arm64 und windows/amd64

Nicht enthalten und als solches gekennzeichnet: Kapazitaetsprognose, Backup
Copy, Changed Block Tracking bei Proxmox, erweiterte Attribute und ACLs.

Gebaut, aber nie auf echter Hardware gefahren: der Windows-Dienst, die
systemd-Einheit und der verpflichtende Proxmox-Meilenstein — ob eine
wiederhergestellte VM startet, ist ungeprueft. Einzelheiten in CHANGELOG.md
und docs/release-candidate.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:10:54 +02:00

6.2 KiB

Chaos Testing (Phase 21)

Die Vorgabe des Plans (§23) steht in einem Satz:

Every failure must produce a controlled result.

Das ist keine Aufforderung, Fehler zu vermeiden — Platten laufen voll, Datenbanken fallen aus, Prozesse werden abgeschossen. Es ist die Aufforderung, dass jeder dieser Fälle vorhersagbar endet.

Was „kontrolliert" heißt

Vier Bedingungen, alle vier prüfbar, und drei von vier genügen nicht:

  1. Der Fehler wird gemeldet, nicht verschluckt.
  2. Er ist klassifiziert — sonst kann niemand entscheiden, ob eine Wiederholung sinnvoll ist.
  3. Es bleibt kein sichtbares, unvollständiges Backup zurück.
  4. Der Zustand danach ist konsistent.

Die dritte ist die wichtigste. Ein abgebrochener Lauf, der nichts hinterlässt, ist ein Ärgernis. Ein abgebrochener Lauf, der ein unvollständiges Backup als gültig hinterlässt, ist ein Datenverlust mit Zeitzünder: Er fällt erst an dem Tag auf, an dem jemand wiederherstellen will.

Die neun Störungen

Störung Ergebnis Wo nachgewiesen
Prozess abgeschossen kontrolliert Phase 18, Szenario D
Dienst neu gestartet kontrolliert Phase 18
Netz verloren kontrolliert Phase 5 (Agent-Backoff)
Repository nicht erreichbar kontrolliert Phase 14 (Meldungen)
Datenbank nicht erreichbar ein Fund hier
Platte voll ein Fund hier
Block beschädigt kontrolliert Phase 10
Manifest beschädigt kontrolliert hier
Stromausfall kontrolliert Phase 2 (atomares Schreiben)

Platte voll — echtes Dateisystem, kein simulierter Fehler

Ein 40-MiB-Dateisystem per hdiutil, darin ein Repository, darauf eine Sicherung von 120 MiB. Nach 33,8 MiB ist Schluss.

syncova-agent: die quelle "zu-gross.bin" konnte nicht gesichert werden:
  der chunk konnte nicht geschrieben werden: … no space left on device
Exit-Status: 1

Manifeste (= sichtbare Backups): 0
Chunks:                          32
Halbe Dateien (.tmp-*):          0

Kontrolliert in allen vier Punkten. Besonders: null Manifeste. Das Backup wird erst mit dem Manifest sichtbar (Schritt 5 von 7 des Commit-Protokolls) — es entsteht nie ein halbes, scheinbar gültiges Backup. Die 32 Blöcke bleiben liegen und werden von einem späteren Lauf wiederverwendet; das ist gewollt.

Der Fund: falsch klassifiziert und deshalb schädlich wiederholt

Der Schreibfehler kam als ALL_SOURCES_FAILED mit Klasse source an — und source wird wiederholt.

Die Quelle ist aber in Ordnung; das Repository ist voll. Jeder Wiederholungslauf legte weitere Blöcke ab und verschärfte die Lage. Ein Fehler, der falsch klassifiziert wird, führt hier nicht nur zu einem sinnlosen, sondern zu einem schädlichen Versuch.

Jetzt erkennt der Executor ENOSPC und meldet:

REPOSITORY_FULL (Klasse: configuration — nicht wiederholbar)
  Im Repository ist kein Platz mehr. Der Lauf wird nicht wiederholt: Ein neuer
  Versuch legte weitere Blöcke ab und verschärfte die Lage. Schaffen Sie Platz —
  über eine Aufbewahrungsregel oder mehr Speicher.

Die Erkennung läuft über den Fehlerwert des Betriebssystems, nicht über den Meldungstext: Ein Textvergleich bräche bei der ersten übersetzten Fehlermeldung, und zwar unbemerkt. Ein Test prüft, dass ENOSPC die vierfache Fehlerverschachtelung von Repository über Engine und Agent bis zum Executor übersteht.

Datenbank fällt im laufenden Betrieb aus

Container angehalten, Dienst läuft weiter.

/health/live:   HTTP 200   ← prüft bewusst keine Abhängigkeiten
/health/ready:  HTTP 503
GET /api/v1/health: 503 mit vollständigem Bericht im Datenteil
Dienstprozess: lebt
Erholung nach Rückkehr der Datenbank: 2 s, ohne Neustart

Genau das Verhalten, das Phase 0 entworfen hat: Liveness schaut nicht auf die Datenbank — sonst löste eine kurz nicht erreichbare Datenbank einen Prozessneustart aus und verschlimmerte den Ausfall.

Der Fund: „Sitzung abgelaufen" statt „Datenbank weg"

Eine fachliche Anfrage mit gültigem Token meldete:

UNAUTHENTICATED — Die Sitzung ist abgelaufen. Bitte erneut anmelden.

Die Tokenprüfung braucht die Datenbank. Fällt sie aus, scheitert jede Prüfung — und die Middleware deutete das als Anmeldeproblem. Der Betreiber meldet sich neu an, was ebenfalls scheitert, und sucht den Fehler bei der Anmeldung statt bei der Datenbank.

Nach der Definition oben: Der Fehler wurde zwar gemeldet, aber falsch klassifiziert — also nicht kontrolliert. Jetzt:

SERVICE_UNAVAILABLE — Die Anmeldung lässt sich derzeit nicht prüfen:
  Die Datenbank ist nicht erreichbar. Das ist kein Problem Ihrer Sitzung.

Auch hier über den Fehlerwert, nicht über den Text.

Manifest beschädigt

Ein Byte gekippt, danach die Datei halbiert. Drei Zugriffswege, drei kontrollierte Ergebnisse:

Zugriff Ergebnis
Integritätsprüfung „Das Repository ist nicht vollständig wiederherstellbar", mit Empfehlung
Wiederherstellung Bricht ab: „das manifest ist beschädigt" — 0 Dateien im Ziel
Katalog-Neuaufbau „Gefundene Backups: 0" — das beschädigte Manifest wird ausgeschlossen

Der Katalog zeigte das Backup zunächst weiter an. Das ist richtig und kein Mangel: Er ist ein Beschleuniger und wird nicht bei jeder Abfrage neu gebaut. Verbindlich sind die Manifeste — und jeder Weg, der tatsächlich auf sie zugreift, erkennt den Schaden.

Bekannte Grenzen

  • Netzverlust wurde nicht neu ausgelöst. Alle Repositories dieser Umgebung sind lokal. Der Nachweis stammt aus Phase 5 (Agent-Backoff mit wachsender Wartezeit) und Phase 18 (Abbruch mitten im Lauf, Fortsetzung am Prüfpunkt).
  • Der Stromausfall ist simuliert, nicht echt. SIGKILL beendet den Prozess ohne Aufräumen; das trifft den Anwendungsfall. Ein echter Stromausfall kann zusätzlich den Schreibzwischenspeicher der Platte verlieren — dagegen steht das fsync des Commit-Protokolls, aber nachgewiesen ist das hier nicht.
  • Die Störungen liefen einzeln. Zwei gleichzeitig — volle Platte und Datenbankausfall — wurden nicht geprüft.
  • Kein Dauerlauf. Ein Chaos-Werkzeug, das über Stunden zufällige Störungen auslöst, findet Fälle, die eine gezielte Prüfung nicht trifft. Das ist der nächste Schritt, nicht dieser.