Data Quality¶
havn provides a comprehensive data quality framework with three complementary systems: inline assertions in SQL models, automatic profiling, and freshness monitoring. For standalone quality rules, see Contracts.
Inline Assertions¶
Add @assert directives to SQL model files. Assertions are evaluated after each model builds during havn transform:
@config materialized=table, schema=gold
@assert row_count > 0
@assert unique(customer_id)
@assert no_nulls(customer_id)
@assert no_nulls(email)
@assert accepted_values(status, ['active', 'inactive', 'suspended'])
@assert "lifetime_value >= 0"
SELECT
customer_id,
email,
status,
SUM(order_total) AS lifetime_value
FROM silver.customers
GROUP BY 1, 2, 3
(Legacy -- assert: ... comment syntax still parses for back-compat.)
Available Assertion Types¶
row_count > N¶
Checks that the table has more than N rows:
@assert row_count > 0
@assert row_count > 100
@assert row_count >= 1000
unique(column)¶
Checks that a column contains no duplicate values:
@assert unique(customer_id)
@assert unique(email)
no_nulls(column)¶
Checks that a column contains no NULL values:
@assert no_nulls(customer_id)
@assert no_nulls(email)
accepted_values(column, [values])¶
Checks that all values in a column are within the allowed set:
@assert accepted_values(status, ['active', 'inactive', 'suspended'])
@assert accepted_values(country_code, ['US', 'CA', 'GB', 'DE'])
Custom SQL Expressions¶
Any boolean SQL expression can be used as an assertion. Wrap complex expressions in quotes:
@assert "AVG(amount) > 0"
@assert "MAX(created_at) > CURRENT_DATE - INTERVAL '7 days'"
@assert "COUNT(DISTINCT region) > 1"
Custom expressions are evaluated as SELECT (<expression>) FROM <table> and must return a single truthy value.
Assertion Behavior¶
- Assertions run after a model is built (after
CREATE OR REPLACE TABLE) - If an assertion fails, the model status is set to
assertion_failed - The model data is not rolled back -- the table exists but is flagged
- All assertions for a model are evaluated (not short-circuited)
- Results are stored in
_havn.assertion_results
Assertion Debugging¶
When an assertion fails, havn provides diagnostic details to help you find the problem:
unique(col)failure -- shows top 10 duplicated values with countsno_nulls(col)failure -- shows null count, percentage, and sample rows with NULLsaccepted_values(col, [...])failure -- shows unexpected values with countsrow_countfailure -- shows actual count vs threshold
To re-run assertions with full diagnostics on demand:
curl http://localhost:3000/api/quality/assertion-debug/gold.customer_summary
In the web UI, click "Re-run with diagnostics" on any failed assertion in the Observe → Quality panel to see detailed debugging output.
Viewing Assertion Results¶
havn assertions
Shows recent assertion results with pass/fail status and details.
Via API:
# All assertions
curl http://localhost:3000/api/assertions
# For a specific model
curl http://localhost:3000/api/assertions/gold.customer_summary
Data Profiling¶
havn automatically computes column-level statistics for every model after it builds. Profiles include:
- Row count -- Total number of rows
- Column count -- Number of columns
- Null percentages -- Percentage of NULL values per column
- Distinct counts -- Number of distinct values per column
Viewing Profiles¶
# Summary of all models
havn profile
# Detailed profile for one model
havn profile gold.earthquake_summary
The detailed view shows per-column statistics:
gold.earthquake_summary (1,234 rows, 8 columns)
Profiled at: 2025-01-15 06:00:12
Column Null % Distinct Status
region 0% 42 ok
magnitude 0% 156 ok
location 12% 891 has nulls
depth 0% 734 ok
Profile via API¶
# All profiles
curl http://localhost:3000/api/profiles
# Specific model
curl http://localhost:3000/api/profiles/gold.earthquake_summary
Table-Level Profiling¶
The table browser in the web UI provides interactive profiling for any table:
curl http://localhost:3000/api/tables/gold/earthquake_summary/profile
Returns detailed statistics including min/max values, averages (for numeric columns), and sample values.
Freshness Monitoring¶
Freshness monitoring detects models that have not been rebuilt within a specified time window.
Check Model Freshness¶
havn freshness --hours 24
Shows all models with their last run time and whether they are stale (not rebuilt within 24 hours).
Check Source Freshness¶
havn freshness --sources
Checks source freshness against SLAs declared in project.yml. See Sources.
Freshness Alerts¶
Send alerts for stale models:
havn freshness --hours 24 --alert
This sends notifications via Slack or webhook (requires alert configuration in project.yml).
Freshness via API¶
curl "http://localhost:3000/api/freshness?max_hours=24"
Combined Validation¶
The havn check command runs all quality checks in one pass:
havn check
This executes:
- Model validation -- SQL syntax, dependency resolution, column references
- Inline assertions --
@assertdirectives against live data - YAML contracts -- Rules from
contracts/directory
CI/CD Integration¶
Use havn check in your CI/CD pipeline:
havn check --env test
Exit code 1 if any validation or assertion fails, making it suitable for automated quality gates.
Alerts¶
havn supports alerting for pipeline events:
Configuration¶
alerts:
channels:
- slack
- webhook
slack_webhook_url: ${SLACK_WEBHOOK_URL}
webhook_url: "https://alerts.internal/havn"
on_success: false
on_failure: true
Alert Types¶
- Pipeline failure -- Sent when a transform or stream fails
- Assertion failure -- Sent when data quality assertions fail
- Stale data -- Sent when models exceed freshness thresholds
Test Alerts¶
curl -X POST http://localhost:3000/api/alerts/test \
-H "Content-Type: application/json" \
-d '{"channel": "slack", "slack_webhook_url": "https://hooks.slack.com/..."}'
Alert History¶
curl http://localhost:3000/api/alerts
Anomaly Detection¶
havn tracks profile statistics over time and automatically detects anomalies using Z-score analysis. When the current run's metrics deviate significantly from the historical baseline, an anomaly is flagged.
How It Works¶
After each transform run, havn compares the current profile (row_count, null percentages, distinct counts) against the last N runs (default 30). If the Z-score exceeds the threshold (default 2.0), an anomaly is logged.
Configuration¶
quality:
anomaly_detection:
enabled: true
lookback: 30 # Number of historical runs to compare
threshold: 2.0 # Z-score threshold for flagging
notify: [slack] # Alert channels
Viewing Anomalies¶
# Recent anomalies
curl http://localhost:3000/api/anomalies
# Anomalies for a specific model
curl http://localhost:3000/api/anomalies/gold.customer_summary
In the web UI, anomalies appear in the Observe → Quality panel with severity coloring, showing the current value vs expected range.
Edge Cases¶
- Fewer than 3 historical profiles → skipped (not enough baseline data)
- Zero standard deviation → skipped (constant value, no deviation possible)
- First run → skipped (no baseline)
Related Pages¶
- Contracts -- Standalone YAML data quality rules
- Transforms -- Adding assertions to SQL models
- Sources -- Source freshness SLAs
- Lineage -- Understanding data dependencies
- CLI Reference -- Quality-related commands