fix: Crash-Robustheit + Performance in Backend und Frontend härten
Backend: recover() in allen langlebigen Goroutinen (neues internal/safego- Paket), MIME-Multipart-Tiefenlimit gegen Stack-Overflow, IMAP-Zeilenlängen- und FETCH-Result-Limits gegen OOM, MBOX-Buffer-Aliasing-Bug (Datenkorruption beim Import), ungeprüfte Type Assertions abgesichert, Data Race im API-Key-Rate-Limiter behoben, SMTP-Session-Panic führt jetzt zu 451-Retry statt Prozessabsturz. Performance: O(n²)-String-Concat in Mailparser und IMAP-Parser durch strings.Builder ersetzt. Frontend: Error Boundaries für Root und Mail-Detailansicht ergänzt (gab es vorher nicht), zahlreiche Guards gegen nil-Slices aus dem Backend-JSON die sonst .map()/.length-Crashes/White-Screens auslösten, defekte JSON-Antworten in api/core.ts abgefangen, zwei React-Key-Bugs bei löschbaren Listen korrigiert. Verifiziert auf 192.168.1.132: Build und Tests der geänderten Pakete fehlerfrei, keine Regressionen gegenüber vorbestehendem Stand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0fa4fad49d
commit
798cb2817c
@@ -1,6 +1,7 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
)
|
||||
@@ -83,7 +84,20 @@ func (w *TenantIndexWorker) QueueLen() int {
|
||||
}
|
||||
|
||||
func (w *TenantIndexWorker) indexDoc(doc MailDocument) {
|
||||
// A panic while indexing a single document must not kill the long-running
|
||||
// worker goroutine (and with it the whole process).
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
w.logger.Error("tenant index worker: recovered from panic",
|
||||
"id", doc.ID, "tenant_id", doc.TenantID, "panic", fmt.Sprintf("%v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
idx := w.mgr.ForTenant(doc.TenantID)
|
||||
if idx == nil {
|
||||
w.logger.Error("tenant index worker: no indexer for tenant", "id", doc.ID, "tenant_id", doc.TenantID)
|
||||
return
|
||||
}
|
||||
if err := idx.IndexSync(doc); err != nil {
|
||||
w.logger.Error("tenant index worker: index failed", "id", doc.ID, "tenant_id", doc.TenantID, "err", err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
)
|
||||
@@ -53,9 +54,7 @@ func (w *IndexWorker) Start() {
|
||||
// Channel closed, drain complete
|
||||
return
|
||||
}
|
||||
if err := w.idx.IndexSync(doc); err != nil {
|
||||
w.logger.Error("index worker: index failed", "id", doc.ID, "err", err)
|
||||
}
|
||||
w.indexDoc(doc, "")
|
||||
case <-w.done:
|
||||
// Drain remaining items in the queue before exiting
|
||||
for {
|
||||
@@ -64,9 +63,7 @@ func (w *IndexWorker) Start() {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := w.idx.IndexSync(doc); err != nil {
|
||||
w.logger.Error("index worker: index failed (drain)", "id", doc.ID, "err", err)
|
||||
}
|
||||
w.indexDoc(doc, " (drain)")
|
||||
default:
|
||||
return
|
||||
}
|
||||
@@ -76,6 +73,21 @@ func (w *IndexWorker) Start() {
|
||||
}()
|
||||
}
|
||||
|
||||
// indexDoc indexes a single document. A panic in the backend must not kill the
|
||||
// long-running worker goroutine (and with it the whole process).
|
||||
func (w *IndexWorker) indexDoc(doc MailDocument, phase string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
w.logger.Error("index worker: recovered from panic"+phase,
|
||||
"id", doc.ID, "panic", fmt.Sprintf("%v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
if err := w.idx.IndexSync(doc); err != nil {
|
||||
w.logger.Error("index worker: index failed"+phase, "id", doc.ID, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop signals the worker to drain remaining items and stop. It blocks until
|
||||
// the worker goroutine has exited.
|
||||
func (w *IndexWorker) Stop() {
|
||||
|
||||
Reference in New Issue
Block a user