Stichprobenbasierter Scrub-Job: nimmt BAK-05s existing_in_storage, priorisiert nach eigenem scrub_state.last_scrubbed_at (nicht file_revisions.created_at, sonst kein echtes Rotationsverhalten), prueft Inhalt per SHA-256 gegen file_revisions.checksum_sha256. Meldung ueber echten dauerhaften /metrics-Endpunkt (Pull-Modell, OPS-03 scrapt, kein Push), Counter monoton steigend. Real registriert in Core metrics_sources, End-zu-Ende ueber OPS-03-Aggregator bestaetigt, realer Befund-Durchlauf mit absichtlich falscher Pruefsumme durchgefuehrt.
7.9 KiB
BAK-08 – Prüfprotokoll: Checksum-basierte Objekt-Integritätsprüfung
Voraussetzungen BAK-05, FDN-04, FDN-09, OPS-05 – alle erledigt, siehe eigene Protokolle. Vor Start zwei offene Rückfragen geklärt (siehe unten).
Grundsatzentscheidung: eigener Zustand statt file_revisions.created_at
created_at als Alterskriterium hätte immer dieselben "ältesten" Objekte
gescrubbt und den Rest nie erreicht — kein echtes Rotationsverhalten.
Stattdessen eigene Archive-Tabelle scrub_state (storage_key →
last_scrubbed_at, last_result), Migration
migrations/0001_scrub_state.up.sql. internal/scrub.Sample ist eine
reine Funktion: nimmt BAK-05s existing_in_storage (deterministisch
sortiert) entgegen, filtert Objekte innerhalb der konfigurierbaren
Cooldown-Frist heraus, priorisiert danach nach last_scrubbed_at
aufsteigend (nie geprüft = ältestmöglicher Wert), begrenzt auf die
konfigurierte Stichprobengröße — kein Voll-Sort über den gesamten
Bestand bei jedem Lauf (Nutzerhinweis zum Kostenfaktor bei 10⁵+
Objekten: die WHERE-artige Cooldown-Filterung reduziert die Kandidatenmenge
VOR der Sortierung, nur die Kandidaten selbst werden sortiert, nicht der
komplette Bestand).
Nachtrag: zwei Rückfragen vor Implementierungsbeginn geklärt
- OPS-05-Anbindung ist Pull, nicht Push. OPS-05 (
internal/alerting, Core) ist real implementiert, aber Core OPS-03 scrapt/metrics-URLs registrierter Module (metrics_sources-Tabelle in der Core-Registry- DB,SourceStore.RegisterSource) — kein Push-API. Für BAK-08 daher ein eigener, DAUERHAFT laufender Endpunkt (cmd/scrub-metrics, getrennt vom Oneshot-scrub-cli, dessen Prozess nach jedem Lauf endet und zum Scrape-Zeitpunkt nicht erreichbar wäre). Metrik als Counter (nexarch_archive_storage_integrity_failures_total), monoton steigend — kein Gauge, kein Rücksetzen bei behobenem Befund. Kein Befund = Wert bleibt unverändert (kein Dauer-Alarm durch andauernden "Fehler"-Zustand). Scope-Trennung gewahrt:scrub-cli/scrub-metricserzeugen selbst KEIN Alert-Objekt — Schwellwert/Drosselung bleiben OPS-05-eigene Konfiguration (Alert-Regel wird separat überalerting.RuleStore.CreateRuleangelegt, nicht Teil dieses Tickets). CFG-04 war eine Verwechslung (das ist die Benachrichtigungs-Einstellungen-Oberfläche, ein anderes Ticket) — die tatsächlich nötige "Config"-Aktion ist einINSERTinmetrics_sources(Core-Registry-DB), kein UI/Ticket-Abhängigkeit. Real ausgeführt (siehe „Echte Verdrahtung" unten). - Sampling-Kriterium. Siehe Grundsatzentscheidung oben —
scrub_state.last_scrubbed_atstattfile_revisions.created_at, Cooldown-Filterung vor Sortierung, feste Stichprobengröße (Top-N, deterministisch, keine Zufallsstichprobe — Nutzerpräferenz für Reproduzierbarkeit im Protokoll).
Umsetzung
migrations/0001_scrub_state.up.sql/.down.sql—scrub_state,scrub_counters(Einzelzeile, monotoner Zähler).internal/scrub.Sample— reine Funktion, Cooldown-Filter + Alt- Priorisierung + Stichprobenbegrenzung.internal/scrub.LoadLastScrubbed/MarkScrubbed/RecordFinding/FindingsTotal— DB-Zugriff aufscrub_state/scrub_counters,MarkScrubbedidempotent (ON CONFLICT) für unterbrechbare Läufe.internal/scrub.ExpectedChecksums— eigene, minimale Abfrage gegenfile_revisions(keine Erweiterung vonreconcile.DBEntry— BAK-05 bleibt existenz-only).internal/scrub.ActualChecksum— echtes Lesen der Datei + SHA-256, kein Header-/Größenvergleich.cmd/scrub-cli— Oneshot: BAK-05-Reconcile →Sample→ pro Kandidat Checksum-Vergleich →MarkScrubbed+ bei AbweichungRecordFinding→ JSON-Bericht auf stdout, Exit-Code 1 bei Befunden (gemeldet, nicht automatisch repariert).cmd/scrub-metrics— dauerhafter/metrics-Endpunkt, liestscrub_counters.findings_total.
Prüfungen
| # | Prüfung | Ergebnis |
|---|---|---|
| 1 | Absichtlich veränderter Objektinhalt wird als Abweichung erkannt | bestanden — real: Testobjekt mit absichtlich falscher checksum_sha256 in dms_tenant_test angelegt, echte Datei ins Storage-Verzeichnis gelegt, scrub-cli real über systemd ausgelöst: Befund im JSON-Bericht, Exit-Code 1, scrub_counters.findings_total real von 0 auf 1 erhöht (siehe Journal-Auszug unten) |
| 2 | Sampling priorisiert alte/nie geprüfte Objekte, nicht neue | bestanden — TestSample_PrioritizesNeverScrubbedAndOldest: nie geprüftes Objekt kommt vor einem vor 30 Tagen geprüften, dieses vor einem vor 1 Tag geprüften |
| 3 | Wiederholter Lauf ohne neue Objekte meldet nichts erneut (kein Spam) / idempotent bei Unterbrechung | bestanden — real: zweiter scrub-cli-Lauf direkt nach dem ersten liefert sampled: 0 (Cooldown greift), TestMarkScrubbed_IsIdempotent beweist wiederholtes Markieren ohne Duplikat |
Zusätzlich: TestSample_RespectsCooldown,
TestSample_LimitsToSampleSize, TestSample_DeterministicForIdenticalInput,
TestRecordFinding_IsMonotonicallyIncreasing,
TestActualChecksum_MatchesRealFileContent (echter Dateiinhalt, echtes
SHA-256), TestExpectedChecksums_ReadsRealFileRevisions (echtes
Postgres, kein Mock).
Echte Verdrahtung auf 192.168.1.131
scrub-cli,scrub-metricsgebaut nach/opt/nexarch-archive/bin//etc/nexarch/archive-scrub.env,/etc/nexarch/archive-scrub-metrics.env(0600)- Migration real gegen
dms_tenant_testangewendet (psql -f migrations/0001_scrub_state.up.sql) nexarch-archive-scrub.timerinstalliert/aktiviert (täglich 06:00 UTC),nexarch-archive-scrub-metrics.serviceinstalliert/aktiviert (dauerhaft,Restart=on-failure) — beidesystemctl status: aktiv- Reales
INSERTinmetrics_sources(Core-Registry-DBnexarch_registry):('archive', 'http://127.0.0.1:8090/metrics')— bestätigt überSELECT * FROM metrics_sources - End-to-End über OPS-03 bestätigt:
curl http://127.0.0.1:8085/metrics(Core-Aggregator) zeigtnexarch_module_archive_nexarch_archive_storage_integrity_failures_total— reale Umbenennung gemäß OPS-03-Namenskonvention, kein synthetischer Wert - Realer Befund-Durchlauf: Testobjekt mit absichtlich falscher Prüfsumme
angelegt →
scrub-clireal viasystemctl startausgelöst → Befund im Journal,scrub_counters.findings_totalreal 0→1, sichtbar sowohl aufscrub-metricsals auch über den Core-Aggregator → Testdaten anschließend bereinigt (file_revisions/documents/users-Zeilen gelöscht,scrub_state/scrub_counterszurückgesetzt, Testdatei entfernt)
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 -> 4/4 Pakete mit Tests ok (internal/backup, internal/objectbackup, internal/reconcile, internal/scrub), 0 Fehlschläge
internal/scrub-Tests separat mit gesetzter TEST_TENANT_DSN gegen
dms_tenant_test verifiziert: 8/8 Tests bestanden.
Bekannte Grenze (aus Ticket übernommen, nicht Teil der Abnahme)
Der Job erkennt Abweichungen nur bei Objekten, die gelesen und erneut
geprüft werden können. Ersetzt keine storage-seitige WORM-/
Versionierungsstrategie und keine Zugriffs-/Audit-Logs des
Storage-Providers (STORAGE-KONZEPT.md Abschnitt 6.1) — bei extern
eingebundenem, nicht-kompatiblem Kunden-Storage (Betriebsmodus 3, ohne
Versioning/Object Lock/Audit-Logs) bleibt eine Lücke, die BAK-08
technisch nicht schließen kann.
Gesamtergebnis
Bestanden. Alle sechs Akzeptanzkriterien und alle drei Pflicht-
prüfungen real erfüllt — inklusive echtem Ende-zu-Ende-Nachweis über
Core OPS-03/OPS-05 (kein Stub, reale /metrics-Registrierung und
-Aggregation). Beide vor Implementierungsbeginn gestellten Rückfragen
(OPS-05-Anbindungsmechanismus, Sampling-Kriterium) im Protokoll
dokumentiert und in der Umsetzung berücksichtigt.