docs(savepoint): v3.14 - Durchsicht, mit den zwei eigenen Fehlschluessen
SAVEPOINT bekommt den Stand v3.14: der unwirksame Platten-Schutz zuerst (wichtigster Fund), dann die gemessenen Werte, das Gebaute, die vier toten Endpunkte als Entscheidungsvorlage - und ein Abschnitt "SOFORT ENTSCHEIDEN". Der laufende Akira-Encode wird beim Fertigwerden mit dem DEPLOYTEN, alten Code versuchen, 75 GB in 37 GB zu kopieren. keepOriginal steht auf true, und den Wert hat die Aufgabe beim Start gelesen - jetzt umstellen aendert daran nichts mehr. Der Savepoint nennt die drei Optionen mit Empfehlung. Zwei Richtigstellungen an v3.13, beide durch Messung: - "progress=99 ist ein Altwert aus dem Absturz" war falsch. Beide Startpfade setzen auf 0; der Wert kam frisch vom Scan-Durchlauf. Ein Bug, kein Ueberrest. - Die Erwartung, _original_aufheben werde nur warnen, war falsch - siehe EXDEV-Fund im vorigen Commit. Ehrlich als OFFEN markiert: Was die Platte am 25.07. mittags fuellte, ist NICHT belegt. Es gibt keine Kopier-Reste, kein original-Verzeichnis und keinen einzigen Log-Eintrag von _original_aufheben, das in beiden Zweigen loggt. Der Mechanismus ist jetzt bewiesen, sein Zuschlagen an jenem Tag nicht. Lieber offen lassen als eine dritte Vermutung aufstellen. AGENTS bekommt Etappe 19 und einen neuen Abschnitt, der das wiederkehrende Muster benennt: nicht aus einem Zustandswert auf einen Mechanismus schliessen - vier belegte Faelle. Plus die Umkehrung, die diese Sitzung gekostet hat: gleiches st_dev heisst NICHT gleicher Mount. Was ausprobierbar ist, wird ausprobiert statt vorhergesagt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+162
-1
@@ -1,6 +1,167 @@
|
||||
# SAVEPOINT — Rippy
|
||||
|
||||
## Aktueller Stand: v3.13 — ÜBERGABE (25.07.2026, 18:05)
|
||||
## Aktueller Stand: v3.14 — Durchsicht: vier Placebos und ein unwirksamer Schutz (25.07.2026, 18:40)
|
||||
|
||||
> Diese Sitzung hat nichts deployt (der Akira-Encode läuft — ein Rebuild
|
||||
> schießt ihn ab). Alles unten ist committet und die Ampel ist grün.
|
||||
> **Zuerst lesen: „SOFORT ENTSCHEIDEN" ganz unten.**
|
||||
|
||||
### DER WICHTIGSTE FUND: der Platten-Schutz aus `c065967` greift nicht
|
||||
|
||||
`_original_aufheben()` entschied per `os.stat().st_dev`, ob umgehängt oder
|
||||
kopiert werden muss. Auf der VM gemessen — **beides gleichzeitig wahr**:
|
||||
|
||||
```
|
||||
st_dev /app/temp = 2050
|
||||
st_dev /app/media = 2050 → identisch
|
||||
os.rename(...) → EXDEV, "Invalid cross-device link"
|
||||
```
|
||||
|
||||
Der Kernel vergleicht bei `rename()` den **Mount**, nicht das Gerät.
|
||||
`/app/temp` (Docker-Volume) und `/app/media` (Bind-Mount) sind zwei Mounts
|
||||
DERSELBEN ext4-Partition. Die Prüfung sah deshalb „gleiches Dateisystem",
|
||||
**übersprang die Platzprüfung**, und `shutil.move` kopierte doch — 75 GB bei
|
||||
37 GB frei. Der Schutz hätte genau den Schaden zugelassen, gegen den er
|
||||
gebaut wurde.
|
||||
|
||||
**Behoben:** `os.rename` wird jetzt VERSUCHT statt vorhergesagt. Klappt es,
|
||||
ist es umgehängt. Kommt EXDEV, steht die Kopie fest — und erst dann wird der
|
||||
Platz geprüft. Vier Tests dazu (`test_original_aufheben.py`), inklusive des
|
||||
Falls, der die Platte füllte.
|
||||
|
||||
### GEMESSEN, Stand 18:30 (alles per Befehl auf der VM geprüft)
|
||||
|
||||
- **Container:** alle 5 `Up`, api/postgres/redis `healthy`. api/ui/worker seit
|
||||
18:00:39 (Deploy von `8bb075c`), postgres/redis älter.
|
||||
- **Platte:** 148 G, 105 G belegt, 37 G frei = 75 G Rohschnitt + 4,9 G Medien
|
||||
(Evangelion) + ~25 G System. **Keine Kopier-Reste**, kein `original`-Ordner.
|
||||
- **Akira-Encode läuft:** HandBrake PID 40 seit 18:01:37, Preset
|
||||
`H.265 MKV 2160p60 4K`, **3,84 von 4 Kernen**. Leseposition im Quellstrom
|
||||
**1.145.940.149 von 79.604.951.639 Bytes = 1,44 %**.
|
||||
- **Restlaufzeit ≈ 28–55 h.** Zwei Messfenster ergaben 395 und 784 KB/s — das
|
||||
Tempo schwankt mit der Bildkomplexität, deshalb eine Spanne und keine Zahl.
|
||||
- **Die Ursache dafür ist neu und behebbar:** Die VM läuft auf dem generischen
|
||||
QEMU-CPU-Modell (`QEMU Virtual CPU version 2.5+`), `grep -c avx2
|
||||
/proc/cpuinfo` = **0**, nur bis `sse4_2`. x265 lebt von AVX2. Abhilfe:
|
||||
CPU-Typ von VM 106 in Proxmox auf `host` stellen (braucht VM-Neustart).
|
||||
- **HandBrake im Worker-Image kann:** `svt_av1`, `x264`, `x265` (je 10/12-bit),
|
||||
`mpeg4/2`, `VP8/9`, `theora` — und **keinen einzigen Hardware-Encoder**.
|
||||
- **Ampel grün** für `b526a0a` und `8bb075c` (Gitea-API abgefragt).
|
||||
|
||||
### GEBAUT — vier Placebos entfernt bzw. echt gemacht
|
||||
|
||||
1. **Fortschritt log statt Wahrheit.** `get_progress_from_line` matchte jede
|
||||
Zahl vor einem `%` — also auch HandBrakes **Scan-Durchlauf**, der VOR dem
|
||||
Encodieren bis 100 % hochläuft. Dazu warf `if progress > 0` alle echten
|
||||
Werte unter 1,00 % weg. Ergebnis: Anzeige klebte stundenlang auf 99 %.
|
||||
Jetzt wird nur die `Encoding:`-Zeile gelesen, `task N of M` mitgerechnet,
|
||||
und **-1 heißt „keine Angabe"** (Muster von `get_progress_from_prgv`).
|
||||
Formatstrings aus dem Binary gelesen, nicht geraten.
|
||||
**⚠️ Richtigstellung zu v3.13:** Dort steht, `progress=99` sei ein „Altwert
|
||||
aus dem Absturz". Falsch — beide Startpfade setzen auf 0, der Wert war
|
||||
frisch vom Scan-Durchlauf geschrieben. Es war ein Bug, kein Überrest.
|
||||
2. **„Automatischer Auswurf" tat nichts.** Die Einstellung (Standard: ein)
|
||||
wurde von niemandem gelesen: DVD/Blu-ray warfen **nie** aus, Audio-CDs
|
||||
**immer**, weil abcde `-x` fest verdrahtet bekam. Jetzt entscheidet die
|
||||
Einstellung beides (`wirf_disc_aus()` per CDROMEJECT-ioctl, Linux-guarded).
|
||||
3. **„Alle Tracks rippen" konnte nichts bewirken** — abcde bekommt keine
|
||||
Track-Auswahl und es gibt keine Oberfläche dafür. Schalter entfernt, an
|
||||
seiner Stelle steht jetzt die Wahrheit („wird immer vollständig gerippt").
|
||||
4. **Encoder-Auslese behauptete statt zu messen.** `cpu-x264`/`cpu-x265`
|
||||
standen fest verdrahtet drin („immer dabei") — ein Rip-Worker **ohne**
|
||||
HandBrake behauptete damit, komprimieren zu können. Und `vaapi` wurde
|
||||
allein wegen `/dev/dri` gemeldet, ohne zu prüfen, ob HandBrake das kann
|
||||
(dieses Image kann es nicht). Jetzt aus `HandBrakeCLI --help` geparst, plus
|
||||
**CPU-Modell, Kernzahl und Vektorbefehlsstufe** je Worker — mit sichtbarer
|
||||
Warnung im UI, wenn AVX2 fehlt. Genau die Angabe, deren Fehlen den
|
||||
55-Stunden-Encode unsichtbar machte.
|
||||
|
||||
### GEBAUT — Zombie-Erkennung (v3.13 Punkt 2 abgearbeitet)
|
||||
|
||||
`zombies.py`: Beim Worker-Start werden Jobs, die auf `ripping`/`transcoding`/
|
||||
`canceling` stehen, gegen Celerys `active`/`reserved`/`scheduled` gehalten und
|
||||
ehrlich auf `failed` gesetzt, wenn niemand daran arbeitet. Drei Sicherungen,
|
||||
weil ein falsch getöteter Job teurer ist als eine stehende Leiche:
|
||||
|
||||
- **nur beim Start** (da ist „es lief nichts" eindeutig),
|
||||
- **120 s Gnadenfrist** (Celery stellt unbestätigte Aufgaben erneut zu),
|
||||
- **Vollzähligkeit**: antworten weniger Knoten als laut Herzschlag online
|
||||
sind, wird NICHTS gewertet — sonst wäre der laufende Job eines
|
||||
beschäftigten Remote-Workers eine falsche Leiche.
|
||||
|
||||
13 Tests, unter anderem: „laufender Job wird nicht angetastet" und
|
||||
„schweigender Worker verhindert jedes Urteil".
|
||||
|
||||
### GEBAUT — Pfad-Prüfung gehärtet
|
||||
|
||||
Elf Stellen prüften mit nacktem `startswith(MEDIA_ROOT)`. `/app/media-boese/x`
|
||||
beginnt mit `/app/media`, liegt aber außerhalb — betroffen waren auch `/browse`
|
||||
und `/browse/mkdir`, wo der Pfad vom Nutzer kommt. Neuer Zwillings-Helfer
|
||||
`unter_wurzel()` in `api/main.py` und `worker/tasks.py`, alle elf Stellen
|
||||
umgestellt, Tests in beiden.
|
||||
|
||||
Nebenbefund dabei: `_zielbasis()` benutzte `os.path.normpath` — unter Windows
|
||||
werden daraus Backslashes und die Prüfung greift nicht mehr; das gewählte Ziel
|
||||
wäre still auf den Standard zurückgefallen. Genau die Falle, die
|
||||
`_arbeitsverzeichnis()` drei Zeilen weiter dokumentiert und mit `posixpath`
|
||||
vermeidet. Live war es nie (nur aus `rip_disc`, das auf Windows verriegelt
|
||||
ist), jetzt konsistent.
|
||||
|
||||
### GEFUNDEN, NICHT ANGETASTET — vier tote Endpunkte
|
||||
|
||||
Nirgends im UI referenziert (mechanisch gegengeprüft: alle `api.*`-Aufrufe
|
||||
gegen alle Routen):
|
||||
|
||||
| Endpunkt | Lage |
|
||||
|---|---|
|
||||
| `POST /jellyfin/format` | ersetzt durch `medien.py` im Worker; zieht `nfo_generator.py` + `image_downloader.py` in der API mit, die sonst niemand nutzt |
|
||||
| `POST /prescan` | ohne Aufrufer (die `PreScan`-Klasse selbst wird woanders sehr wohl gebraucht) |
|
||||
| `GET /stream/jobs` | niemand konsumiert ihn; der Kommentar behauptet einen „Fix 23.07.", der ihn funktionsfähig machte — gebraucht wird er trotzdem nicht |
|
||||
| `GET /worker-setup/windows-gui` | seit der `.exe` (v3.9) unreferenziert |
|
||||
|
||||
Bewusst nicht entfernt: `test_api_smoke.py` prüft `/prescan` als verdrahtete
|
||||
Route, und Entfernen ist eine Entscheidung, keine Reparatur. **Der Commander
|
||||
entscheidet.**
|
||||
|
||||
### NOCH OFFEN / EHRLICH UNGEKLÄRT
|
||||
|
||||
- **Was die Platte am 25.07. mittags füllte, ist nicht belegt.** Es gibt keine
|
||||
Kopier-Reste, kein `original`-Verzeichnis und **keinen einzigen Log-Eintrag
|
||||
von `_original_aufheben`** — das loggt in beiden Zweigen. Zwischen 10:21:50
|
||||
und 17:49 steht überhaupt nichts im Log. Der EXDEV-Mechanismus ist jetzt
|
||||
belegt (siehe oben), dass er DAMALS zuschlug, ist es nicht.
|
||||
- **`tracks:/dev/sr0` meldet `{"status":"done","tracks":[]}`** — null Titel für
|
||||
eine Disc, die MakeMKV mit `TCOUNT:5` öffnet. Nicht weiter verfolgt.
|
||||
- **Der Discord-Webhook und der MakeMKV-Key liegen im Klartext** in der
|
||||
settings-Tabelle. Für Heimnetz erwartbar, aber der Webhook ist eine echte
|
||||
Zugangsberechtigung.
|
||||
- **Zwei neue API-Tests laufen nur in der Ampel** — `test_api_smoke.py`
|
||||
überspringt sich unter Windows selbst (`main.py` braucht `fcntl`).
|
||||
|
||||
### SOFORT ENTSCHEIDEN
|
||||
|
||||
**Der laufende Job wird beim Fertigwerden mit dem DEPLOYTEN (alten) Code
|
||||
versuchen, 75 GB in 37 GB zu kopieren.** `keepOriginal` steht auf `true`, und
|
||||
den Wert hat die Aufgabe beim Start um 18:01:37 gelesen — ihn jetzt umzustellen
|
||||
ändert für diesen Lauf nichts mehr. Der Ablauf wäre: Platte läuft auf 100 %,
|
||||
dann ENOSPC, dann räumt der `except`-Zweig die Teilkopie weg. Selbstheilend —
|
||||
aber währenddessen ist die Systemplatte voll, und genau daran hat sich heute
|
||||
Mittag der Worker-Container verschluckt.
|
||||
|
||||
Die Optionen, in der Reihenfolge, die ich empfehle:
|
||||
|
||||
1. **Encode abbrechen, deployen, Kompression neu anstoßen.** Kostet die
|
||||
bisherigen ~30 Minuten (der Lauf ist bei 1,44 % — praktisch nichts) und
|
||||
bringt sofort alle Fixes live, inklusive des Platten-Schutzes und der
|
||||
ehrlichen Fortschrittsanzeige. Wer ohnehin über den CPU-Typ nachdenkt,
|
||||
erledigt das im selben Fenster.
|
||||
2. **Laufen lassen und in 1–2 Tagen deployen**, vorher die 75 GB von Hand
|
||||
wegräumen oder auf die NAS schieben, damit die Kopie nicht anlaufen kann.
|
||||
3. **Laufen lassen und den vollen Plattenlauf in Kauf nehmen.**
|
||||
|
||||
---
|
||||
|
||||
## Vorheriger Stand: v3.13 — ÜBERGABE (25.07.2026, 18:05)
|
||||
|
||||
> Dieser Block ist eine Übergabe an die nächste Sitzung. Er trennt bewusst
|
||||
> **gemessen** von **vermutet** — in dieser Sitzung wurden zwei Behauptungen
|
||||
|
||||
Reference in New Issue
Block a user