Protect loads and observe warehouses with a reusable measure → disposition stack. Schema contract validation (resource definition validation) remains separate; this layer checks data content (row counts, domains, ranges, nulls, patterns, and custom SQL asserts).
Measure vs disposition
| Stage | Responsibility | Workflow fail on findings? | |
|---|---|---|---|
Measure Data Quality action |
Run rules against catalog subjects; produce a report; optionally persist history |
No (only infrastructure errors) |
|
Evaluate Quality Gate action |
Apply policy to a report; optional alert sinks |
Yes for |
Pre-update (protect the EDW):
Measure Data Quality (sources)
|
v
Evaluate Quality Gate (FAIL_ON_BLOCKING) --fail--> stop
|
success
v
Data Vault Update / other load
Post-update (observe only; cannot un-load):
… load …
|
v
Measure Data Quality (published target tables, lifecycle=POST_UPDATE)
|
v
Evaluate Quality Gate (ALERT_ONLY) --> log / ops_table sinks, continue
The same engine measures sources and published targets. Pre-update gates typically bind CRM/source record definitions; post-update alerts bind model-published vault (or BV/DM) tables. Satellite measures use the full physical table (all historical versions), not a current-row filter.
Rules
Central Data quality rule set Hop metadata holds reusable rules. Catalog record definitions bind rules (library reference or inline) via qualityRules.
Phase 1 rule types
-
MIN_ROW_COUNT/MAX_ROW_COUNT— dataset (min/max) -
NOT_NULL— field -
ALLOWED_VALUES— field (values=M,F,U,nullAllowed=true) -
RANGE— field (min,max) -
NOT_EMPTY_STRING— field (trim=trueoptional)
Phase 2 rule types
| Type | Scope | Parameters | Notes | |
|---|---|---|---|---|
|
field |
|
Fail when |
|
|
field |
|
Prefer exact |
|
|
field |
|
Same; if SAMPLE distribution is truncated and size ≤ max, emits a fixed INFO finding (not fully verified). |
|
|
field |
|
Prefer SQL pushdown (database regex engine). High-cardinality free text: prefer |
|
|
field |
|
Character length on non-null strings ( |
|
|
dataset |
|
Trusted authors only; DB grants are the real boundary (see below). |
Parameters use key=value pairs (semicolon-separated in the rule set editor).
REGEX guidance
-
Pushdown uses the database regex engine (for example Postgres
~/~*), notjava.util.regex— dialects differ on POSIX vs Java and Unicode. -
Patterns are SQL-literal escaped; blank / overlong patterns fall through to a bounded sample path.
-
Incomplete sample coverage can pass the rule and emit an INFO finding (
coverageIncomplete). -
For complex free-text predicates, use
SQL_ASSERTIONinstead of forcing expensive full-table grouping.
SQL_ASSERTION security
SQL_ASSERTION is an escape hatch for trusted metadata authors — not a sandbox.
-
After variable resolution,
${schemaTable}expands to the subject’s quoted schema.table. -
Single
SELECT/WITHonly; multi-statement (embedded;) rejected; DML/DDL tokens denylisted. -
Expectation failure → finding; parse/security rejection, timeout, or SQL error → infrastructure error (measure fails).
-
ZERO_ROWSstops after the first row (no unbounded fetch). Residual risk remains for comment-wrapped keywords and side-effecting functions; control with database grants.
Catalog bindings
On a record definition JSON document:
"qualityRules": [
{
"inlineRule": {
"id": "not-empty",
"name": "Table not empty",
"type": "MIN_ROW_COUNT",
"severity": "BLOCKING",
"enabled": true,
"parameters": { "min": "1" }
},
"enabled": true
}
]
Or reference a rule set (pick the rule set from the catalog Quality tab combo, or from Measure’s metadata field):
"qualityRules": [
{
"ruleSetName": "retail-domains",
"ruleId": "gender-allowed",
"fieldNameOverride": "gender",
"enabled": true
}
]
Leave ruleId empty to bind every enabled rule in that set. The catalog Quality tab edits library bindings (rule set + rule id + field/severity overrides) and preserves any existing inline rules.
DV/BV/DM catalog publishers preserve existing qualityRules (and validation acknowledgements) on republish so target bindings survive routine model publish.
Workflow actions
Measure data quality
-
Scope: resource definition group, and/or
namespace/namekeys, and/or namespace prefix -
Data quality rule set (optional Hop metadata picker): applies every enabled rule in that library to each subject, in addition to catalog
qualityRules(ids de-duplicated; catalog bindings win on conflict) -
Catalog connection and history database use Hop metadata pickers (same pattern as other actions)
-
Evaluation mode:
AUTO(SQL pushdown for databases) -
Lifecycle:
PRE_UPDATE,POST_UPDATE,AD_HOC -
Logs a formatted report; stores it on the workflow for the next gate step
-
Does not fail the workflow for rule findings (only infrastructure errors)
Persist history (opt-in)
| Field | Default | Purpose | |
|---|---|---|---|
|
|
Write run + profile + findings to ops tables after measure |
|
|
empty → resolve |
Target DB connection (retail: |
|
|
|
Schema for quality_* tables |
|
|
|
|
|
|
|
Correlates PRE/POST under one load |
Resolution when historyDatabase is empty: variable QUALITY_HISTORY_DATABASE, else the physical DB of the first successfully profiled subject. Schema falls back to QUALITY_HISTORY_SCHEMA then dv_ops.
Persist failures are logged and do not fail measure by default. GUI Test measure… and gate remeasure do not persist. Runs are immutable by quality_run_id (duplicate PK → skip).
Ops tables (dv_ops)
Sibling tables to load-run metrics (same connection pattern: retail OPS / dv_ops):
| Table | Role | |
|---|---|---|
|
Run header, aggregate finding counts, lifecycle, load/workflow ids |
|
|
Per-subject row count + lifecycle (Phase 3 join grain) |
|
|
Field profiles (nulls, distinct, min/max, lengths, top values) |
|
|
Individual findings |
|
|
Alert header rows when the |
Catalog stubs for retail are published at runtime under retail-example/work/edw-catalog/hop/retail-example/operations/.
Evaluate quality gate
-
Disposition:
FAIL_ON_BLOCKING,FAIL_ON_WARNINGS,ALERT_ONLY -
Uses the prior measure report when available; otherwise re-measures if configured (same rule set / catalog scope fields as Measure)
-
Prefer
remeasureIfNoPriorReport=Nwhen a Measure step immediately precedes the gate (remeasure isAD_HOCand would mislabel lifecycle)
Alert sinks
Comma-separated alertSinks (blank → log):
| Sink id | Behavior | |
|---|---|---|
|
Structured |
|
|
Insert |
Wiring matrix:
| Mode | Findings | Sinks | |
|---|---|---|---|
|
0 |
None (no noise) |
|
|
> 0 |
Every sink listed in |
|
|
0 |
None |
|
|
> 0 |
|
Gate fields for ops_table: historyDatabase, historySchema, autoCreateHistoryTables (same defaults as measure).
Catalog Quality tab
On a record definition:
-
Bindings table (library / inline rules)
-
Test measure… — SAMPLE/AUTO measure, results dialog; does not persist
-
History… — recent profile snapshots for this subject from ops tables (up to 50 rows); double-click a run for findings
History connection resolution:
-
Variables
QUALITY_HISTORY_DATABASE/QUALITY_HISTORY_SCHEMA(schema defaults todv_ops) -
Catalog
hop/{projectKey}/operations/quality_runphysical table -
Fallback
hop/{projectKey}/operations/load_run -
Otherwise: configure a history database or publish operations catalog definitions
Subject key is always namespace/name (same as measure/persist).
Limits and deferred work
-
Database physical tables via SQL pushdown are fully supported
-
File/Iceberg sample scanning is deferred
-
Phase 3 historical rules (
ROW_COUNT_DELTA,DISTINCT_DELTA, …) need persisted profiles — schema is ready; evaluators are not Phase 2 -
Mail/Slack/webhook sinks beyond the SPI are deferred
-
Quality finding acknowledgements deferred
Retail example
The retail-example project ships libraries and wiring for both pre-update source gates and post-update target alerts:
| Asset | Location | |
|---|---|---|
Source rule set |
|
|
Target rule set |
|
|
Source bindings |
|
|
Target bindings |
|
|
Pre-load gate |
|
|
Post-load alert |
|
|
Ops catalog stubs |
|
Workflows: run-retail-initial.hwf, run-retail-update.hwf.
Target rules include table-not-empty, hub business-key not-null / min-distinct, segment and loyalty domains, and segment NULL_RATIO_MAX (maxRatio=0.01) over the full satellite table.