98 lines
3.3 KiB
Python
98 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Roll up and optionally prune persisted admin health samples."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
import os
|
|
import sys
|
|
from dataclasses import asdict
|
|
from pathlib import Path
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
API_ROOT = REPO_ROOT / "apps" / "api"
|
|
|
|
if hasattr(sys.stdout, "reconfigure"):
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
if hasattr(sys.stderr, "reconfigure"):
|
|
sys.stderr.reconfigure(encoding="utf-8")
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(
|
|
description=(
|
|
"Roll up app.admin_health_event into app.admin_health_daily_rollup "
|
|
"and optionally prune old raw samples."
|
|
)
|
|
)
|
|
parser.add_argument("--rollup-days", type=int, required=True, help="Roll up raw samples older than this many days.")
|
|
parser.add_argument("--retention-days", type=int, required=True, help="Prune raw samples older than this many days.")
|
|
parser.add_argument("--apply", action="store_true", help="Mutate the database. Default is dry-run.")
|
|
parser.add_argument(
|
|
"--allow-non-dev-apply",
|
|
action="store_true",
|
|
help="Required with --apply outside ENVIRONMENT=dev.",
|
|
)
|
|
parser.add_argument("--json", action="store_true", help="Print compact JSON.")
|
|
return parser
|
|
|
|
|
|
def _prepare_imports() -> None:
|
|
os.chdir(API_ROOT)
|
|
api_root = str(API_ROOT)
|
|
if api_root not in sys.path:
|
|
sys.path.insert(0, api_root)
|
|
|
|
|
|
async def _run(args: argparse.Namespace) -> dict[str, object]:
|
|
_prepare_imports()
|
|
from app.auth_sessions import ensure_runtime_tables
|
|
from app.config import settings
|
|
from app.db import close_pool, init_pool
|
|
from app.services.admin_health_maintenance import maintain_admin_health_events
|
|
|
|
if args.apply and settings.environment != "dev" and not args.allow_non_dev_apply:
|
|
raise RuntimeError("--apply outside ENVIRONMENT=dev requires --allow-non-dev-apply")
|
|
|
|
await init_pool()
|
|
try:
|
|
await ensure_runtime_tables()
|
|
result = await maintain_admin_health_events(
|
|
rollup_days=args.rollup_days,
|
|
retention_days=args.retention_days,
|
|
apply=args.apply,
|
|
)
|
|
finally:
|
|
await close_pool()
|
|
return asdict(result)
|
|
|
|
|
|
def _print_result(result: dict[str, object], *, as_json: bool) -> None:
|
|
if as_json:
|
|
print(json.dumps(result, ensure_ascii=False, separators=(",", ":")))
|
|
return
|
|
mode = "applied" if result["applied"] else "dry-run"
|
|
print(
|
|
"{mode}: rollup_events={rollup_event_count}, rollup_buckets={rollup_bucket_count}, "
|
|
"upserted_rollups={upserted_rollups}, prunable_events={prunable_event_count}, "
|
|
"deleted_events={deleted_events}".format(mode=mode, **result)
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
args = build_parser().parse_args()
|
|
try:
|
|
result = asyncio.run(_run(args))
|
|
except Exception as exc: # noqa: BLE001 - CLI should report compact failure.
|
|
payload = {"ok": False, "error_type": exc.__class__.__name__, "error": str(exc)}
|
|
print(json.dumps(payload, ensure_ascii=False, separators=(",", ":")), file=sys.stderr)
|
|
return 2
|
|
_print_result(result, as_json=args.json)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|