Skip to main content

Data Quality Monitoring

Comprehensive data profiling

Monitor Data Quality In-Flight and at Rest

DataBridge goes beyond event tracking by monitoring data quality both in real-time as events flow and at rest in your data warehouse. Catch anomalies before they affect your analytics or ML models.


Open Source CLI: dbqctl

The core data quality engine is available as dbqctl - a free, open-source CLI tool you can run locally or in CI/CD. It supports profiling, validation and quality checks against PostgreSQL, ClickHouse and MySQL.

DataBridge Cloud builds on top of dbqctl with a managed agent, scheduling, dashboards and alerting - but you can start with just the CLI.

Install dbqctl

Check the GitHub Releases for latest version.

# Linux
curl -L https://github.com/DataBridgeTech/dbqctl/releases/latest/download/dbqctl-linux-amd64 -o dbqctl
chmod +x dbqctl && sudo mv dbqctl /usr/local/bin/

Configure a Data Source

Create ~/.dbq.yaml:

version: "1"
datasources:
- id: pg
type: postgresql
configuration:
host: localhost
port: 5432
username: readonly_user
password: ${PG_PASSWORD}
database: analytics
datasets:
- public.users
- public.orders
# Test the connection
dbqctl ping -d pg

# Import available tables
dbqctl import -d pg --filter "public"

Profile Your Data

Run a profile to understand the shape of your data before writing any checks:

dbqctl profile -d pg --dataset public.users
Dataset: public.users
Rows: 10,542

Column: user_id (bigint)
Null Count: 0 (0.0%)
Unique Values: 10,542
Min: 1 Max: 10,542

Column: email (varchar)
Null Count: 3 (0.03%)
Unique Values: 10,539
Most Frequent: user@example.com (5)

Column: country (varchar)
Null Count: 127 (1.2%)
Unique Values: 43
Most Frequent: US (3,421), UK (1,892), CA (876)

Write Quality Checks

Create checks.yaml:

version: "1"
rules:
- dataset: pg@[public.users]
checks:
# Schema-level: make sure expected columns exist
- schema_check:
expect_columns:
columns: [user_id, email, created_at, country]
desc: "Users table must have core columns"
on_fail: error

# Table-level: volume sanity check
- row_count between 1000 and 1000000:
desc: "Should have a reasonable number of users"
on_fail: error

# Column-level checks
- not_null(user_id):
desc: "User ID is required"

- not_null(email):
desc: "Email is required"

- uniqueness(email):
desc: "Emails must be unique"
on_fail: error

- freshness(created_at) < 7d:
desc: "Should have new users in the last week"
on_fail: warn

Run the checks:

dbqctl check --checks ./checks.yaml
Running checks on pg@[public.users]...

✓ Schema check passed: Users table must have core columns
✓ Row count check passed: Should have a reasonable number of users (10,542 rows)
✓ Not null check passed: User ID is required (0 nulls)
✗ Not null check FAILED: Email is required (3 nulls found)
✓ Uniqueness check passed: Emails must be unique
✓ Freshness check passed: Should have new users in the last week (last: 2h ago)

Results: 5 passed, 1 failed

What Makes DataBridge Different?

Unlike event-only tracking tools like Segment or Snowplow that only validate events in-flight, DataBridge also monitors the quality of data already stored in your warehouse - similar to dedicated tools like Great Expectations or Soda, but unified in one platform.

Dual Quality Monitoring:

  1. In-Flight Validation: Real-time schema validation as events flow (covered in Schema Registry)
  2. At-Rest Monitoring: Continuous profiling and quality checks on warehouse tables (this page)

This combination catches issues that only show up after data reaches your warehouse - aggregation errors, unexpected distributions, late-arriving data, schema drift and more.


Available Check Types

Key profiling metrics

Schema Checks

Validate table structure hasn't drifted:

# Columns exist (any order)
- schema_check:
expect_columns:
columns: [id, name, created_at]
desc: "Required columns exist"

# Columns in specific order
- schema_check:
expect_columns_ordered:
columns_order: [id, name, email, created_at]
desc: "Columns are in expected order"

# Sensitive columns are absent
- schema_check:
columns_not_present:
columns: [credit_card_number, ssn]
pattern: "pii_*"
desc: "No PII columns in this table"

Table-Level Checks

- row_count > 1000:
desc: "Must have minimum data volume"

- row_count between 1000 and 1000000:
desc: "Row count within expected range"

# Custom SQL for complex validations
- raw_query:
query: "SELECT COUNT(*) FROM {{dataset}} WHERE status = 'pending' AND created_at < NOW() - INTERVAL '24 hours'"
desc: "No orders stuck in pending for over 24h"
on_fail: warn

Column-Level Checks

# Null checks
- not_null(user_id):
desc: "User ID is mandatory"

# Uniqueness
- uniqueness(email):
desc: "Emails must be unique"
on_fail: error

# Numeric range
- min(price) > 0:
desc: "Price must be positive"
- max(price) < 50000:
desc: "Price within expected range"
- avg(price) between 20.0 and 500.0:
desc: "Average price looks reasonable"

# Statistical checks
- stddev(trip_distance) < 100:
desc: "Trip distance variation within normal range"

# Freshness
- freshness(updated_at) < 24h:
desc: "Data should update daily"
on_fail: warn

A Real-World Example

Here's a checks file covering multiple datasets across ClickHouse and PostgreSQL - based on the open source examples:

version: "1"
rules:
# ClickHouse: NYC Taxi dataset
- dataset: ch@[nyc_taxi.trips_small]
where: "pickup_datetime > '2024-01-01'"
checks:
- schema_check:
expect_columns:
columns: [trip_id, fare_amount, trip_distance]
desc: "Required columns exist"
on_fail: error

- row_count between 10000 and 3500000:
desc: "Dataset has a reasonable number of trips"

- not_null(trip_id):
desc: "Trip ID is mandatory"
- uniqueness(trip_id):
desc: "Trip IDs must be unique"

- min(trip_distance) >= 0:
desc: "Trip distance cannot be negative"
- min(fare_amount) > 0:
desc: "Fare amount should be positive"
- avg(trip_distance) between 1.0 and 20.0:
desc: "Average trip distance looks reasonable"

# PostgreSQL: UK Land Registry
- dataset: pg@[public.land_registry_price_paid_uk]
where: "transfer_date >= '2025-01-01'"
checks:
- not_null(price):
desc: "Property price is mandatory"
- min(price) >= 100:
desc: "Minimum price should be realistic"
- max(price) < 50000000:
desc: "Maximum price within UK market range"
- uniqueness(transaction):
desc: "Each transaction must have a unique ID"
- freshness(transfer_date) < 1d:
desc: "Transfer date should be recent"
on_fail: warn

Real-Time Alerts

Real-time quality alerts

CLI: Script Your Own Alerts

With dbqctl, you can wire up alerting yourself using exit codes:

#!/bin/bash
if dbqctl check --checks checks.yaml; then
echo "All checks passed"
else
curl -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d '{"text":"Data quality checks FAILED!"}'
fi

Cloud: Built-In Notifications

DataBridge Cloud adds managed alerting on top:

  • Slack, email, Telegram and webhook notifications
  • Configurable thresholds per metric and per dataset
  • Alert grouping to reduce noise
  • Scheduled checks - no cron jobs needed
  • Auto-resolution when metrics return to normal

Running Checks in CI/CD

Integrate quality gates directly into your deployment pipeline:

# .github/workflows/data-quality.yml
name: Data Quality Checks
on:
schedule:
- cron: '0 */6 * * *'
workflow_dispatch:
jobs:
quality-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dbqctl
run: |
curl -L https://github.com/DataBridgeTech/dbqctl/releases/latest/download/dbqctl-linux-amd64 -o dbqctl
chmod +x dbqctl && sudo mv dbqctl /usr/local/bin/
- name: Run Quality Checks
env:
PG_PASSWORD: ${{ secrets.PG_PASSWORD }}
run: dbqctl check --checks ./checks.yaml

Cloud vs. CLI

Featuredbqctl (open source)DataBridge Cloud
Data profilingYesYes
Quality checksYesYes
PostgreSQL, ClickHouse, MySQLYesYes
SchedulingDIY (cron, Airflow)Built-in
Dashboards & trends-Yes
Managed alerting-Yes (Slack, email, Telegram, webhooks)
Team collaboration-Yes
Managed Agent-Yes

Ready to Get Started?

Open source: Install dbqctl and run your first profile in 5 minutes.

Cloud: Data quality monitoring is included in all DataBridge plans. Start with the free tier (100K events/month, 3 tracked datasets) or explore paid plans starting at $79/month.