fix(files): beschaedigte Downloads werden gemeldet und abgebrochen
Die Pruefsumme wurde beim Ausliefern mitgerechnet und das Ergebnis weggeworfen -- ein gekipptes Bit wurde erkannt und verschwiegen. Jetzt: Logeintrag mit Datei-ID, Speicherschluessel, erwarteter und gelesener Summe, dazu panic(http.ErrAbortHandler), den chis Recoverer ausdruecklich durchreicht. Damit das wirkt, werden die letzten 32 KiB zurueckgehalten. Sonst waere die Datei beim Erkennen laengst vollstaendig beim Empfaenger und der Abbruch kaeme zu spaet. Erst wenn die Summe stimmt, darf der Schluss hinterher; sonst bekommt die Gegenstelle weniger Bytes als angekuendigt und wertet das als Fehler statt als Datei. scripts/pruefsumme-selbsttest.py, 14 Pruefungen: kippt ein Bit auf der Platte (nicht im Programm), in der Mitte der Datei, damit es nicht schon an der Laenge auffaellt; zwei Groessen ueber und unter den 32 KiB; und zuerst die Gegenprobe, dass eine heile Datei durchkommt. **Dabei gefunden:** Der neue Logeintrag blieb aus, obwohl der Abbruch funktionierte. zerolog.Ctx liefert einen *abgeschalteten* Logger, wenn im Kontext keiner steckt -- und Aether legte nie einen hinein. Alle 34 log.Ctx-Aufrufe im Baum schrieben ins Nichts, darunter die Warnung ueber verwaiste Blobs beim Zurueckrollen einer Fassung. Behoben zweifach: zerolog.DefaultContextLogger beim Start (deckt auch Hintergrundarbeit ab) und ein Logger mit request_id im Anfragekontext. Zwei Tests halten es fest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c26bcba012
commit
2cc02941b9
7 files changed
+501
-10
No files matched your search
+61
-3
@@ -94,7 +94,65 @@ Zweitens ließe sich die Verbindung abbrechen, statt sie sauber zu beenden: Der
|
||||
Browser zeigt dann einen abgebrochenen Download statt einer heilen Datei mit
|
||||
falschem Inhalt. Das ist die ehrlichere Ausgabe.
|
||||
|
||||
**Vorschlag:** Logeintrag jetzt, Abbruch dazu. Beides zusammen unter zehn Zeilen.
|
||||
**✅ Erledigt am 12.09.2026.** Beides – und ein Drittes, das dabei herauskam.
|
||||
|
||||
Der Logeintrag nennt Datei-ID, Speicherschlüssel, erwartete und gelesene Summe.
|
||||
Dazu wird die Verbindung fallen gelassen (`panic(http.ErrAbortHandler)`, den
|
||||
chis Recoverer ausdrücklich durchreicht).
|
||||
|
||||
**Damit das überhaupt wirkt, mussten die letzten 32 KiB zurückgehalten
|
||||
werden.** Sonst wäre die Datei beim Erkennen des Fehlers längst vollständig
|
||||
beim Empfänger, und der Abbruch käme zu spät: Die Kopfzeilen sind draußen, die
|
||||
Bytes auch. Ein `tailWriter` reicht deshalb alles durch außer dem Schluss; erst
|
||||
wenn die Summe stimmt, darf der hinterher. Stimmt sie nicht, bekommt die
|
||||
Gegenstelle weniger Bytes als im `Content-Length` angekündigt – und wertet das
|
||||
als Fehler statt als Datei.
|
||||
|
||||
Geprüft in `scripts/pruefsumme-selbsttest.py` (14 Prüfungen): Der Test kippt
|
||||
ein Bit **auf der Platte**, nicht im Programm, und zwar in der Mitte der Datei,
|
||||
damit der Fehler nicht schon an der Länge auffällt. Zwei Größen – über und
|
||||
unter den 32 KiB, denn bei einer kleinen Datei wird gar nichts herausgegeben.
|
||||
Und er prüft zuerst, dass eine **heile** Datei vollständig durchkommt; ohne das
|
||||
bewiese der Rest nichts.
|
||||
|
||||
---
|
||||
|
||||
## 2b · Und der Fund dahinter: 34 Logaufrufe schrieben ins Nichts
|
||||
|
||||
**Schwere: hoch, indirekt. Ein Fehler, der andere Fehler versteckt.**
|
||||
|
||||
Der neue Logeintrag blieb beim ersten Testlauf aus, obwohl der Abbruch
|
||||
funktionierte. Ursache:
|
||||
|
||||
```go
|
||||
func Ctx(ctx context.Context) *Logger {
|
||||
if l, ok := ctx.Value(ctxKey{}).(*Logger); ok { return l }
|
||||
else if l = DefaultContextLogger; l != nil { return l }
|
||||
return disabledLogger // ← hier landete alles
|
||||
}
|
||||
```
|
||||
|
||||
`zerolog.Ctx` liefert einen **abgeschalteten** Logger, wenn im Kontext keiner
|
||||
steckt und kein Standard gesetzt ist. Aethers `middleware.Logger` hängte nie
|
||||
einen hinein, und `DefaultContextLogger` war nie gesetzt.
|
||||
|
||||
**Alle 34 `log.Ctx(...)`-Aufrufe im Baum schrieben damit ins Nichts.** Darunter:
|
||||
|
||||
* die Warnung, dass beim Zurückrollen einer Fassung ein Blob verwaist ist
|
||||
(`files/handler.go`, `files/replace.go`, `handler_versions.go`)
|
||||
* gescheiterte Suchen
|
||||
* Fehler der Speicherbereinigung
|
||||
* die neue Meldung über beschädigte Dateien
|
||||
|
||||
Das ist die unangenehmste Art Fehler: Der Aufruf sieht richtig aus, `go vet`
|
||||
schweigt, und auffallen kann es nur, wenn man den Fehler herbeiführt, den die
|
||||
Zeile melden soll.
|
||||
|
||||
**Behoben zweifach:** `zerolog.DefaultContextLogger` beim Start gesetzt (deckt
|
||||
auch Hintergrundarbeit mit eigenem Kontext ab), und `middleware.Logger` hängt
|
||||
zusätzlich einen Logger mit `request_id` in den Anfragekontext – damit bei zwei
|
||||
gleichzeitigen Downloads erkennbar bleibt, welche Meldung zu welchem gehört.
|
||||
Zwei Tests in `pkg/middleware/logger_test.go` halten es fest.
|
||||
|
||||
---
|
||||
|
||||
@@ -163,8 +221,8 @@ gibt:
|
||||
## Reihenfolge, die ich vorschlagen würde
|
||||
|
||||
1. ~~`recover` in die Hintergrund-Goroutinen~~ ✅ 12.09.2026
|
||||
2. **Prüfsummen-Abweichung melden** (Punkt 2). Zehn Zeilen für eine Auskunft,
|
||||
die es sonst nie gibt.
|
||||
2. ~~Prüfsummen-Abweichung melden~~ ✅ 12.09.2026 – und dabei fiel auf, dass
|
||||
**alle** kontextgebundenen Logzeilen ins Nichts gingen (2b).
|
||||
3. **Tests für `documents`** (Punkt 3). Das Paket, dessen stilles Versagen am
|
||||
teuersten ist: Die Suche findet dann einfach nichts, und das sieht aus wie
|
||||
„ist nicht drin".
|
||||
Reference in new issue
Block a user