SRC-02: indexierungs-worker-synchronisierung
Indexierungs-Worker, der neu archivierte Mails asynchron in den Manticore-Index (SRC-01) einpflegt und Löschungen nachzieht. - indexworker/queue.go: Postgres-Jobqueue (mail_index_jobs), FOR UPDATE SKIP LOCKED, Stale-Lock-Wiedervorlage bei Worker-Absturz, arithmetischer Backoff bei Fail (kein String-Concat für Intervalle) — Konvention aus dms/internal/jobqueue (FDN-04), hier bewusst ohne DLQ (nicht Bestandteil der Akzeptanzkriterien dieser Kachel). - indexworker/worker.go: RunOnce verarbeitet index-/delete-Jobs über search.Client. - search: minimale Erweiterung um Client.Delete und deterministisches DocumentID(tenantSlug, messageID), damit Index/Delete für dieselbe Mail immer dasselbe Dokument treffen. Prüfungen (alle real durchgeführt, siehe mail/docs/SRC-02-PRUEFPROTOKOLL.md): 1. TestDequeue_WorkerCrashMidRunLosesNoJob: simulierter Worker-Absturz, Job wird nach Ablauf der Stale-Lock-Frist real erneut zugestellt. 2. TestDeleteJob_RemovesMailFromSearchResults: Lösch-Job entfernt Mail nachweislich aus Suchtreffern. 3. TestConsistency_DatabaseAndIndexMatchOnSample: DB-Job-Status und Index-Inhalt stichprobenartig real abgeglichen. Zusätzlich TestIndexJob_MakesMailSearchable für Akzeptanzkriterium 1. Kein Umbau: storage/crypto/encstorage/dedup unverändert, bestehendes SRC-01-Verhalten unverändert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c3bf8100b1
commit
c5bdde0cc8
@@ -106,6 +106,37 @@ func (c *Client) Index(ctx context.Context, doc Document) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete entfernt ein Suchdokument anhand seiner ID (SRC-02
|
||||
// Akzeptanzkriterium 2: Löschungen werden im Index nachgezogen). Löschen
|
||||
// eines nicht (mehr) vorhandenen Dokuments ist kein Fehler (idempotent).
|
||||
func (c *Client) Delete(ctx context.Context, id uint64) error {
|
||||
payload := map[string]any{
|
||||
"index": IndexName,
|
||||
"id": id,
|
||||
}
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("search: lösch-anfrage serialisieren: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/delete", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("search: lösch-anfrage bauen: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("search: dokument löschen: %w", err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("search: dokument löschen, status %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Result ist ein Suchtreffer.
|
||||
type Result struct {
|
||||
MessageID string
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
// String-Zusammenbau von SQL), nicht über die SQL-Schnittstelle.
|
||||
package search
|
||||
|
||||
import "hash/fnv"
|
||||
|
||||
// IndexName ist der einzige Ort, an dem der Manticore-Indexname als
|
||||
// Literal steht.
|
||||
const IndexName = "mail_documents"
|
||||
@@ -27,3 +29,17 @@ const (
|
||||
// searchableTextFields sind die Volltextfelder, über die eine Suchanfrage
|
||||
// läuft (Akzeptanzprüfung 3: Volltextsuche liefert erwartete Treffer).
|
||||
var searchableTextFields = []string{FieldSubject, FieldBody, FieldAttachmentText}
|
||||
|
||||
// DocumentID berechnet deterministisch die Manticore-Dokument-ID aus
|
||||
// Mandant und Message-ID (FNV-1a, 64 Bit). Deterministisch statt einer
|
||||
// separat vergebenen ID, damit Re-Indexierung (Index) und Löschung
|
||||
// (Delete) für dieselbe Mail immer dieselbe Dokument-ID referenzieren,
|
||||
// ohne dass der Aufrufer sie zwischenspeichern muss (SRC-02: Löschungen
|
||||
// müssen ohne zusätzlichen Zustand nachgezogen werden können).
|
||||
func DocumentID(tenantSlug, messageID string) uint64 {
|
||||
h := fnv.New64a()
|
||||
_, _ = h.Write([]byte(tenantSlug))
|
||||
_, _ = h.Write([]byte{0})
|
||||
_, _ = h.Write([]byte(messageID))
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user