Files
archivmail/features/PROJ-86-fix-manticore-index-drift-und-date-sortierung.md
sysopsandClaude Sonnet 5 26c0e04c75 fix(PROJ-86): Manticore-Index-Drift (emails_global) + Datumssortierung bei fehlendem Date-Header
emails_global bekam nie Tenant-Mails gespiegelt, Superadmin-Suche sah nur
~30% aller Mails. Zusätzlich sanken Mails ohne gültigen Date-Header
(date_ts=0) beim datumssortierten Listing ans Ende aller Ergebnisse und
waren dadurch praktisch unauffindbar ("GUI zeigt neue Mails nicht") -
Import/Indexierung liefen technisch korrekt, nur Sortierung/Spiegelung
waren kaputt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPFC6Jk2ke1Pq9XcuVGP1R
2026-09-01 12:17:09 +02:00

3.4 KiB

PROJ-86: Fix Manticore-Index-Drift (emails_global) + Datumssortierung bei fehlendem Date-Header

Status: Deployed

Created: 2026-09-01 Last Updated: 2026-09-01

Kontext

Ausgangspunkt: Nutzer meldete "Manticore-Index passt nicht" / "GUI zeigt aktuelle Mails aus dem Postfach nicht an". Diagnose auf 192.168.1.132 (192.168.1.131 gehört seit 2026-09-01 nicht mehr zum archivmail-Projekt) ergab zwei unabhängige Bugs:

  1. emails_global-Lücke: Reindex und Live-Indexierung schrieben jede Mail nur in ihre Tenant-Tabelle (ForTenant(tenantID)), nie zusätzlich nach emails_global. Die Superadmin-Suche (search_handlers.go) liest bei tenantID == nil aber genau diesen globalen Index. Ergebnis: Superadmin sah nur ~30% aller Mails (16246 von 54202).
  2. date_ts=0 bei fehlendem/kaputtem Date-Header: Die Sortierung läuft standardmäßig über date_ts DESC (aus dem Mail-eigenen Date:-Header, pm.Date). Fehlt der Header oder ist er unparsebar (häufig bei Spam/Marketing-Mails), bleibt pm.Date Zero-Value → date_ts=0 → die Mail sinkt beim datumssortierten Listing ans Ende aller Ergebnisse und taucht auf Seite 1 nie auf. Das war der eigentliche Grund für "GUI zeigt neue Mails nicht" — Import/Indexierung liefen technisch korrekt, die Mails waren nur unauffindbar einsortiert.

Fix

  • Neue Hilfsfunktion index.EffectiveDate(headerDate, fallback time.Time) time.Time — nutzt fallback (received_at bei Reindex/Backfill, time.Now() bei Live-Ingest) wenn headerDate Zero-Value ist.
  • Neue Storage-Methode Store.GetReceivedAt(ctx, id).
  • Store.UnindexedMail um Feld ReceivedAt erweitert (kein zusätzlicher DB-Roundtrip nötig).
  • An allen 9 Stellen, die index.MailDocument{} bauen, Date: pm.Date auf Date: index.EffectiveDate(pm.Date, fallback) umgestellt: cmd_reindex.go, cmd_index_pending.go, cmd_fix_subjects.go, cmd_import.go, main.go (submitToWorker + reindexTenant), internal/imap/importer.go, internal/pop3/importer.go, internal/api/upload.go, cmd/archivmail-import/main.go.
  • 5 Stellen um Spiegelung nach emails_global ergänzt (Write: cmd_reindex.go, tenant_worker.go, internal/imap/importer.go; Delete: cmd_purge.go, internal/api/dsgvo_handlers.go) — jeweils nur wenn tenantID != nil (sonst landet die Mail ohnehin direkt im globalen Index).

Dependencies

  • Betrifft dieselbe Indexier-Infrastruktur wie PROJ-58 (Cron-Batch-Indexierung) und PROJ-65 (physische Tenant-Trennung) — die emails_global-Lücke entstand vermutlich mit PROJ-65.

Acceptance Criteria

  • emails_global enthält nach Reindex alle Tenant-Mails (54208 vs. DB-Gesamt 54202, Differenz = 6 Vorfix-Karteileichen, unkritisch)
  • date_ts=0-Einträge nach Reindex in allen Tabellen (emails_global, emails_tenant_1/2/3) = 0
  • Build + go vet grün auf 192.168.1.132
  • Deployed auf 132 (einziger verbleibender Server), Voll-Reindex durchgeführt (54206/54206, 0 Fehler)
  • Stichprobe verifiziert: Mail mit vormals date_ts=0 hat nach Fix korrekten Timestamp (1788254163)

Bekannte Restarbeit (nicht Teil dieses Tickets)

  • Vorfix-Karteileichen in emails_global (6 Stück, gelöschte Mails ohne Index-Cleanup vor diesem Fix) bereinigen sich beim nächsten regulären Purge-Zyklus von selbst — kein Handlungsbedarf.
  • 192.168.1.131 ist aus dem Projekt-Deployment-Ziel entfernt (Memory aktualisiert) — Skills/Doku, die noch 131 referenzieren, sollten bei Gelegenheit bereinigt werden.