feat(docker): retention + hourly rollup for metrics/events with Settings windows
Bounds Docker time-series growth (the main scaling concern). New docker_metrics_hourly table + docker_006 migration; a plugin retention module (docker.run_retention capability) rolls raw docker_metrics older than the raw window into hourly averages (idempotent upsert), deletes the rolled raw rows, then prunes stale rollups + lifecycle events. Core cleanup.py drives it each hourly run via the capability (no plugin-model import), reading the three retention windows fresh from settings so changes apply without restart (rule 25). Settings → "Thresholds & Retention" gains a Docker retention card (raw / rolled-up / events windows, working defaults 7/90/30 days). Unit tests cover the hour-aligned cutoff/bucketing helpers; integration test exercises the real rollup-average + prune across both windows. Milestone 77 task #941. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Jg27rgypiW2efULXJDtMC
This commit is contained in:
@@ -127,6 +127,14 @@ _THRESHOLD_FIELDS = [
|
||||
("latency_warn", "ping.threshold.good_ms", False), ("latency_crit", "ping.threshold.warn_ms", False),
|
||||
]
|
||||
|
||||
# Docker time-series retention windows (days). Read fresh by the cleanup task,
|
||||
# so a save takes effect on the next hourly run — no app.config wiring needed.
|
||||
_RETENTION_FIELDS = [
|
||||
("docker_metrics_raw_days", "docker.retention.metrics_raw_days"),
|
||||
("docker_metrics_rollup_days", "docker.retention.metrics_rollup_days"),
|
||||
("docker_events_days", "docker.retention.events_days"),
|
||||
]
|
||||
|
||||
|
||||
@settings_bp.get("/thresholds/")
|
||||
@require_role(UserRole.admin)
|
||||
@@ -151,6 +159,15 @@ async def save_thresholds():
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
await set_setting(db, key, val)
|
||||
for field, key in _RETENTION_FIELDS:
|
||||
raw = form.get(field, "")
|
||||
if raw == "":
|
||||
continue
|
||||
try:
|
||||
val = max(1, int(raw)) # at least a day — 0 would prune everything
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
await set_setting(db, key, val)
|
||||
await _reload_app_config()
|
||||
await log_audit(current_app, session.get("user_id"), session.get("username", ""),
|
||||
"settings.saved", detail={"section": "thresholds"})
|
||||
|
||||
Reference in New Issue
Block a user