Monitoring: the noxdb_sweep cron job¶
scripts/sweep/noxdb_sweep.py is a standalone monitoring script meant to run
on the ccr-lab LiSC VM via cron. It answers the operational questions
that don't fit into notebook analysis: is the database live, has the schema
changed, how big is it, and is anything inconsistent between the DB and the
files on disk. Results are emailed as an HTML report.
It's built entirely on top of queries and
projects — nothing here talks to the database directly
outside a handful of small checks (liveness, schema fingerprint, audit log)
that don't have a library function of their own.
Full deployment instructions (installing on ccr-lab, ~/.my.cnf,
credentials file, crontab entries) live in
scripts/sweep/README.md
in the repo. This page covers what it checks and why.
Why it runs on ccr-lab directly¶
The production Galera cluster is only reachable from inside the LiSC
network. Every other noxDB use case (notebooks, imports) runs from outside
that network and opens an SSH tunnel through ccr-lab (see
Install). The sweep script instead runs natively on
ccr-lab, so init_pool() connects straight to host:port from
~/.my.cnf's [noxdb] section — no [noxdb-ssh] section, no tunnel.
Modes¶
| Mode | Cadence | Emails |
|---|---|---|
heartbeat |
daily | only on failure or a slow response |
weekly |
weekly | always |
monthly |
monthly | always |
manual |
on demand | always |
manual runs the full check set (same as monthly, including the slow
filesystem walk) for an on-demand "tell me the state of things right now"
report.
What's checked¶
- Liveness —
SELECT 1throughtransaction(), timed. If this fails, every other check for that run is skipped — no point walking the filesystem when the DB itself is unreachable. - Row counts — an exact
COUNT(*)per table, compared with the previous run. Distinct from the population snapshot below, which sums per-project figures: there a sample shared by two projects counts twice and one linked to no project is invisible. A table that shrank at all is a warning; one that lost 10% or more is an error. This runs nightly as well as weekly, because a vanishing table is the one thing worth interrupting someone for. - Schema fingerprint — hashes
information_schema.columnsfor the connected database and diffs against the last run's hash. Catches both deliberate migrations and accidentalALTERs. - Population snapshot — loops
projects.list_allthroughqueries.project_summaryand addsqueries.list_inputs, giving total projects, subjects, visits, samples (patient/mockIP/anchor/NC/input broken out), and files. Each run's snapshot is appended to a JSONL history file, so every report shows the delta since the previous snapshot. - Integrity — loops
queries.integrity_checkper project; the report only lists projects that actually have issues. - DB→disk drift —
queries.find_db_files_missing_on_disk, cheap enough to run weekly. - Disk→DB drift —
queries.find_disk_files_missing_in_db, a recursive walk of the directories that already hold registered files (queries.registered_file_dirs), which the report lists underscanned_dirs. It deliberately does not walk the tier roots: on this cluster/lisc/data/workis the whole institute's work filesystem, so every readable file belonging to another group would be reported as unregistered. Still reserved formonthly/manual. - Orphans — rows that hang off nothing. Foreign-key orphans cannot happen (InnoDB constraints prevent them); these are the semantic kind, such as a sample belonging to no project, which is invisible to every project-scoped query while still occupying a row. Controls legitimately sit unlinked until a study sample lands on their plate, so they are reported but not flagged.
- Duplicates — two passes. Exact: rows identical on every column except
the surrogate key and
created_at, per table. Near: samples whose plate, well and identity match but whose names are padded differently, which is how one specimen came to be registered twice asR05P01_01_..andR05P01_1_... - Activity — new rows in the last 7 days per table, and new samples per
project, read from
created_at. This is deliberately not the audit log below: that only sees writes made through noxdb on this host, so work done from another machine never appears in it. - Database size —
data_length + index_lengthper table, largest first. - Audit log — tails
~/.noxdb/audit.logand counts writes per table over the last 7 days, as a lightweight "is this thing actually being used" signal. - Sequencing coverage — how much of what the lab ever sequenced is in
noxDB.
scripts/sweep/sqr_coverage.pyreads a master sheet of every sequenced well (default/lisc/data/work/ccr/mariaDB/master_sequenced_samples.csv, override withNOXDB_MASTER_SHEET) and matches it againstsamples, tolerating the run sheet's different spellings of the same name. The report gives samples in noxDB per sequencing run and lists the run-sheet projects that are only partly or not at all imported, with the reason recorded in the sheet (e.g. held for metadata). Coverage gaps are expected and never fail the sweep; the check warns only if the master sheet is missing or noxDB holds samples the sheet does not know, which means the sheet is out of date. Run it on its own for the full report:python scripts/sweep/sqr_coverage.py [--out report.csv]. - Metadata completeness — per project, over study samples only:
the share of subjects with sex, of visits with an age and with a real
group (not empty or
unknown), whether the project is longitudinal (a subject with more than one timepoint), whichvisit_metadatafields exist and how complete each is, and the share of samples with a counts file. The report lists the least complete projects first. Informational only: missing metadata is a curation gap, not a fault. Full table on demand:python scripts/sweep/metadata_completeness.py [--out completeness.csv]. - Uptime % —
monthlyonly, computed from the JSONL history's liveness results over the trailing 30 days.
Operational details¶
Lock file, 30-minute hard timeout, and a top-level crash handler that emails
a "sweep crashed" alert with the traceback are all documented alongside the
credentials/log paths in
scripts/sweep/README.md.