automatic DB maintenance: log rotation, orphan cleanup, VACUUM

The existing daily cleanup never effectively ran: it was an interval
job counted from container start (reset on every deploy), it called
asyncio.get_event_loop() from a worker thread, and even when deleting,
SQLite never returns freed pages to the filesystem. Live DB: 4.1 GB,
of which 2.6 GB log and 0.6 GB unused pages.

New maintenance service, scheduled daily at MAINTENANCE_HOUR_UTC:30
and once 5 minutes after every start:
- delete log entries older than LOG_RETENTION_DAYS (30) and keep at
  most LOG_MAX_ENTRIES (200k) newest ones
- delete processed_mails markers of deleted rules/accounts (FKs are
  not enforced by SQLite)
- VACUUM when >25% of the file is unused, only if free disk space
  covers the copy; never runs twice in parallel

GET/POST /api/logs/maintenance expose status and a manual run; the log
menu shows DB size, entry count and last run, with "Datenbank
aufräumen & verkleinern". Removed the dead cleanup_old_logs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 11:04:15 +02:00
co-authored by Claude Opus 5.5
parent 9bd2507cb1
commit 7d03c62d8e
7 changed files with 251 additions and 24 deletions
+6
View File
@@ -12,6 +12,12 @@ class Settings(BaseSettings):
# Ausführliches Verarbeitungslog (jede Nicht-Übereinstimmung, jede Regel pro
# Poll). Erzeugt Millionen Einträge — nur zur Fehlersuche einschalten.
log_verbose: bool = False
# Log-Rotation: Einträge älter als N Tage löschen und höchstens so viele
# behalten (die neuesten). Läuft täglich zur angegebenen Stunde (UTC) und
# einige Minuten nach jedem Start.
log_retention_days: int = 30
log_max_entries: int = 200_000
maintenance_hour_utc: int = 2
model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}