Use header-only IMAP lists
This commit is contained in:
parent
a52963c380
commit
a17d2c8a2c
5 changed files with 456 additions and 184 deletions
99
ui-fetch-brief.md
Normal file
99
ui-fetch-brief.md
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# Codex-Brief — UI-Fetch: Header-only + Pagination
|
||||
|
||||
Der Migrationspfad streamt jetzt (bewiesen: 80 MB Ordner durch eine harte
|
||||
16-MiB-Grenze). **Die UI tut es nicht.** Sie ist damit das letzte OOM-Loch — und
|
||||
das gefährlichere, weil ein Mensch es mit **einem Klick** auslöst.
|
||||
|
||||
## Das Problem
|
||||
|
||||
`fetchSourceFolderMessages` → `collectSourceMessages` ([00-router.go:1548](backend/00-router.go))
|
||||
sammelt **alle Mails eines Ordners samt vollem Body** in eine Slice — nur um eine
|
||||
**Liste** zu rendern. Aufgerufen an **sechs** Stellen:
|
||||
`00-router.go:397, 422, 1286, 1369, 1585, 1602`.
|
||||
|
||||
Dasselbe auf der Zielseite: [05-imap-target.go:124](backend/05-imap-target.go) → `fetchAll`.
|
||||
|
||||
Rechnung für ein echtes `@dr-gold.de`-Postfach: 20.000 Mails × 200 KB =
|
||||
**~4 GB RAM für einen Klick auf einen Ordner** → Container wird OOM-gekillt.
|
||||
|
||||
**Der Kern der Sache:** Eine Nachrichtenliste braucht **Betreff, Absender, Datum,
|
||||
Flags** — sie braucht **keine Bodies**. Wir holen also Daten, die wir wegwerfen.
|
||||
|
||||
## Der Fix
|
||||
|
||||
### 1. Header-only-Fetch (neu)
|
||||
|
||||
`ENVELOPE` liefert Betreff/Absender/Datum/Message-ID — **ohne** `BodySection`:
|
||||
|
||||
```go
|
||||
type MessageHeader struct {
|
||||
UID uint32
|
||||
MessageID string
|
||||
Subject string
|
||||
From string
|
||||
Date time.Time
|
||||
InternalDate time.Time
|
||||
Flags []string
|
||||
Size uint32 // RFC822.SIZE, fuer die Anzeige
|
||||
}
|
||||
|
||||
// Fenster: neueste `limit` Mails ab `offset`. NIE unbegrenzt 1:*.
|
||||
Headers(folder string, limit, offset int) ([]MessageHeader, error)
|
||||
```
|
||||
|
||||
Implementierung (go-imap/v2) — beachte: **kein `BodySection`**:
|
||||
```go
|
||||
fo := &imap.FetchOptions{
|
||||
Envelope: true, Flags: true, InternalDate: true, UID: true, RFC822Size: true,
|
||||
// BodySection: ABSICHTLICH NICHT
|
||||
}
|
||||
```
|
||||
Damit ist der Speicher proportional zur **Fenstergröße**, nicht zur Ordnergröße.
|
||||
|
||||
### 2. Fenster statt „alles"
|
||||
Immer ein Bereich (z. B. die **neuesten 200**), berechnet aus
|
||||
`selected.NumMessages`. Blättern über `offset`. Kein `1:*` für Listen — nirgends.
|
||||
|
||||
### 3. Voller Body erst beim Öffnen **einer** Mail
|
||||
```go
|
||||
FetchOne(folder string, uid uint32) (RawMessage, error) // mit BodySection
|
||||
```
|
||||
Der Lesebereich holt genau **eine** Mail. Das ist der einzige Ort, an dem ein
|
||||
Body in den Speicher gehört.
|
||||
|
||||
### 4. Migration bleibt unangetastet
|
||||
`Fetch(folder string, fn func(RawMessage) error) error` ist der **Migrationspfad**
|
||||
und bleibt exakt wie er ist (inkl. der verifizierten Reihenfolge
|
||||
`AlreadyCopied → dst.Append → mbox.Append → MarkCopied`). Nicht anfassen.
|
||||
|
||||
## Umzubauen
|
||||
|
||||
- Die **sechs** `fetchSourceFolderMessages`-Aufrufer auf `Headers(...)` umstellen
|
||||
(Listen) bzw. auf `FetchOne(...)` (Lesebereich).
|
||||
- Zielseite: `05-imap-target.go:124` (`fetchAll`) genauso.
|
||||
- `collectSourceMessages` und `fetchAll` danach **löschen** — sie sind die Falle;
|
||||
solange sie existieren, greift sie jemand wieder auf.
|
||||
- `10-pop3.go` mitziehen (POP3 kennt keine Envelope-Fetches → dort Header aus dem
|
||||
`TOP`-Kommando bauen oder die Methode als „nicht unterstuetzt" zurueckgeben).
|
||||
|
||||
## Kleinigkeit aus dem letzten Review (gern mitnehmen)
|
||||
In `fetchEach` ([04-imap-source.go](backend/04-imap-source.go)) wurde
|
||||
`defer cmd.Close()` durch explizite Closes ersetzt. Alle Pfade sind abgedeckt,
|
||||
aber bei einem Panic im Callback bleibt das FETCH-Kommando offen und die
|
||||
Verbindung kaputt. `defer` wieder rein (Doppel-Close ist harmlos).
|
||||
|
||||
## Abnahme
|
||||
|
||||
1. `go test ./...` grün.
|
||||
2. Funktional unverändert: Listen zeigen weiterhin Betreff/Absender/Datum,
|
||||
Öffnen einer Mail zeigt weiterhin den vollen Inhalt inkl. Anhängen.
|
||||
3. **Speicher (fahre ich):** Ordner mit ~40 Mails à 2 MB (80 MB), Web-Container
|
||||
mit harter 64-MiB-Grenze, dann per HTTP die Ordner-Liste abrufen. Vorher:
|
||||
OOM-Kill. Nachher: Liste kommt, Speicher bleibt flach. Ich setze das auf und
|
||||
verifiziere es — du baust nur den Code.
|
||||
|
||||
## NICHT in diesem Schritt
|
||||
- Der lokale **mbox-Viewer** (`ReadMboxList`) liest die ganze Datei, hält aber nur
|
||||
Header — Speicher ist unkritisch. Pagination dort später, reine Kosmetik.
|
||||
- POP3 vollständig verdrahten, DB-Namen-Altlast (`emailforwarder.db` vs.
|
||||
`mail-graveyard.db` + zwei 0-Byte-Leichen), RFC-2047-Betreff im Forward.
|
||||
Loading…
Add table
Add a link
Reference in a new issue