Show netx database used size on the workbench storage gauge.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-06 09:36:17 +08:00
parent 155cb4e921
commit 938cd39c06
7 changed files with 178 additions and 2 deletions

View file

@ -0,0 +1,65 @@
"""PostgreSQL database size for workbench (used bytes only)."""
from __future__ import annotations
import threading
import time
from typing import Any
from sqlalchemy import text
from .config import settings
_CACHE_LOCK = threading.Lock()
_CACHED: dict[str, Any] | None = None
_CACHED_AT = 0.0
_CACHE_TTL_SEC = 30.0
def _open_session():
"""Indirection so tests can patch without importing the full engine."""
from .db import SessionLocal
return SessionLocal()
def _collect_uncached() -> dict[str, Any]:
url = str(settings.database_url or "")
out: dict[str, Any] = {
"used_bytes": 0,
"source": "none",
}
if url.startswith("sqlite"):
out["source"] = "sqlite"
out["error"] = "not_applicable"
return out
try:
with _open_session() as db:
row = db.execute(
text(
"SELECT current_database() AS db_name, "
"pg_database_size(current_database())::bigint AS used_bytes"
)
).mappings().one()
out["db_name"] = str(row["db_name"] or "")
out["used_bytes"] = int(row["used_bytes"] or 0)
out["source"] = "pg_database_size"
except Exception as exc: # noqa: BLE001
out["source"] = "error"
out["error"] = str(exc)[:200]
return out
def collect_db_storage_metrics() -> dict[str, Any]:
"""Return PG used-size dict; never raises. Cached ~30s."""
global _CACHED, _CACHED_AT
now = time.monotonic()
with _CACHE_LOCK:
if _CACHED is not None and (now - _CACHED_AT) < _CACHE_TTL_SEC:
return dict(_CACHED)
out = _collect_uncached()
with _CACHE_LOCK:
_CACHED = dict(out)
_CACHED_AT = time.monotonic()
return out

View file

@ -12,6 +12,7 @@ from .audit_async import audit_queue_status
from .cli_budget import cli_budget_status
from .config import settings
from .db import db_pool_status
from .db_storage_metrics import collect_db_storage_metrics
from .host_metrics import collect_host_metrics
from .oclaw_alarm_forwarder import forwarder_status
@ -26,6 +27,7 @@ def collect_runtime_metrics() -> dict[str, Any]:
"pid": os.getpid(),
"thread_count": threading.active_count(),
"db_pool": db_pool_status(),
"db_storage": collect_db_storage_metrics(),
"cli_budget": cli_budget_status(),
"audit_queue": audit_queue_status(),
"oclaw_forwarder": forwarder_status(),
@ -142,6 +144,9 @@ def _prom_lines(metrics: dict[str, Any]) -> str:
lines.append(f'netx_host_mem_used_bytes {host["mem_used_bytes"]}')
if host.get("mem_total_bytes") is not None:
lines.append(f'netx_host_mem_total_bytes {host["mem_total_bytes"]}')
storage = metrics.get("db_storage") or {}
if storage.get("used_bytes") is not None:
lines.append(f'netx_db_storage_used_bytes {int(storage.get("used_bytes") or 0)}')
lines.append("")
return "\n".join(lines)