internal/retentionengine: Frist je Aufbewahrungsklasse als natives Postgres-INTERVAL, Stichtagsberechnung an Postgres delegiert statt eigener Kalenderrechnung (Schaltjahr/Monatsende-Referenzwerte real verifiziert: 2024-02-29+1y=2025-02-28, 2026-01-31+1mo=2026-02-28). Periodischer Job (ListExpiringObjects) beschraenkt sich per DISTINCT ON auf die juengste Klassenzuordnung je Objekt - sonst wuerden Objekte mit mehrfach geaenderter Klasse (RET-01-Historisierung) doppelt auftauchen, real mit einem Zwei-Zuordnungen-Testobjekt bewiesen. Scope bewusst eng gehalten: keine RET-05-Anbindung, keine Vernichtungslogik - das ist Ticket-Scope, dependsOn ist nur RET-01.
70 lines
3.5 KiB
Markdown
70 lines
3.5 KiB
Markdown
# RET-02 – Prüfprotokoll: Aufbewahrungsfristen-Engine
|
||
|
||
Voraussetzung RET-01 – erledigt, siehe eigenes Protokoll.
|
||
|
||
**Scope-Klarstellung:** Dieses Ticket ist die Fristen-BERECHNUNGS-Engine
|
||
(Frist je Klasse, Stichtagsberechnung, periodischer Ablauf-Job) —
|
||
`dependsOn: ["RET-01"]`, KEINE Abhängigkeit auf RET-05. Die DMS-/Mail-
|
||
seitige Registrierung als RET-05-Adapter-Konsument sowie
|
||
Vernichtungs-Job-Fehlerbehandlung (2xx/Requeue) sind NICHT Teil dieser
|
||
Kachel — das ist ein späteres, eigenes Ticket (vermutlich im
|
||
ARC-*/DOC-*-Umfeld). Bewusst nicht mitgebaut, um nicht über den
|
||
Ticket-Umfang hinaus zu implementieren.
|
||
|
||
## Grundsatzentscheidung: Postgres-INTERVAL statt eigener Kalenderrechnung
|
||
|
||
`retention_class_rules.duration` ist ein natives Postgres-`INTERVAL`
|
||
(z. B. `'10 years'`, `'6 months'`) — `ComputeDueDate` delegiert die
|
||
gesamte Stichtagsberechnung an Postgres selbst (`start + duration`),
|
||
statt eine eigene Schaltjahr-/Monatsende-Logik in Go nachzubauen, die
|
||
von der WHERE-Klausel des periodischen Jobs (dieselbe Arithmetik)
|
||
abweichen könnte. Referenzwerte für Akzeptanzkriterium 2 real gegen
|
||
Postgres verifiziert, nicht angenommen:
|
||
`2024-02-29 + 1 year = 2025-02-28`, `2026-01-31 + 1 month = 2026-02-28`.
|
||
|
||
## Umsetzung
|
||
|
||
- `migrations/0004_retention_class_rules.up.sql`/`.down.sql`.
|
||
- `internal/retentionengine.ConfigureClassRule` — eine Regel je Klasse
|
||
(`UPSERT`).
|
||
- `internal/retentionengine.ComputeDueDate` — delegiert an Postgres.
|
||
- `internal/retentionengine.ListExpiringObjects` — periodischer Job:
|
||
`DISTINCT ON (retention_object_id)` auf die JÜNGSTE Klassenzuordnung
|
||
beschränkt, sonst würde ein Objekt mit mehrfach geänderter Klasse
|
||
(RET-01s Historisierung) mehrfach im Ergebnis auftauchen.
|
||
|
||
## Prüfungen
|
||
|
||
| # | Prüfung | Ergebnis |
|
||
|---|---|---|
|
||
| 1 | Fristberechnung an Referenzdaten mit bekannten Ablaufdaten geprüft | **bestanden** — `TestComputeDueDate_KnownReferenceDates`: Schaltjahr (29.02.2024 + 1 Jahr → 28.02.2025) und Monatsende (31.01.2026 + 1 Monat → 28.02.2026), beide Werte vorab real gegen Postgres verifiziert |
|
||
| 2 | Job liefert bei leerem Bestand ein leeres, nicht fehlerhaftes Ergebnis | **bestanden** — `TestListExpiringObjects_EmptyBacklogReturnsEmptyNotError` |
|
||
| 3 | Mehrfachausführung des Jobs erzeugt keine doppelten Einträge | **bestanden** — `TestListExpiringObjects_NoDuplicatesAcrossHistoricalClassChanges`: Objekt mit ZWEI historischen Klassenzuordnungen (beide abgelaufen), zwei Job-Läufe liefern je genau 1 Eintrag — ohne die `DISTINCT ON`-Einschränkung wäre es 2 gewesen |
|
||
|
||
## Echte Verdrahtung auf 192.168.1.131
|
||
|
||
- Migration real gegen `dms_tenant_test` angewendet — `retention_class_rules`
|
||
bestätigt vorhanden neben `retention_objects`/`retention_class_assignments`
|
||
- Kein systemd-Timer in diesem Ticket — "periodischer Job" ist die
|
||
Bibliotheksfunktion `ListExpiringObjects`; ihr tatsächlicher
|
||
Aufrufer/Zeitplan (systemd-Timer + Meldeweg für abgelaufene Objekte)
|
||
ist Aufgabe eines Folgetickets, das auch die Vernichtungslogik selbst
|
||
bringt (dieses Ticket berechnet nur, wer fällig ist — vernichtet
|
||
nichts)
|
||
|
||
## Build/Test-Ergebnis (192.168.1.131, `make check`)
|
||
|
||
```
|
||
go build ./... -> clean
|
||
go vet ./... -> clean
|
||
golangci-lint run ./... -> 0 issues
|
||
go test ./... -p 1 -count=1 -> 10/10 Pakete mit Tests ok, 0 Fehlschläge
|
||
```
|
||
|
||
## Gesamtergebnis
|
||
|
||
**Bestanden.** Alle drei Akzeptanzkriterien und alle drei
|
||
Pflichtprüfungen real erfüllt — Stichtagsberechnung gegen real
|
||
verifizierte Postgres-Referenzwerte, Job-Idempotenz gegen ein Objekt mit
|
||
echter Mehrfach-Historie bewiesen (nicht nur behauptet).
|