Zum Inhalt

Backups und das Wiederherstellungsfenster

Die Historie eines Ziels ist eine Kette: Vollsicherungen nach Zeitplan und dazwischen ein fortlaufender Änderungsstrom (Postgres-WAL, MySQL-Binlog). Jede Sekunde, die von einer Vollsicherung und den darauf folgenden Änderungen abgedeckt ist, lässt sich wiederherstellen. Diese Spanne ist das Point-in-Time-Fenster.

gantt
  dateFormat  YYYY-MM-DD HH:mm
  axisFormat  %a %d
  section Full backups
  daily full      :milestone, 2026-10-01 02:07, 0d
  daily full      :milestone, 2026-10-02 02:07, 0d
  daily full      :milestone, 2026-10-03 02:07, 0d
  daily full      :milestone, 2026-10-04 02:07, 0d
  section Change stream
  WAL / binlog segments :active, 2026-10-01 02:07, 2026-10-04 18:00

Vollsicherungen

Engine Modus Werkzeug Wiederherstellung
Postgres 15–17 physical (Standard) pg_basebackup -Ft -X fetch über das Replikationsprotokoll Base-Backup + WAL-Replay auf die Sekunde
Postgres 15–17 logical pg_dump -Fc, ein Objekt pro Datenbank; Tabellenausschlüsse und selektive Exporte pg_restore, plus WAL-Replay über eine physische Kette, falls vorhanden
MySQL 8.4 logical mysqldump --single-transaction --source-data=2 (Routinen, Events, Trigger) Laden + Binlog-Replay auf die Sekunde

Jedes Backup erfasst ein Inventar: Datenbanken, Tabellen, Spalten, Indizes, Zeilenschätzungen und Größen sowie die Migrationsversion, wenn eine Migrationstabelle existiert. Genau das vergleichen die Konsole und tenax diff.

Der Änderungsstrom

Jedes Ziel mit Point-in-Time-Recovery hat einen Streamer:

  • Postgres: der eigene WAL-Receiver von Tenax auf einem physischen Replikations-Slot (ADR 0005). Der Slot garantiert, dass kein WAL verloren geht, während der Streamer neu startet.
  • MySQL: mysqlbinlog --read-from-remote-server --raw --stop-never ab der Binlog-Position der letzten Vollsicherung.

Segmente werden hochgeladen, sobald sie vollständig sind, und das gerade geschriebene Segment wird alle partialInterval (Standard 60 s) hochgeladen, sodass auch die letzte Minute wiederherstellbar ist. Jedes Segment trägt eine Zusammenfassung: Commits, Inserts, Updates und Deletes pro Tabelle, DDL und optional maskierte Zeilenvorschauen.

Reißt der Stream ab (ein Slot geht verloren, ein Binlog wird gelöscht), markiert Tenax die Kette als unterbrochen, alarmiert (TenaxStreamChainBroken), beginnt eine neue Kette und erstellt sofort eine Vollsicherung (Grund chain-restart). Das alte Fenster bleibt bis zum Abbruch wiederherstellbar.

Aufbewahrung

Vollsicherungen werden nach Stufen aufbewahrt; Segmente nur so weit zurück, wie das Fenster sie braucht; die Garbage Collection entfernt alles andere, nie aber etwas Angeheftetes.

Preset Fenster Täglich Wöchentlich Monatlich Jährlich
prod 7 Tage 14 8 12 5
dev 3 Tage 7 4 3 0
critical 14 Tage 30 12 24 7

Die erste abgeschlossene Vollsicherung eines Tages ist dessen Tagessicherung, die einer ISO-Woche deren Wochensicherung, und so weiter. Ein Backup kann mehrere Stufen tragen; nichts wird kopiert. Pins (tenax pin <id> --reason …) halten ein Backup unabhängig von den Stufen. Die GC schreibt ihren Plan in den Bucket, bevor sie löscht, löscht Manifeste vor den Daten und räumt verwaiste und unvollständige Uploads nach einem Tag auf.

Speicherformat

<store prefix>/<org>/<project>/<namespace>/<target>/
  full/<backup-id>/manifest.json            # written last: the commit marker
  full/<backup-id>/data/<database>.zst.age  # dumps or the base backup tar
  full/<backup-id>/inventory.json.zst.age   # also readable with the catalog key
  full/<backup-id>/tags.json                # tiers, pins, verification results
  stream/…/<segment>.zst.age                # WAL / binlog segments and their summaries
  stream/position.json                      # the streamer's confirmed position
  • Komprimierung: zstd auf der Stufe der Policy, einmal, vor der Verschlüsselung; standardmäßig aktiv (ADR 0013). compression.algorithm: none speichert die Daten unverändert.
  • Verschlüsselung: Jedes Objekt wird auf dem Client mit age für die Empfänger des Stores verschlüsselt. Der Bucket sieht nie Klartext.
  • Integrität: Größe und SHA-256 jedes Objekts stehen im Manifest und werden bei jedem Lesen geprüft.
  • Commit: Ein Backup existiert erst, wenn manifest.json geschrieben ist. Ein Absturz davor hinterlässt Waisen, die die GC entfernt, nie ein Backup, das vollständig aussieht.

Laststeuerung

Backups konkurrieren nie blind mit der Produktion: Slots pro Host, pro Ziel und global, ein Bandbreitenlimit, eine Priorität, ein Jitter auf Zeitplänen und ein Lastschutz, der eine Vollsicherung verschiebt, solange der Server zu viele aktive Verbindungen, zu viel Replikationsverzögerung oder lang laufende Transaktionen hat. All das wird in der BackupPolicy eingestellt.