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.
113 lines
5.4 KiB
Markdown
113 lines
5.4 KiB
Markdown
# 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
|
|
|
|
1. **Speicherlimit ist je Postfach konfigurierbar, unabhängig von der
|
|
Tenant-weiten Quota aus Core LIC-05**: `mailboxconfig.
|
|
SetQuotaBytes`/`LimitBytes`, durch Pflichtprüfung 3 belegt.
|
|
2. **Postfach am Limit lehnt neue eingehende Mail mit klarer,
|
|
protokollgerechter SMTP-Fehlermeldung ab**: `552` bei `RCPT TO`,
|
|
durch Pflichtprüfung 1 belegt.
|
|
3. **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).
|