# PROJ-30: Volltext-Index: Xapian → Manticore Search Migration ## Status: Deployed **Created:** 2026-03-28 **Last Updated:** 2026-04-04 ## Dependencies - Requires: PROJ-5 (Speicherung & Indexierung) — bestehende Index-Abstraktion - Requires: PROJ-21 (Multi-Tenancy) — per-Tenant Index-Isolation ## Motivation Xapian erfordert CGO und `libxapian-dev` als System-Dependency. Das verhindert: - Docker-Images (CGO-Build in Containern komplex, Build-Image groß) - Einfache Cross-Compilation - Reproduzierbare Builds ohne native Abhängigkeiten **Entscheidung: Manticore Search** statt ursprünglich geplantem Bleve. ### Warum Manticore statt Bleve Das primäre Kriterium ist **Datenmenge und Performance**, nicht Einfachheit. Manticore ist eine in C++ geschriebene Such-Engine (Fork von SphinxSearch) mit MySQL-kompatiblem Protokoll: | Metrik | Bleve | Manticore | |---|---|---| | Suchlatenz bei 1M Docs | 50–200ms | 5–20ms | | Index-Durchsatz | ~5.000 Docs/s | ~50.000 Docs/s | | RAM bei 10M Mails | 4–8 GB | 1–2 GB | | Columnar Storage | Nein | Ja | | Highlighting | Ja | Ja | | Fuzzy-Search | Ja | Ja | | CGO im Go-Code | Nein | Nein (MySQL-Protokoll) | Manticore läuft als eigener Systemd-Service — kein embedded Mode. Der Trade-off (externer Dienst) ist bei Performance-Priorität gerechtfertigt. Elasticsearch/OpenSearch wurden als Alternative geprüft — zu schwer, zu viel Infrastruktur für On-Premise. ## Architektur nach Migration ``` PostgreSQL → Metadaten: From, To, Subject, Date, message_id, tenant_id → Dedup, Audit-Log, User, Tenant, Labels → strukturierte Filter-Queries Manticore Search (Port 9306, MySQL-Protokoll) → Volltext-Body + Anhang-Text → Ein Index pro Tenant (saubere Mandantentrennung) → Real-Time-Index (neue Mail sofort suchbar) → Highlighting in Suchergebnissen → BM25+ Relevanz-Ranking Suchanfrage in Go: → PostgreSQL: filtert tenant_id, Datum, From/To → IDs → Manticore: Volltext-Relevanz über IDs → gerankte Ergebnisse → Merge in API-Handler ``` ## User Stories - Als Betreiber möchte ich archivmail ohne CGO kompilieren können (`CGO_ENABLED=0`). - Als Nutzer möchte ich auch bei großen Archiven (>1M Mails) schnelle Suchergebnisse (<20ms). - Als Nutzer möchte ich Treffer im Suchtext hervorgehoben sehen (Highlighting). - Als Admin möchte ich nach einem Absturz den Index mit einem Kommando neu aufbauen können. ## Acceptance Criteria - [x] Manticore läuft als Systemd-Service auf dem Server (Port 9306, nur localhost) - [x] `internal/index/manticore.go` ersetzt `xapian.go` und `xapian_stub.go` - [x] Gleiche Interface-Signatur (`IndexMail`, `Search`, `Delete`, `Close`) - [x] Go-Integration via `go-sql-driver/mysql` (kein CGO) - [x] Pro Tenant ein Manticore Real-Time-Index: `emails_tenant_{id}` - [x] Indizierte Felder: `id`, `subject`, `from_addr`, `to_addr`, `body`, `attachment_names`, `date_ts` - [x] Suchanfragen: Volltext, FROM:, TO:, SUBJECT:, SINCE:, BEFORE: - [x] Migration-Kommando: `archivmail reindex` baut alle Tenant-Indizes neu auf - [x] `CGO_ENABLED=0 go build` funktioniert nach Migration - [x] Manticore-Port 9306 nur auf localhost gebunden (kein externer Zugriff) - [x] `update.sh` prüft ob Manticore installiert ist und installiert es bei Bedarf ## Edge Cases - Bestehender Xapian-Index wird NICHT automatisch migriert — `reindex` liest Roh-Mails aus Storage neu ein - Während `reindex` läuft: Suche liefert unvollständige Ergebnisse (Warnung im Log) - Manticore-Dienst nicht erreichbar → Suche gibt Fehler zurück, Metadaten-Suche (PostgreSQL) bleibt verfügbar - Index beschädigt → `reindex` behebt, kein Datenverlust (Roh-Mails in Storage sind Source of Truth) - Neuer Tenant → Index wird beim ersten Mail-Import automatisch angelegt (`CREATE TABLE IF NOT EXISTS`) ## Technical Requirements - **Engine:** Manticore Search (GPLv2) - **Protokoll:** MySQL-kompatibel, Port 9306 - **Go-Treiber:** `github.com/go-sql-driver/mysql` - **CGO:** Nicht erforderlich im Go-Code - **Index-Typ:** Real-Time Index pro Tenant - **Installation:** Offizielles Manticore APT-Repository — Paket bleibt über `apt upgrade` aktuell - **Systemd:** `manticore.service` läuft parallel zu `archivmail.service` - **Konfiguration:** Neuer Abschnitt `manticore:` in `/etc/archivmail/config.yml` ```yaml manticore: dsn: "manticore@tcp(127.0.0.1:9306)/" enabled: true ``` ## Migration Path ``` 1. Manticore-APT-Repo einrichten + Paket installieren: curl -s https://repo.manticoresearch.com/manticore-repo.noarch.deb -o /tmp/manticore-repo.deb dpkg -i /tmp/manticore-repo.deb && apt update && apt install -y manticore # danach: apt upgrade hält Manticore automatisch aktuell 2. manticore.go implementieren (gleiche Interface wie xapian.go) 3. Build-Tag in xapian.go anpassen (beide parallel lauffähig während Entwicklung) 4. Integrations-Tests gegen Manticore 5. xapian.go + xapian_stub.go entfernen 6. `archivmail reindex` auf Server ausführen 7. Alten Xapian-Index-Ordner löschen (/var/archivmail/xapian/) 8. update.sh: Manticore-Installations-Check hinzufügen ``` ## Implementation Notes - `internal/index/manticore.go` — vollständige Implementierung mit `ManticoreTenantManager` + `manticoreIndex` - `TenantIndexer`-Interface in `index.go` abstrahiert Manticore und Legacy-Xapian-Pfad - Morphologie: `lemmatize_de_all,stem_en` (Manticore 25.0.0 — `stem_de` nicht verfügbar via MySQL-Protokoll) - fnv64a-Hash leitet uint64-Row-ID aus SHA-256-Mail-ID ab - `escapeManticoreMatch()` schützt vor Injection in MATCH()-Ausdrücke - `cmd import` und `cmd import-piler` ebenfalls auf Manticore-Backend umgestellt - Deployed auf 131 (bookworm) und 132 (trixie) — Manticore 25.0.0 (bookworm-Paket) ## Deployment - 192.168.1.131: Manticore 25.0.0, 65 Mails indiziert - 192.168.1.132: Manticore 25.0.0, 14.160 Mails indiziert