check retention
This commit is contained in:
@@ -3,3 +3,7 @@
|
|||||||
DOCKER_REGISTRY=docker.io
|
DOCKER_REGISTRY=docker.io
|
||||||
DOCKER_IMAGE=myorg/myapp
|
DOCKER_IMAGE=myorg/myapp
|
||||||
IMAGE_TAG=latest
|
IMAGE_TAG=latest
|
||||||
|
|
||||||
|
# Optional: check retention (limits DB growth)
|
||||||
|
# CHECK_RETENTION_COUNT=5000 # keep last N checks per service (default 5000)
|
||||||
|
# CHECK_RETENTION_DAYS=30 # also delete checks older than N days (0=disabled)
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -42,6 +42,21 @@ docker run -p 8080:8080 -v $(pwd)/data:/app/data myapp:test
|
|||||||
|
|
||||||
Add services from the dashboard (e.g. `https://example.com`, `google.com:443` for TCP) and view reports.
|
Add services from the dashboard (e.g. `https://example.com`, `google.com:443` for TCP) and view reports.
|
||||||
|
|
||||||
|
### Check Retention
|
||||||
|
|
||||||
|
To limit database growth, the app prunes old checks every 15 minutes:
|
||||||
|
|
||||||
|
| Env var | Default | Description |
|
||||||
|
|---------|---------|-------------|
|
||||||
|
| `CHECK_RETENTION_COUNT` | 5000 | Keep last N checks per service |
|
||||||
|
| `CHECK_RETENTION_DAYS` | 0 (disabled) | Also delete checks older than N days |
|
||||||
|
|
||||||
|
Example: keep 2000 checks per service and drop anything older than 30 days:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -e CHECK_RETENTION_COUNT=2000 -e CHECK_RETENTION_DAYS=30 ...
|
||||||
|
```
|
||||||
|
|
||||||
## Jenkins Pipeline
|
## Jenkins Pipeline
|
||||||
|
|
||||||
The pipeline:
|
The pipeline:
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ def report(service_id):
|
|||||||
status_filter = request.args.get("status")
|
status_filter = request.args.get("status")
|
||||||
search = request.args.get("search", "").strip() or None
|
search = request.args.get("search", "").strip() or None
|
||||||
page = max(1, int(request.args.get("page", 1)))
|
page = max(1, int(request.args.get("page", 1)))
|
||||||
per_page = min(100, max(10, int(request.args.get("per_page", 25))))
|
per_page = min(100, max(10, int(request.args.get("per_page", 10))))
|
||||||
stats = models.get_report_stats(service_id, from_ts=from_ts, to_ts=to_ts)
|
stats = models.get_report_stats(service_id, from_ts=from_ts, to_ts=to_ts)
|
||||||
checks_total = models.get_checks_count(service_id, from_ts=from_ts, to_ts=to_ts, status_filter=status_filter, search=search)
|
checks_total = models.get_checks_count(service_id, from_ts=from_ts, to_ts=to_ts, status_filter=status_filter, search=search)
|
||||||
checks = models.get_checks(
|
checks = models.get_checks(
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
DATA_PATH = os.environ.get("DATA_PATH", "/app/data")
|
DATA_PATH = os.environ.get("DATA_PATH", "/app/data")
|
||||||
DB_PATH = Path(DATA_PATH) / "monitor.db"
|
DB_PATH = Path(DATA_PATH) / "monitor.db"
|
||||||
|
|
||||||
|
# Retention: keep last N checks per service, and optionally drop checks older than N days
|
||||||
|
CHECK_RETENTION_COUNT = int(os.environ.get("CHECK_RETENTION_COUNT", "5000"))
|
||||||
|
CHECK_RETENTION_DAYS = int(os.environ.get("CHECK_RETENTION_DAYS", "0")) or None
|
||||||
|
|
||||||
|
|
||||||
def _ensure_data_dir():
|
def _ensure_data_dir():
|
||||||
Path(DATA_PATH).mkdir(parents=True, exist_ok=True)
|
Path(DATA_PATH).mkdir(parents=True, exist_ok=True)
|
||||||
@@ -232,3 +236,38 @@ def get_all_services_for_scheduler():
|
|||||||
with get_db() as conn:
|
with get_db() as conn:
|
||||||
rows = conn.execute("SELECT id, target, protocol, interval_seconds FROM services").fetchall()
|
rows = conn.execute("SELECT id, target, protocol, interval_seconds FROM services").fetchall()
|
||||||
return [dict(r) for r in rows]
|
return [dict(r) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
|
def prune_checks_retention() -> int:
|
||||||
|
"""
|
||||||
|
Remove old checks to limit storage. Keeps last CHECK_RETENTION_COUNT per service.
|
||||||
|
If CHECK_RETENTION_DAYS is set, also deletes checks older than that.
|
||||||
|
Returns number of rows deleted.
|
||||||
|
"""
|
||||||
|
with get_db() as conn:
|
||||||
|
deleted = 0
|
||||||
|
# Delete checks older than N days (if configured)
|
||||||
|
if CHECK_RETENTION_DAYS:
|
||||||
|
cutoff = (datetime.now(timezone.utc) - timedelta(days=CHECK_RETENTION_DAYS)).isoformat()
|
||||||
|
cur = conn.execute("DELETE FROM checks WHERE timestamp < ?", (cutoff,))
|
||||||
|
deleted += cur.rowcount
|
||||||
|
|
||||||
|
# Keep only last N checks per service
|
||||||
|
service_ids = [r[0] for r in conn.execute("SELECT id FROM services").fetchall()]
|
||||||
|
for sid in service_ids:
|
||||||
|
# Get ids of checks to keep (most recent N)
|
||||||
|
keep_ids = conn.execute(
|
||||||
|
"SELECT id FROM checks WHERE service_id = ? ORDER BY timestamp DESC LIMIT ?",
|
||||||
|
(sid, CHECK_RETENTION_COUNT),
|
||||||
|
).fetchall()
|
||||||
|
keep_ids = [r[0] for r in keep_ids]
|
||||||
|
if not keep_ids:
|
||||||
|
continue
|
||||||
|
placeholders = ",".join("?" * len(keep_ids))
|
||||||
|
cur = conn.execute(
|
||||||
|
f"DELETE FROM checks WHERE service_id = ? AND id NOT IN ({placeholders})",
|
||||||
|
[sid] + keep_ids,
|
||||||
|
)
|
||||||
|
deleted += cur.rowcount
|
||||||
|
|
||||||
|
return deleted
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
|
||||||
from app.checker import run_check
|
from app.checker import run_check
|
||||||
from app.models import get_all_services_for_scheduler
|
from app.models import get_all_services_for_scheduler, prune_checks_retention
|
||||||
|
|
||||||
|
|
||||||
def _run_all_checks():
|
def _run_all_checks():
|
||||||
@@ -54,4 +54,7 @@ def start_scheduler():
|
|||||||
# Sync job list every 60 seconds (only adds/removes when services change)
|
# Sync job list every 60 seconds (only adds/removes when services change)
|
||||||
scheduler.add_job(sync_jobs, "interval", seconds=60, id="sync_jobs")
|
scheduler.add_job(sync_jobs, "interval", seconds=60, id="sync_jobs")
|
||||||
|
|
||||||
|
# Prune old checks every 15 minutes (retention/compression)
|
||||||
|
scheduler.add_job(prune_checks_retention, "interval", minutes=15, id="prune_checks")
|
||||||
|
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ services:
|
|||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
environment:
|
environment:
|
||||||
- VERSION=${IMAGE_TAG:-latest}
|
- VERSION=${IMAGE_TAG:-latest}
|
||||||
|
# Optional: CHECK_RETENTION_COUNT=5000, CHECK_RETENTION_DAYS=30
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
Reference in New Issue
Block a user