agent v1.4: MySQL/MariaDB live metrics (_mysql_stats)
Same fields as the postgres variant: sizes/tables/rows/top tables from information_schema.TABLES, connections/uptime from SHOW GLOBAL STATUS, InnoDB buffer-pool hit ratio as cache_hit_pct. Best-effort like the rest — any failure returns None and never disturbs the heartbeat.
This commit is contained in:
+43
-2
@@ -22,7 +22,7 @@ import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
VERSION = "1.3"
|
||||
VERSION = "1.4"
|
||||
RESULT_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".dump-result.json")
|
||||
|
||||
|
||||
@@ -81,9 +81,11 @@ def db_version(t):
|
||||
def db_stats(t):
|
||||
"""Read-only live metrics for the dashboard's 'nerd stats' — size, tables, rows, index
|
||||
footprint, connections, cache-hit ratio, the biggest tables, server version + uptime.
|
||||
Best-effort: any failure returns None and never disturbs the heartbeat. Postgres only."""
|
||||
Best-effort: any failure returns None and never disturbs the heartbeat."""
|
||||
if t["engine"] == "sqlite":
|
||||
return _sqlite_stats(t)
|
||||
if t["engine"] in ("mysql", "mariadb"):
|
||||
return _mysql_stats(t)
|
||||
if t["engine"] != "postgres":
|
||||
return None
|
||||
env = dict(os.environ, PGPASSWORD=t["pw"])
|
||||
@@ -120,6 +122,45 @@ def db_stats(t):
|
||||
return None
|
||||
|
||||
|
||||
def _mysql_stats(t):
|
||||
"""Live metrics for MySQL/MariaDB, same fields as the postgres variant. Sizes and row
|
||||
counts come from information_schema.TABLES (InnoDB estimates — same spirit as
|
||||
n_live_tup); cache hit is the InnoDB buffer pool hit ratio."""
|
||||
def q(sql):
|
||||
r = subprocess.run(("mysql", "-h", t["host"], "-P", t["port"], "-u", t["user"],
|
||||
f"-p{t['pw']}", "-N", "-B", t["db"], "-e", sql),
|
||||
capture_output=True, text=True, timeout=30)
|
||||
return r.stdout.strip()
|
||||
|
||||
def status(var):
|
||||
out = q(f"SHOW GLOBAL STATUS LIKE '{var}'")
|
||||
return int(out.split("\t")[1]) if "\t" in out else 0
|
||||
|
||||
try:
|
||||
size, idx, tables, rows = (int(float(v or 0)) for v in q(
|
||||
"SELECT COALESCE(SUM(data_length+index_length),0),"
|
||||
" COALESCE(SUM(index_length),0), COUNT(*), COALESCE(SUM(table_rows),0)"
|
||||
" FROM information_schema.TABLES"
|
||||
" WHERE table_schema=DATABASE() AND table_type='BASE TABLE'").split("\t"))
|
||||
top = [{"name": f[0], "bytes": int(f[1]), "rows": int(f[2])}
|
||||
for f in (line.split("\t") for line in q(
|
||||
"SELECT table_name, data_length+index_length, COALESCE(table_rows,0)"
|
||||
" FROM information_schema.TABLES"
|
||||
" WHERE table_schema=DATABASE() AND table_type='BASE TABLE'"
|
||||
" ORDER BY data_length+index_length DESC LIMIT 10").splitlines())
|
||||
if len(f) == 3]
|
||||
req = status("Innodb_buffer_pool_read_requests")
|
||||
miss = status("Innodb_buffer_pool_reads")
|
||||
return {"size_bytes": size, "tables": tables, "rows_est": rows,
|
||||
"index_bytes": idx, "connections": status("Threads_connected"),
|
||||
"cache_hit_pct": round(100.0 * (req - miss) / req, 2) if req else None,
|
||||
"server_version": q("SELECT VERSION()"),
|
||||
"uptime_s": status("Uptime") or None,
|
||||
"top_tables": top}
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _sqlite_stats(t):
|
||||
"""Live metrics for a SQLite file: file size, table count, row totals, largest tables."""
|
||||
def q(sql):
|
||||
|
||||
Reference in New Issue
Block a user