Skip to content

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 counts
  • no_nulls(col) failure -- shows null count, percentage, and sample rows with NULLs
  • accepted_values(col, [...]) failure -- shows unexpected values with counts
  • row_count failure -- 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:

  1. Model validation -- SQL syntax, dependency resolution, column references
  2. Inline assertions -- @assert directives against live data
  3. 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)