backup: gegenpruefen, nachholen, und endlich Bescheid sagen
Deploy / check (push) Canceled after 0s
Deploy / deploy (push) Canceled after 0s

Befund 2, 3 und 4 aus docs/befunde-2026-08-12.md. Befund 1 bleibt offen, Befund
5 war falsch und ist zurueckgezogen.

DER BOT SAGT JETZT BESCHEID. src/melden.js ist neu und enthaelt, was vorher in
watchdog.js eingeschlossen war: dmAdmin. Genau deshalb hat alles andere im Bot
geschwiegen oder in die Konsole geschrieben -- und eine Konsolenzeile in einem
Container liest niemand.

Dazu dmAdminEinmalig: meldet nur bei ZUSTANDSWECHSEL. Sonst wuerde eine
Pruefung, die alle zehn Minuten laeuft, denselben Ausfall alle zehn Minuten
melden, und nach der dritten DM liest man sie nicht mehr. Der Merker steht in
den Einstellungen und nicht im Arbeitsspeicher -- ein Bot, der nach jedem
Update neu startet, haette sonst nach jedem Update wieder eine frische Meinung.
Gemessen gegen eine KOPIE der Datenbank, sechs Schritte, alle wie beabsichtigt.

DER WACHHUND AUF last_backup. pruefeSicherung() meldet, wenn die letzte
Sicherung aelter als 26 Stunden ist, wenn sie fehlgeschlagen ist, oder wenn es
nie eine gab. Im Fehlerfall steht NICHT last_backup als Zeitpunkt im Embed: der
wird nur bei Erfolg gesetzt, dort staende also der letzte GUTE Lauf, und das
liesse die Sicherung frischer aussehen als sie ist.

DER TAG FAELLT NICHT MEHR AUS. Vorher "if (now.getHours() !== 3) return" -- wer
waehrend dieser einen Stunde unten war, hatte den Tag verloren, und ein Bot,
der nach jedem Update neu startet, ist genau dieser Fall. Jetzt zaehlt nur: es
ist nach 03:00 und heute war noch keine.

DAS ARCHIV WIRD AUSGEPACKT UND GEZAEHLT. integrity_check plus Tabellenzahl
gegen die laufende Datenbank. Faellt das durch, wird das Archiv GELOESCHT (im
Sicherungsordner saehe es sonst aus wie eine Sicherung), die Rotation laeuft
nicht, und last_backup bleibt stehen.

An echten Archiven gemessen, mit zwei Gegenproben, damit die Pruefung nicht nur
"ja" sagen kann:

    2026-08-10  817 KB  54 Tabellen  86 ms
    2026-08-11  886 KB  54 Tabellen  64 ms
    2026-08-12  970 KB  55 Tabellen  78 ms   (laufend: 55)
    halbes gzip -> "unexpected end of file"
    Muell       -> "incorrect header check"

Die 54 gegen 55 sind kein Fehler, sondern eine gewachsene Tabelle zwischen dem
11. und dem 12. Verglichen wird zeitgleich, also stoert das nicht -- wissen
sollte man es, bevor jemand alte Archive gegen die heutige Zahl haelt.

BEFUND 5 WAR FALSCH. pruneMessageCache wird sehr wohl aufgerufen, taeglich, in
mod-tools.js:239 -- und stand schon zum Zeitpunkt der Durchsicht dort. Gezaehlt
wurden "Aufrufe ausserhalb von db.js", und die Sammel-Importzeile ist als
Import durchgegangen, waehrend der Aufruf zwanzig Zeilen tiefer nicht mitkam.
Das ist genau der Fehler, den zu vermeiden dieser Bericht dasteht; die anderen
vier sind deshalb einzeln nachgemessen worden und stimmen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 14:47:09 +02:00
co-authored by Claude Opus 5
parent 1808b7c40b
commit 93156e5205
4 changed files with 372 additions and 32 deletions
+116 -12
View File
@@ -128,22 +128,39 @@ Ein Backup, das man nie zurückgespielt hat, ist keins.
---
## 5 · `pruneMessageCache` wird nie aufgerufen
## ~~5 · `pruneMessageCache` wird nie aufgerufen~~ — FALSCH, zurückgezogen am 12.08.2026
```
prunePlayerHistory 1 Aufruf ausserhalb db.js
pruneWatchdogHistory 1
pruneHeartbeat 1
pruneIncidents 1
pruneMessageCache 0 <-
**Dieser Befund war falsch.** Der Aufruf steht in `src/bot/mod-tools.js:239`, in
`registerModTools`, mit eigenem Kommentar — und er stand schon zum Zeitpunkt der
Durchsicht dort (nachgesehen in `git show 1808b7c:src/bot/mod-tools.js`):
```js
const aufraeumen = () => {
const weg = pruneMessageCache(Math.max(0, tuning('modlog_cache_days')));
if (weg > 0) console.log(`[modlog] ${weg} alte Nachrichten vergessen`);
};
aufraeumen();
setInterval(aufraeumen, 86_400_000);
```
Exakt dasselbe Muster, das in `d4rk_media` bei `pruneEvents` gefunden wurde: die
Funktion ist da, die Aufbewahrungsfrist ist dokumentiert, der Aufruf fehlt.
Täglich, mit Frist aus der Feineinstellung. **Es ist nichts zu tun.**
**Heute harmlos** — die Tabelle hat 18 Zeilen. Aber die Frist greift nicht, und
wenn der Nachrichten-Zwischenspeicher je stärker benutzt wird, wächst er ohne
Ende.
Wie es passieren konnte: gezählt wurden „Aufrufe außerhalb von `db.js`" — und
`mod-tools.js` importiert die Funktion in einer Sammelzeile mit fünf anderen
Namen. Beim Zählen ist die Zeile als Import durchgegangen und der Aufruf
zwanzig Zeilen weiter unten nicht mitgezählt worden.
**Das ist genau der Fehler, den zu vermeiden dieser Bericht dasteht:** eine
plausible Zahl, die niemand am Gegenstand nachgeprüft hat. Die anderen vier
Befunde sind deshalb am 12.08.2026 alle noch einmal einzeln nachgemessen worden
— sie stimmen:
| Befund | Nachgemessen |
|---|---|
| 1 · Sicherung ohne sicheren Ort | `backup_channel_id` ist **leer** — der Offsite-Zweig lief nie |
| 2 · Ausbleiben fällt niemandem auf | `last_backup` kommt an drei Stellen vor: schreiben, Tagesbremse, anzeigen. **Nirgends ein Vergleich mit heute.** |
| 3 · Zu groß endet in der Konsole | `console.warn` in `backup.js`, unverändert |
| 4 · Archiv nie gegengeprüft | kein Auspacken, kein Öffnen, kein Zählen im ganzen Modul |
---
@@ -189,3 +206,90 @@ eine Kopie neben dem Original, deren Ausfall unbemerkt bliebe.
Der billigste Schritt ist `backup_channel_id` zu setzen — ein Klick, und der
vorgesehene Weg funktioniert. Der solide Schritt ist derselbe wie bei
`d4rk_media`: abholen, prüfen, in die Nextcloud, und ein Wachhund darauf.
---
## Was am 12.08.2026 daraus gebaut wurde
**Befund 2, 3 und 4 sind erledigt** — Befund 1 zur Hälfte (siehe unten).
### Der Bot sagt jetzt Bescheid
`src/melden.js` ist neu und enthält das, was vorher in `watchdog.js`
eingeschlossen war: `dmAdmin`. Deshalb hat alles andere im Bot geschwiegen oder
in die Konsole geschrieben — **eine Konsolenzeile in einem Container liest
niemand.**
Dazu `dmAdminEinmalig(client, schluessel, zustand, …)`: meldet nur, wenn sich
der Zustand *geändert* hat. Ohne das würde eine Prüfung, die alle zehn Minuten
läuft, denselben Ausfall alle zehn Minuten melden — nach der dritten DM liest
man sie nicht mehr. Der Merker steht in den Einstellungen und nicht im
Arbeitsspeicher: ein Bot, der nach jedem Update neu startet, hätte sonst nach
jedem Update wieder eine frische Meinung.
Nachgemessen gegen eine **Kopie** der Datenbank, sechs Schritte:
```
1. Ausfall zum ersten Mal gebaut: 1 Merker: "alt:…T03"
2. derselbe Ausfall noch zweimal gebaut: 0 Merker: "alt:…T03"
3. ANDERER Ausfall gebaut: 1 Merker: "fehler:…T09"
4. wieder in Ordnung (null) gebaut: 0 Merker: ""
5. derselbe Ausfall danach erneut gebaut: 1 Merker: "alt:…T03"
```
### Der Wachhund auf `last_backup`
`pruefeSicherung(client)` läuft im selben Zehn-Minuten-Takt wie der Planer und
meldet, wenn die letzte Sicherung älter als **26 Stunden** ist, wenn die letzte
fehlgeschlagen ist, oder wenn es noch nie eine gab.
Beim Fehlerfall steht **nicht** `last_backup` als Zeitpunkt im Embed: der wird
nur bei Erfolg gesetzt, dort stünde also der letzte *gute* Lauf, und das ließe
die Sicherung frischer aussehen, als sie ist. Der Zeitpunkt des Fehlschlags
steht getrennt daneben (`last_backup_fehler_at`).
### Der Tag fällt nicht mehr aus
Vorher:
```js
if (now.getHours() !== 3) return;
```
Wer während dieser **einen Stunde** unten war, hatte den Tag verloren — und ein
Bot, der nach jedem Update neu startet, ist genau dieser Fall. Jetzt zählt nur
noch: es ist nach 03:00 und heute war noch keine. Der Lauf wird also nachgeholt.
### Das Archiv wird ausgepackt und gezählt
Nach dem Zippen wird das Archiv wieder ausgepackt, die Datenbank darin geöffnet,
`PRAGMA integrity_check` gefahren und die Tabellen gezählt — gegen die
**laufende** Datenbank. Fällt das durch, wird das Archiv **gelöscht** (im
Sicherungsordner sähe es sonst aus wie eine Sicherung), die Rotation läuft
nicht, und `last_backup` bleibt stehen.
An den echten Archiven gemessen, und mit zwei Gegenproben, damit die Prüfung
nicht nur „ja" sagen kann:
```
laufende Datenbank: 55 Tabellen
2026-08-10 817 KB 54 Tabellen, 86 Einstellungen 86 ms
2026-08-11 886 KB 54 Tabellen, 86 Einstellungen 64 ms
2026-08-12 970 KB 55 Tabellen, 86 Einstellungen 78 ms
halbes gzip -> abgelehnt: "unexpected end of file"
Müll -> abgelehnt: "incorrect header check"
```
Die 54 gegen 55 sind übrigens kein Fehler, sondern eine **gewachsene** Tabelle
zwischen dem 11. und dem 12. Verglichen wird zeitgleich — Archiv gegen die
Datenbank, aus der es gerade entstanden ist —, also stört das nicht. Wissen
sollte man es trotzdem, bevor jemand alte Archive gegen die heutige Zahl hält.
### Was offen bleibt: Befund 1
Die Sicherung liegt **weiterhin im selben Volume** wie die Datenbank. Der
eingebaute Ausweg (`backup_channel_id`) braucht eine Kanal-ID, die nur du geben
kannst — ein privater Kanal, in dem der Bot Dateien anhängen darf. Ein Klick in
Setup → Einstellungen, und der Zweig läuft.