InfraGuard

Registering a Database Collector

Register an access=database collector as per-host db@alias rows to drive Service ▸ Database and Alerts, storing the password with Crypt or reusing it via cred_ref=env:PREFIX.

Overview — what it does

Administration ▸ Collectors is InfraGuard's collector registry (the infr_collector table). It lifts hardcoded credentials and host maps out of code and stores them as data. Collectors are abstracted by access method (access).

access Purpose Drives
web Remote agent endpoint (tlx probe · run_report) Infra ▸ Health
database Direct connection to a registered DB to collect load metrics Service ▸ Database · Infra ▸ Alerts
agent Internal-network representative collector (Redis + nginx‑vts) Service ▸ Sessions · Service ▸ Web

This article covers the access=database collector. The InfraGuard app periodically connects to a registered DB and reads SHOW GLOBAL STATUS · information_schema.processlist · information_schema.tables, writing a time series to infr_db_metric. It never reads or writes your data rows — only load and structure metrics.

DB collectors are the source for the DB load monitoring screen and Alerts. Register one DB here and it appears on both automatically.

When to use

Layout

The collector list renders cards grouped by access method. access=database rows are folded into one "Database" group card (to avoid card sprawl). The group card's [Edit] opens the grouped editor (2 tabs).

Grouped editor tabs

Tab Contents
Basic (shared connection defaults) driver · port · database · username · waiting threshold (slow_time) · schedule · cred_ref — applied to every DB item
Databases (item array) Per-item alias · display title · host/IP · driver (override) · run toggle · password (write-only)

Although the UI shows a single group card, saving upserts each item as its own db@{alias} row (access=database). The collection pipeline, Service ▸ Database, and alert targeting all operate per collector. Removing an item deletes its db@ row.

Per-collector fields (infr_collector)

Column Meaning
key Unique key. DB items are db@{alias} (e.g. db@blue) — immutable (alerts target by key)
title Display name (defaults to {alias} DB)
access Fixed database
kind Fixed metrics
host_alias The item's alias (host label)
feeds_table Fixed infr_db_metric
cred_ref Credential reference (e.g. env:STATS_DB) — empty falls back to config + secret_enc
config {driver, host, port, database, username, sample:{slow_time_sec, connect_timeout}} (no secret)
secret_enc DB password (Crypt · write‑only · never returned to browser)
is_run Whether the schedule is active

How to use

1. Register a new DB (grouped editor)

  1. Go to Administration ▸ Collectors.
  2. On the "Database" group card, click [Edit] → Basic tab and fill shared values.
    • driver: mysql or mariadb
    • port: default 3306
    • database: just the default connection schema — leave empty to connect to information_schema (the collection scope is decided by the account's grants)
    • username: the monitoring account (e.g. infra_ro)
    • waiting threshold (slow_time): count queries running longer than this many seconds as "waiting/backed up" (1–60 s, default 2)
    • cred_ref: env:PREFIX to reuse .env credentials
  3. Databases tab → [Add DB] → per item: alias (required) · host/IP · optional password.
  4. [Save]. Each item is created/updated as a db@{alias} row.

2. Handling the secret — two paths

(A) Enter the password directly — typed into the password field, it is stored to secret_enc via encryption. The list and API responses never expose the plaintext; only has_secret/has_db_pw booleans are returned.

The password is write‑only. On edit, leaving the password field blank preserves the existing value (no overwrite). To remove it explicitly, use the clear option.

(B) Reuse .env credentials (cred_ref=env:PREFIX) — do not store the password in the DB; reference a credential set already in .env. DbCollectorService::resolveConn() reads these keys:

Values filled into config take precedence; empty ones are filled from PREFIX_* env (host/port/database/username/password).

3. Server-wide waiting queries — the PROCESS grant

The two core metrics (connection saturation = Threads_running/Threads_connected/max_connections, and QPS · slow rate = ΔQuestions/ΔSlow_queries) are collectible with a plain (USAGE) account. However, server-wide waiting-query aggregation and whole-schema size analysis require extra grants.

A least-privilege account script to run once as root on each target DB host: sql/infr_db_monitor_grant.sql.

-- Dedicated read-only account (narrow the source host to the InfraGuard app server IP)
CREATE USER 'infra_ro'@'%' IDENTIFIED BY 'CHANGE_ME_TO_A_STRONG_SECRET';

-- Global observation grants (NOT data access)
GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'infra_ro'@'%';

-- Schema size analysis (all DBs · read-only). For one DB only, narrow to design.*
GRANT SELECT ON *.* TO 'infra_ro'@'%';

-- Blast-radius isolation — prevent connection monopolization (lesson from the 2026-08-06 [1040] incident)
ALTER USER 'infra_ro'@'%' WITH MAX_USER_CONNECTIONS 5;
FLUSH PRIVILEGES;

MariaDB 10.5+ can use BINLOG MONITOR instead of REPLICATION CLIENT. PROCESS and SELECT are identical.

4. Run and verify collection

Collection is performed by the infr:collect-db artisan command. The dev launcher (composer dev) scheduler pane calls it every minute.

# Sample one collector once immediately (bypass the ops-policy gate)
php artisan infr:collect-db --key=db@blue --force

# Collect all registered DBs (is_run=1) subject to policy
php artisan infr:collect-db

Collection gating is decided by Operations Policy.

Policy key Default Meaning
db_collect_mode economy economy = collect only when a console user (presence) is active · always = always
db_collect_interval_sec 60 Collection interval (seconds, minimum 10)
presence_window_sec — Window keeping "user present" after last activity

Use --key to test a specific database collector. The [Test] / [Sync code] buttons are for access=web collectors only and do not apply to database collectors — verify with the command above.

5. Status thresholds

DbCollectorService::statusOf() judges up/degraded against thresholds from Operations Policy.

Threshold key Default When exceeded
db_degrade_conn_pct 80 connection saturation (%) ≥ → degraded
db_degrade_running 40 threads_running ≥ → degraded
db_degrade_waiting 5 waiting query count ≥ → degraded
db_degrade_slow_qps 1 slow rate (/s) ≥ → degraded

Data sources

Target Table / source Notes
Collector registry infr_collector (migrations 000201 + 000680) access · config (JSON) · secret_enc (Crypt)
Collected time series infr_db_metric (migration 000681) derived from SHOW GLOBAL STATUS · qps/slow_qps deltas
Remote DB status SHOW GLOBAL STATUS · SHOW VARIABLES max_connections direct connection to each registered DB
Waiting queries information_schema.processlist server-wide with PROCESS grant
Schema size information_schema.tables 30-minute long cache (slow-query bypass)

No connection leaks. DbCollectorService::withConn() creates a runtime connection per sample and closes it immediately in finally via DB::purge. A PDO timeout (default 5 s) keeps web requests/schedules from blocking on remote latency. Never spawn an infinite-loop daemon from the scheduler — it exhausts the shared MySQL connection pool (the 2026‑08‑06 [1040] Too many connections incident).

Access

See RBAC levels for the full scheme.

Troubleshooting & FAQ

Related

Collectors — the database group
Collectors — the database group