Fehler und Resilienz
Wenn etwas schiefgeht: Fehlerresilienz
Die vorherigen Kapitel haben den Idealfall beschrieben: jeder Dienst ist erreichbar, jede Antwort kommt rechtzeitig. In der Praxis geht regelmäßig etwas schief. Ein Sprachmodell antwortet nicht, eine Datenbank ist kurzzeitig unerreichbar, ein Worker-Prozess stürzt ab. Die Frage ist, wie das System damit umgeht.
Begriffe zuerst: In diesem Kapitel bedeutet failed den finalen fachlichen Endstatus einer Email. Die Verarbeitung ist abgebrochen und ein Mensch muss eingreifen. Nicht zu verwechseln mit dead auf Queue-Ebene (siehe email-rag-ki-worker-und-queues): Ein dead Queue-Eintrag bedeutet nur, dass für diesen konkreten Job-Retry keine automatische Wiederholung mehr erfolgt. Beide Ebenen werden in diesem Kapitel gemeinsam beleuchtet.
Strategien für Fehler
Das System kennt mehrere grundlegende Strategien, die je nach Fehlertyp kombiniert werden:
Wiederholung mit Wartezeit (Retry mit Backoff): Der fehlgeschlagene Schritt wird erneut versucht, mit steigenden Wartezeiten zwischen den Versuchen. Die Standard-Wartezeiten sind 30, 120 und 600 Sekunden. Nach drei Versuchen wird die Email als fehlgeschlagen markiert. Für den Versand selbst gilt zusätzliche Idempotenz-Garantie: der Send-Attempt wird pro Draft genau einmal erzeugt und bei Retries fortgeschrieben (Details in email-rag-ki-versand-feedback).
Fallback auf reduzierten Betrieb: Wenn ein optionaler Dienst ausfällt (Qdrant für die Vektorsuche, ArangoDB für den Graphen), arbeitet das System mit reduziertem Kontext weiter. Die Email wird als partially_indexed markiert und später nachindexiert.
Dead Letter und manuelle Prüfung: Emails, die nach allen Versuchen nicht verarbeitet werden konnten, landen im Dead-Letter-Status. Sie werden nicht gelöscht, sondern bleiben zur manuellen Prüfung im System.
Fehlerbehandlung nach Dienst
Jeder Dienst hat eine eigene Fehlerstrategie:
- IMAP-Fehler: Unbegrenzte Wiederholung mit exponentiellem Backoff. Der Poller versucht es beim nächsten Cron-Lauf erneut.
- MariaDB-Fehler: Sofortiger Reconnect. Alle Service-Caches werden geleert, der Worker erstellt seine Dienste neu. Bei anhaltendem Ausfall wartet der Worker.
- LLM-Timeout: Ein Wiederholungsversuch. Schlägt auch der fehl, wird die Email in die manuelle Queue gestellt.
- LLM-JSON-Fehler: Das Modell liefert ungültiges JSON. Ein Retry mit angepasstem Prompt. Danach Standardwerte.
- Qdrant/ArangoDB: Drei Versuche mit Backoff. Bei anhaltendem Ausfall:
partially_indexed. Der periodische Cron-Sync füllt die fehlenden Daten nach. - SMTP-Fehler: Drei Versuche. Der freigegebene Entwurf bleibt als
approvedstehen und wird beim nächsten Versuch erneut gesendet.
Stuck Processing: die Sicherheitsleine
Ein besonderer Fall: was passiert, wenn ein Worker-Prozess abstürzt, während er eine Email verarbeitet? Der Queue-Eintrag bleibt im Status processing hängen, niemand holt ihn ab.
Dafür gibt es den Stuck-Processing-Reset: der CleanupService prüft stündlich, ob Queue-Einträge länger als 600 Sekunden im Status processing stehen. Falls ja, werden sie auf failed zurückgesetzt und landen im normalen Retry-Kreislauf.
Jeder Fehler, jeder Retry und jedes Fallback wird im Audit-Log protokolliert. So lässt sich nachvollziehen, warum eine Email auf einem bestimmten Weg durch das System gelaufen ist.