Relational Database Monitoring in the agent
The agent reads each engine's own statistics views over a read-only session inside your network. Nothing needs the database to be reachable from Watchtower, and no exporter runs next to it. DSNs live only in the agent's own config file. Postgres is covered in integrations.md. All engines share one model:
Per-statement series (
dbm: true): the engine's cumulative statement statistics are read every round and turned into deltas. The agent keeps its own running totals, so a reset, eviction or window rollover on the engine side does not breakrate(). Up to 200 statements per round, busiest first. Labels:database, the engine's statement id (digest,query_hash,sql_id,fingerprint_id,queryid) and the bind-safequery.Activity samples (
collect_samples: true): the running statements are read every second. Every 10 s the agent shipsdb.activity.sessions{db.namespace,db.session.state,db.wait_event_type,db.wait_event}(average active sessions;CPUwhen not waiting) and onedb.query.samplelog record per statement and wait.Plans (
explain: true): a sampled SELECT is planned at most once per 10 minutes, at most 5 per 10 s, with a 2 s timeout. Plans arrive asdb.query.planlog records withdb.plan.total_cost.Join key:
db.query.hash(see sql-normalization.md) is computed from the raw statement before it is made bind-safe. It is stamped on activity samples, on sampled plans, on slow-log samples, and on the per-statement series of the engines whose statistics keep raw or$ntext (mssql, oracle, yugabytedb). It is the same hash spans carry. MySQL, TiDB and CockroachDB digests are already normalized by the engine and have no raw text, so their series join through the samples instead.Privacy: statement text is bind-safe (every literal becomes
?). Every string in a plan goes through the same scrubber, and so do the SQL Server showplan attributes that carry literals. Deadlock and lock records never include row data.
Module | Always |
|
|
|
|---|---|---|---|---|
mysql (MariaDB too) | status counters, InnoDB buffer pool ( |
|
|
|
mssql | performance counters, wait stats, blocked requests |
|
| the cached plan from |
oracle ( |
|
|
|
|
cockroachdb | live/total nodes, per-store ranges, unavailable and under-replicated ranges, leaseholders, capacity, contention per table |
| n/a | the plan CockroachDB samples per fingerprint |
tidb | TiKV/TiFlash stores (up, regions, leaders, capacity), |
| n/a |
|
yugabytedb | tablet servers and connections ( |
| n/a | n/a |
sqlite | header-only reads of the files in | |||
db_slow_log | tails |
Grants
-- MySQL / MariaDB
CREATE USER 'wt_monitor'@'%' IDENTIFIED BY '...';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON performance_schema.* TO 'wt_monitor'@'%';
GRANT SELECT ON sys.* TO 'wt_monitor'@'%';
-- waits in samples need: UPDATE performance_schema.setup_consumers SET ENABLED='YES' WHERE NAME='events_waits_current';
-- explain needs SELECT on the application schemas (EXPLAIN never runs the statement).
-- SQL Server
GRANT VIEW SERVER STATE TO wt_monitor; -- plus VIEW DATABASE STATE in each database with Query Store
-- Oracle
GRANT CREATE SESSION, SELECT_CATALOG_ROLE TO wt_monitor;
-- CockroachDB
GRANT SYSTEM VIEWACTIVITY TO wt_monitor;Oracle reads no Diagnostics Pack views (v$active_session_history, DBA_HIST_*). Without dsn, the oracle module keeps scraping oracledb_exporter at url. YugabyteDB views are per node, so configure one module per tserver to cover a universe. The sqlite module never opens the database through SQLite and takes no lock. The WAL generation age counts from when the agent first saw the current WAL generation, so it reads low right after an agent restart.