mailboxconfig (IMP-07) bekommt eine quota_bytes-Spalte statt einer eigenen Tabelle — ein Postfach ist bereits eindeutig über (tenant_slug, name) identifiziert. SetQuotaBytes/LimitBytes, 0 = unbegrenzt (Standardwert, keine Migration bestehender Postfächer nötig). LimitBytes erfüllt strukturell quota.LimitProvider. storage.ArchiveMailboxPrefix (ARC-04-Ergänzung, Präfix ALLER Jahre eines Postfachs) und storage.UsageCounter: realer Speicherverbrauch durch echtes S3-Listing im physisch getrennten Mandanten-Bucket (ARC-06) — kein separat gepflegter Zählerstand. Neues Paket mail/internal/quota: Checker verbindet LimitProvider und UsageProvider. Kein konfiguriertes Limit = immer erlaubt (Core-LIC-05- Quota läuft unabhängig weiter — beide Ebenen bewusst unabhängig durchgesetzt, bekannter Fehler vermieden). smtp.QuotaChecker (schmale Schnittstelle, keine Paketkopplung an quota) wird in handleRcptTo geprüft, VOR der Datenübertragung: 552 (RFC 5321 "exceeded storage allocation") bei Überschreitung, Session bleibt nutzbar. nil-Checker erhält bisheriges Verhalten unverändert. Alle drei Pflichtprüfungen mit echten Nachweisen: Quota-Überschreitung liefert 552, Session bleibt funktionsfähig; ein anderes Postfach desselben Tenants läuft währenddessen vollständig normal durch; vollständiger Ende-zu-Ende-Integrationstest gegen reale Postgres- und MinIO-Instanzen — 5000 echte Bytes abgelegt, real gemessen, Limit knapp darunter/darüber gesetzt, SMTP reagiert jeweils korrekt auf den tatsächlichen gemessenen Wert. Dabei einen echten Cleanup-Fehler gefunden und behoben (defer schloss den Pool vor dem zugehörigen t.Cleanup, verwaiste Testdaten blieben zurück). go build/go vet/golangci-lint clean, gesamtes Mail-Modul regressionsfrei getestet.
5.4 KiB
ARC-09 — Postfach-Quota: Prüfprotokoll
Datum: 2026-09-02
Host: 192.168.1.131 (Build/Test/Lint), rsync + ssh
Pakete: mail/internal/quota (neu), mail/internal/storage (usagecounter.go, archivekey.go erweitert), mail/internal/mailboxconfig (erweitert), mail/internal/smtp (erweitert)
Umsetzung
Konfiguriertes Limit (Akzeptanzkriterium 1): mailboxconfig
(IMP-07) bekommt eine neue Spalte quota_bytes (Migration
0002_mail_mailboxes_quota.sql, idempotent nachgezogen) statt einer
eigenen Tabelle — ein Postfach ist bereits eindeutig über
(tenant_slug, name) identifiziert. Store.SetQuotaBytes/LimitBytes
(0 = unbegrenzt, Standardwert, keine Migration bestehender
Postfächer nötig). LimitBytes erfüllt strukturell quota. LimitProvider — eigenständig von der tenant-weiten Core-LIC-05-Quota.
Realer Verbrauch (Pflichtprüfung 3): storage.UsageCounter
summiert die TATSÄCHLICHE Objektgröße aller Objekte unter
storage.ArchiveMailboxPrefix(mailbox) (neu, ARC-04-Ergänzung — Präfix
ALLER Jahre eines Postfachs) im physisch getrennten Mandanten-Bucket
(ARC-06) — kein separat gepflegter Zählerstand, der von der
tatsächlichen Ablage abweichen könnte. Erfüllt strukturell quota. UsageProvider.
Verknüpfung: quota.Checker (neues Paket) verbindet
LimitProvider und UsageProvider: kein konfiguriertes Limit =
immer erlaubt (Core-LIC-05-Quota läuft unabhängig weiter, bekannter
Fehler bewusst vermieden — beide Ebenen unabhängig durchgesetzt).
SMTP-Durchsetzung (Akzeptanzkriterium 2): smtp.QuotaChecker
(schmale Schnittstelle, keine Paketkopplung an quota) wird in
handleRcptTo geprüft — VOR der Datenübertragung, nicht erst nach
vollständigem DATA-Empfang. Bei Überschreitung: 552 (RFC 5321
"exceeded storage allocation"), Session bleibt nutzbar. Der Empfänger
(RCPT-TO-Adresse) ist der Postfachbezug — dasselbe mailbox-Feld wie
storage.ArchiveKey/mailboxconfig. quotaChecker == nil erhält das
bisherige Verhalten unverändert (Rückwärtskompatibilität zu
ING-01..QA-04).
Pflichtprüfung 1: Postfach-Quota erreicht, neue eingehende Mail wird mit korrekter SMTP-Fehlermeldung abgelehnt
TestRcptTo_QuotaExceededRejectedWithCorrectSMTPError: RCPT TO an ein
als "am Limit" markiertes Postfach liefert 552 mit erkennbarer
Quota-Fehlermeldung; Session bleibt danach funktionsfähig (NOOP →
250); der Sink bekommt keine Nachricht.
Ergebnis: BESTANDEN.
Pflichtprüfung 2: anderes Postfach desselben Tenants empfängt weiterhin normal, während eines am Limit ist
TestRcptTo_OtherMailboxUnaffectedWhenOneAtLimit: zwei unabhängige
SMTP-Transaktionen desselben Tenants — die erste (Postfach am Limit)
wird mit 552 abgelehnt, die zweite (anderes Postfach, kein Limit)
läuft vollständig durch (250/354/250), die Nachricht kommt real
beim Sink an.
Ergebnis: BESTANDEN.
Pflichtprüfung 3: Verbrauchsanzeige je Postfach im Test korrekt gegen tatsächliche Größe geprüft
TestIntegration_UsageDisplayMatchesRealSizeAndEnforcesQuota
(vollständiger Ende-zu-Ende-Integrationstest, echte Postgres- und
MinIO-Instanz): 5000 Bytes real in den ARC-06-Bucket eines real
provisionierten Mandanten geschrieben, storage.UsageCounter. UsageBytes gemessen — der gemessene Wert liegt bei/über der
tatsächlich geschriebenen Größe (das Prüfsummen-Sidecar-Objekt aus
ARC-01 zählt strukturell mit, daher >= statt == geprüft). Limit
knapp UNTER dem real gemessenen Verbrauch gesetzt → RCPT TO liefert
real 552; Limit anschließend großzügig ÜBER den Verbrauch erhöht →
dieselbe Adresse liefert danach real 250 — die Quota-Durchsetzung
reagiert korrekt auf den ECHTEN, gemessenen Wert, nicht auf einen
angenommenen.
Ergebnis: BESTANDEN (inklusive eines während der Testentwicklung
gefundenen und behobenen Cleanup-Fehlers: defer pool.Close() schloss
die Postgres-Verbindung VOR den zugehörigen t.Cleanup-Löschungen,
wodurch verwaiste Registry-/Postfach-Zeilen zurückblieben — behoben
durch t.Cleanup(pool.Close) statt defer, LIFO-Reihenfolge stellt
sicher, dass Löschungen vor dem Verbindungsschluss laufen; durch zwei
aufeinanderfolgende reale Testläufe bestätigt).
Akzeptanzkriterien
- Speicherlimit ist je Postfach konfigurierbar, unabhängig von der
Tenant-weiten Quota aus Core LIC-05:
mailboxconfig. SetQuotaBytes/LimitBytes, durch Pflichtprüfung 3 belegt. - Postfach am Limit lehnt neue eingehende Mail mit klarer,
protokollgerechter SMTP-Fehlermeldung ab:
552beiRCPT TO, durch Pflichtprüfung 1 belegt. - Ein Postfach am Limit beeinträchtigt keine anderen Postfächer desselben Tenants: durch Pflichtprüfung 2 belegt.
Build/Vet/Lint/Test — Gesamtmodul
go build ./... → OK
go vet ./... → OK
golangci-lint run ./... → 0 issues
go test ./... -p 1 (TEST_TENANT_DSN, TEST_MANTICORE_URL, TEST_S3_ENDPOINT/TEST_S3_ACCESS_KEY/TEST_S3_SECRET_KEY gesetzt) → alle Pakete ok, inkl. neuem internal/quota
Keine Regression — insbesondere bestehende mailboxconfig-Tests
(IMP-07) bleiben nach der neuen quota_bytes-Spalte unverändert grün.
Ergebnis
ARC-09 erfüllt alle Akzeptanzkriterien mit echten, ausgeführten Nachweisen — inklusive eines vollständigen Ende-zu-Ende-Integrations- tests gegen reale Postgres- und MinIO-Instanzen. Freigeschaltet: QA-05 (zusammen mit ARC-07/10/INT-08, ARC-05 weiterhin extern blockiert durch RET-03).