Skip to main content

Data Quality

A QPI — Quality Performance Indicator — is a named check that runs SQL against the client warehouse and returns a pass/fail verdict with a message and a measured value. Two pages cover them: the Monitor reads, the Administration page writes.

The verdict is not the run state

A QPI run can complete perfectly and still report a failing check — that is exactly what a QPI is for. The pass/fail verdict comes from the probe's own test column, never from whether the run succeeded. Keep the two apart when reading these pages.


QPI Monitor​

Read-only. Scoped to the sidebar date range, with a Max records slider.

QPI Monitor: last result per QPI, the execution metrics, the timeline and the incomplete runs table

Last result per QPI​

The headline. Every defined QPI, joined to the most recent run inside the window, so a check that never ran shows as a gap rather than vanishing.

OutcomeMeaning
🟢 PassThe run completed and the probe returned test = 1
🟠 FailThe run completed and the probe returned test = 0 — the data is wrong
🔴 ErrorThe run failed — the probe SQL itself raised an error
⚪ Not runNo run inside the window

Counters across the top (QPIs defined, Passing, Failing, Errored, Not run) are followed by an alert per condition, then the full table: outcome, QPI, type, description, message, value, last run, error.

Fail and Error need different responses. A Fail is a data problem — go and look at the data. An Error is a broken check — go and fix the SQL on the Administration page.

Last result per QPI: the QPIs defined, passing, failing, errored and not-run tiles above the per-QPI result table

Execution​

State metrics, then three tabs:

  • Timeline (Gantt) — when each run started and how long it took. A run still in flight is drawn up to now so it has a visible bar.
  • Outcome history — runs per QPI in the window, coloured by outcome. This is where a flapping check becomes obvious: a QPI alternating pass and fail between runs is a different problem from one that is steadily failing.
  • Run detail — the raw run table with a filter builder

Incomplete runs​

Runs that never reached Completed, read from a dedicated status endpoint so the list is complete regardless of the page's window or record limit.

Admins get an Admin console here with two actions per stuck run:

ActionEffect
⏹ TerminateCloses the run as Completed, marked terminated, against its run key
🔄 RerunTerminates the run, then queues a fresh one immediately

A run is identified by (qpi, runDttm). If the worker dies mid-probe, that row stays Running forever and blocks the check. Terminating closes it as Completed rather than Failed — a Failed run still counts as incomplete and would linger in the list.

Dependency graph and schedule checklist​

The graph shows what each QPI waits on, with QPI nodes carrying their last known result. The checklist below lists every definition with its frequency, interval, earliest runtime, last and next run, dependencies and description.


QPI Administration​

Everything that changes a QPI. All mutating actions are admin-only and audit-logged; other roles see the page read-only behind a lock notice.

QPI Administration: the New QPI form and the list of existing definitions grouped by type

Definitions​

One card per QPI, grouped by type into expanders, filterable by type and by a name/description search. Each card shows its type badge, its schedule (or a Retired badge), description, last run, next run and dependencies, with three actions: ▶ Run now, ✏️ Edit, and delete — each confirmed inline. Editing opens a form directly below the card's row.

The definition form​

FieldNotes
QPI nameThe identifier. Cannot be changed after creation.
TypeFree-form grouping; a whole type can be triggered as a batch
Sort orderThe order checks run and are reported in
DescriptionShown in the run report next to the pass/fail marker
FrequencyHow often the scheduler runs it. never retires it — the definition and its history are kept, but the scheduler stops picking it up.
IntervalRun every N frequency cycles
Earliest run timeNo run is scheduled before this time of day
DependenciesFlows the check waits on. When one completes, its dependent QPIs are triggered.

Retiring with never is the right way to stop a check you may want back. Deleting is for definitions that were a mistake.

The check​

Either a guided builder or Advanced raw SQL. The guided builder always shows the SQL it generates, in a Generated probe SQL expander — the console never sends something you have not been shown. Switching a guided check to Advanced hands you the SQL to hand-tune; switching back replaces it.

A QPI whose SQL was not produced by the builder opens directly in Advanced mode.

The check: the Threshold, Freshness, Comparison and Advanced options, the result label, the probe query and the pass condition

The probe SQL contract​

The worker runs the SQL against the client warehouse and reads the first row's first three columns, in order:

#ColumnMeaning
1testThe verdict. 1 = pass, 0 = fail. Must be a number.
2messageHuman-readable text shown in the run report
3valueThe measured number, as text

Name the columns exactly, aliasing them where they come from expressions, and write the query so it always returns exactly one row — aggregate with COUNT(*) rather than returning the offending rows, or only the first is read and the rest are silently ignored.

The API rejects a command that does not end with exactly one ;, or that contains INSERT, UPDATE, DELETE, MERGE, DROP, TRUNCATE, ALTER, CREATE or EXEC. A probe reads; it never writes.

An example that passes when no orphaned customers exist:

SELECT
CASE WHEN COUNT(*) = 0 THEN 1 ELSE 0 END AS test,
CONCAT(CAST(COUNT(*) AS VARCHAR), ' customers without a household') AS message,
CAST(COUNT(*) AS VARCHAR) AS value
FROM dwh.Customer c
LEFT JOIN dwh.Household h ON h.HouseholdKey = c.HouseholdKey
WHERE h.HouseholdKey IS NULL;

Zero orphans → test = 1 → 🟢 Pass. Any orphans → test = 0 → 🟠 Fail, with the count carried in message and value.

Pre-check before saving​

The console runs the API's rules locally before the round trip and separates them:

  • Blocking — the same rules the API enforces: terminator count, mutating keywords. These stop the save.
  • Advisory — heuristics that cannot be decided by inspection: the query does not start with SELECT/WITH, or no test/message/value column was found. These warn but never block.

String literals and comments are blanked before keyword scanning, so a probe whose message reads "please update the source" is not mistaken for an UPDATE statement.

Whatever fails, your input stays on screen to be corrected — nothing is discarded.

Stuck runs​

The same force-close facility as the Monitor's admin console, listing every incomplete run with its state, start time and error, and a Terminate action per run.

Dependencies​

Pick a source flow to see which QPIs it gates, then trigger them all as if the flow had just completed. This is the fix for a missed completion signal — the flow ran, but its dependent checks were never woken.