diff --git a/dDashboardServer/routes/DashboardRoutes.cpp b/dDashboardServer/routes/DashboardRoutes.cpp
index 3cddd1389..77b247d1e 100644
--- a/dDashboardServer/routes/DashboardRoutes.cpp
+++ b/dDashboardServer/routes/DashboardRoutes.cpp
@@ -450,6 +450,7 @@ void RegisterDashboardRoutes() {
SimplePage("/diagnostics", Perm("health_view"), "diagnostics.jinja2", "diagnostics", "Packets, bytes and HTTP requests per second of every server");
SimplePage("/network", Perm("health_view"), "network.jinja2", "network", "Live diagram of the traffic between players, the servers and web clients, and the traffic of each connection");
SimplePage("/performance", Perm("health_view"), "performance.jinja2", "performance", "Main loop frame times and phases of every server, slow frames with their scopes, and profiling sessions with a flame graph");
+ SimplePage("/database_usage", Perm("health_view"), "database_usage.jinja2", "database_usage", "Database size over time, the fastest-growing tables, every table's size and rows, and free disk space");
SimplePage("/players", Perm("players_view"), "players.jinja2", "players", "Who is online, with kick, rescue and teleport");
SimplePage("/backups", Perm("backups"), "backups.jinja2", "backups", "Database backups");
// The GM level tabs need permissions_manage, the grants tab grants_manage
diff --git a/dDashboardServer/static/js/perf-charts.js b/dDashboardServer/static/js/perf-charts.js
index bdc58685c..d1abd57f7 100644
--- a/dDashboardServer/static/js/perf-charts.js
+++ b/dDashboardServer/static/js/perf-charts.js
@@ -138,7 +138,8 @@
/**
* A time chart: lines ([{label, color, values, dash}]) or a stack of areas (stacked: true), with a crosshair and
- * every series' value in the tooltip; `threshold` draws a dashed line (the slow frame threshold)
+ * every series' value in the tooltip; `threshold` draws a dashed line (the slow frame threshold); `timeLabel(t, long)`
+ * names a time (default: the time of day)
*/
function timeChart(svg, times, series, options) {
options = options || {};
@@ -170,7 +171,7 @@
[0, Math.floor((n - 1) / 2), n - 1].forEach(function (i) {
if (i < 0 || !times.length) return;
el('text', { x: X(i), y: height - 4, 'text-anchor': i === 0 ? 'start' : i === n - 1 ? 'end' : 'middle', class: 'axis' }, svg).textContent =
- new Date(times[i] * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: n <= 400 ? '2-digit' : undefined });
+ (options.timeLabel ? options.timeLabel(times[i]) : new Date(times[i] * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: n <= 400 ? '2-digit' : undefined }));
});
if (options.stacked) {
var base = new Array(n).fill(0);
@@ -213,7 +214,7 @@
return '
How much room the game database takes, measured by the Database usage task (hourly by default) and kept for
+ database_usage_days. Disk warning: database_disk_warn_percent (Settings).
+
+
+ {% if can.tasks_manage %}{% endif %}
+
+
+
+
+
+
+
+
+
+
No measurements yet. The Database usage task runs every hour{% if can.tasks_manage %}, or use Measure now{% endif %}.
+
+
+
Database
-
+
Data / indexes
-
+
Rows
-
+
Disk free
-
+
+
+
+
+
Size
+
+
Tables
+
+
Table
Total
Data
Indexes
+
Rows
Growth
New rows
+
+
+
+
Fastest-growing
+
+
+{% endblock %}
+
+{% block scripts %}
+
+
+{% endblock %}
diff --git a/dDashboardServer/templates/header.jinja2 b/dDashboardServer/templates/header.jinja2
index 218253c23..ea8ccbf50 100644
--- a/dDashboardServer/templates/header.jinja2
+++ b/dDashboardServer/templates/header.jinja2
@@ -105,7 +105,7 @@
{% endif %}
- {% set logsPages = ["health", "instances", "diagnostics", "network", "performance", "activity_log", "command_log", "audit_log", "system_log", "client_sysinfo"] %}
+ {% set logsPages = ["health", "instances", "diagnostics", "network", "performance", "database_usage", "activity_log", "command_log", "audit_log", "system_log", "client_sysinfo"] %}
{% if can.health_view or can.logs_activity or can.logs_command or can.logs_audit or can.logs_system or can.client_sysinfo %}
- {% if can.health_view %}Server HealthInstance LoadDiagnosticsNetworkPerformance{% endif %}
+ {% if can.health_view %}Server HealthInstance LoadDiagnosticsNetworkPerformanceDatabase Usage{% endif %}
{% if can.logs_activity %}Activity Log{% endif %}
{% if can.logs_command %}Command Log{% endif %}
{% if can.logs_audit %}Audit Log{% endif %}
diff --git a/docs/Dashboard.md b/docs/Dashboard.md
index 84f5c2462..bec95421c 100644
--- a/docs/Dashboard.md
+++ b/docs/Dashboard.md
@@ -911,6 +911,22 @@ The built-in view shows only instrumented scopes. For a native deep dive:
- Or a sampling profiler on the server process: on Linux `perf record -g -p ` then `perf script` into a flame graph
tool; any native profiler on Windows and macOS. The dashboard doesn't run native profilers itself.
+### Database usage
+
+**Database Usage** (`/database_usage`, `health_view`) shows how much room the game database takes: its size, data and
+indexes, rows and the free space on its disk; a size chart over the last day, week or 30 days; the fastest-growing tables
+over that range; and every table's size, rows and growth (click a heading to sort).
+
+- The **Database usage** task (Tasks page, `0 * * * *` by default; **Measure now** on the page with `tasks_manage`) measures
+ on the background connection: MySQL/MariaDB from `information_schema.TABLES` (rows are the server's estimate; on MySQL 8
+ the session asks for fresh statistics), SQLite from its page counts and the `dbstat` table (rows counted).
+- Samples go to `database_usage` and `database_usage_tables`, kept for `database_usage_days` (365; pruned by Log pruning).
+- Disk free: of `database_disk_path` when set, else the SQLite file's disk, or the MySQL data directory (`@@datadir`) when
+ `mysql_host` is this machine; otherwise not shown. Under `database_disk_warn_percent` (10; 0 never) the page warns and
+ a "server" alert goes to subscribed webhooks once (again after it recovers and drops again).
+- Settings: Settings > Dashboard > Database usage, and Data retention.
+- `GET /api/database/usage?range=day|week|month`.
+
### Logs and crash dumps
**System Log** (GM 8+, `logs_system`) shows the end of any server's log file (the newest by default; pick an older one