Skip to content

Data Quality SLAs, Validation Layers, and Observability for Tabular, Geospatial, and ML Data: Best Practices

Objective: Establish comprehensive data quality governance with SLAs, multi-layer validation, and observability for tabular, geospatial, and ML data. When you need data quality assurance, when you want quality SLAs, when you need validation observability—this guide provides the complete framework.

Introduction

Data quality is the foundation of trustworthy analytics and ML systems. Without quality governance, data degrades, models drift, and decisions become unreliable. This guide establishes patterns for data quality SLAs, multi-layer validation, and quality observability across all data types and systems.

What This Guide Covers: - Multi-layer validation (schema, statistical, geospatial, semantic) - Row-level, tile-level, and raster-level DQ expectations - ML feature drift detection - Tools: Great Expectations, Soda, Deequ, custom PostGIS assertions - Quality SLAs for Parquet lakes, FDW sources, feature stores, raster/tiling pipelines - Quality monitors feeding into Grafana/Loki - Fitness functions for DQ stability

Prerequisites: - Understanding of data quality principles - Familiarity with validation frameworks and tools - Experience with data observability and monitoring

Related Documents: This document integrates with: - Data Validation and Contract Governance - Validation patterns - Metadata Standards, Schema Governance & Data Provenance Contracts - Metadata governance - Data Freshness, SLA/SLO Governance, and Pipeline Reliability Contracts - Freshness SLAs - ML Systems Architecture: Feature Stores, Model Serving, Experiment Governance, and Cross-System Reproducibility - ML quality

The Philosophy of Data Quality

Quality Principles

Principle 1: Multi-Layer Validation - Schema validation - Statistical validation - Geospatial validation - Semantic validation

Principle 2: Quality SLAs - Define quality expectations - Measure quality continuously - Enforce quality gates

Principle 3: Observability First - Instrument all validation - Monitor quality metrics - Alert on quality violations

Multi-Layer Validation

Schema Validation

Pattern:

# Schema validation
from pydantic import BaseModel, validator

class UserSchema(BaseModel):
    id: int
    email: str
    age: int

    @validator('email')
    def validate_email(cls, v):
        if '@' not in v:
            raise ValueError('Invalid email')
        return v

    @validator('age')
    def validate_age(cls, v):
        if v < 0 or v > 150:
            raise ValueError('Invalid age')
        return v

Statistical Validation

Pattern:

# Statistical validation
import pandas as pd
from scipy import stats

class StatisticalValidator:
    def validate(self, data: pd.DataFrame) -> ValidationResult:
        """Validate statistical properties"""
        results = []

        # Check for outliers
        for column in data.columns:
            z_scores = stats.zscore(data[column])
            outliers = (abs(z_scores) > 3).sum()

            if outliers > len(data) * 0.05:  # >5% outliers
                results.append(ValidationIssue(
                    column=column,
                    issue="excessive_outliers",
                    severity="warning"
                ))

        return ValidationResult(issues=results)

Geospatial Validation

Pattern:

-- Geospatial validation
CREATE FUNCTION validate_geometry(
    geom GEOMETRY
) RETURNS BOOLEAN AS $$
BEGIN
    -- Check validity
    IF NOT ST_IsValid(geom) THEN
        RETURN FALSE;
    END IF;

    -- Check SRID
    IF ST_SRID(geom) != 4326 THEN
        RETURN FALSE;
    END IF;

    -- Check bounds
    IF NOT ST_Within(geom, ST_MakeEnvelope(-180, -90, 180, 90, 4326)) THEN
        RETURN FALSE;
    END IF;

    RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

Semantic Validation

Pattern:

# Semantic validation
class SemanticValidator:
    def validate(self, data: pd.DataFrame) -> ValidationResult:
        """Validate semantic properties"""
        results = []

        # Check business rules
        if 'order_date' in data.columns and 'ship_date' in data.columns:
            invalid = data[data['ship_date'] < data['order_date']]
            if len(invalid) > 0:
                results.append(ValidationIssue(
                    issue="ship_date_before_order_date",
                    severity="error",
                    count=len(invalid)
                ))

        return ValidationResult(issues=results)

Row-Level DQ Expectations

Great Expectations

Pattern:

# Great Expectations validation
import great_expectations as ge

context = ge.get_context()

# Define expectations
expectation_suite = context.create_expectation_suite("my_suite")

# Add expectations
validator = context.get_validator(
    batch_request={
        "datasource_name": "my_datasource",
        "data_connector_name": "default_inferred_data_connector_name",
        "data_asset_name": "my_table"
    },
    expectation_suite_name="my_suite"
)

validator.expect_column_values_to_not_be_null("id")
validator.expect_column_values_to_be_unique("id")
validator.expect_column_values_to_be_between("age", min_value=0, max_value=150)

Soda Validation

Pattern:

# Soda validation
checks for users:
  - row_count > 0
  - missing_count(id) = 0
  - invalid_count(email) = 0:
      valid format: email
  - duplicate_count(id) = 0
  - freshness(order_date) < 1d

Tile-Level DQ Expectations

Tile Quality Validation

Pattern:

# Tile quality validation
class TileQualityValidator:
    def validate_tile(self, tile: Tile) -> QualityReport:
        """Validate tile quality"""
        issues = []

        # Check tile completeness
        if tile.coverage < 0.95:
            issues.append(QualityIssue(
                type="incomplete_coverage",
                severity="warning"
            ))

        # Check tile format
        if not tile.is_valid_format():
            issues.append(QualityIssue(
                type="invalid_format",
                severity="error"
            ))

        # Check tile bounds
        if not tile.is_valid_bounds():
            issues.append(QualityIssue(
                type="invalid_bounds",
                severity="error"
            ))

        return QualityReport(issues=issues)

Raster-Level DQ Expectations

Raster Quality Validation

Pattern:

# Raster quality validation
import rasterio

class RasterQualityValidator:
    def validate_raster(self, raster_path: str) -> QualityReport:
        """Validate raster quality"""
        issues = []

        with rasterio.open(raster_path) as src:
            # Check CRS
            if src.crs is None:
                issues.append(QualityIssue(
                    type="missing_crs",
                    severity="error"
                ))

            # Check data type
            if src.dtypes[0] not in ['uint8', 'uint16', 'float32']:
                issues.append(QualityIssue(
                    type="invalid_data_type",
                    severity="warning"
                ))

            # Check no-data values
            if src.nodata is None:
                issues.append(QualityIssue(
                    type="missing_nodata",
                    severity="warning"
                ))

        return QualityReport(issues=issues)

ML Feature Drift Detection

Feature Drift Detection

Pattern:

# ML feature drift detection
from evidently import ColumnMapping
from evidently.metric_preset import DataDriftPreset
from evidently.report import Report

class FeatureDriftDetector:
    def detect_drift(self, reference: pd.DataFrame, current: pd.DataFrame) -> DriftReport:
        """Detect feature drift"""
        # Define column mapping
        column_mapping = ColumnMapping(
            target=None,
            numerical_features=['age', 'income'],
            categorical_features=['category']
        )

        # Generate drift report
        report = Report(metrics=[DataDriftPreset()])
        report.run(
            reference_data=reference,
            current_data=current,
            column_mapping=column_mapping
        )

        # Extract drift metrics
        drift_metrics = report.as_dict()['metrics']

        return DriftReport(metrics=drift_metrics)

Quality SLAs

Parquet Lake Quality SLA

SLA Definition:

# Parquet lake quality SLA
parquet_lake_quality:
  sla:
    completeness: "> 0.95"
    validity: "> 0.99"
    freshness: "< 1 hour"
  monitoring:
    check_interval: "5 minutes"
    alert_threshold: "sla_violation"

FDW Source Quality SLA

SLA Definition:

# FDW source quality SLA
fdw_source_quality:
  sla:
    availability: "> 0.99"
    latency: "< 100ms"
    data_quality: "> 0.95"
  monitoring:
    check_interval: "1 minute"
    alert_threshold: "sla_violation"

Feature Store Quality SLA

SLA Definition:

# Feature store quality SLA
feature_store_quality:
  sla:
    feature_completeness: "> 0.98"
    feature_freshness: "< 5 minutes"
    feature_drift: "< 0.05"
  monitoring:
    check_interval: "1 minute"
    alert_threshold: "sla_violation"

Raster/Tiling Pipeline Quality SLA

SLA Definition:

# Raster/tiling pipeline quality SLA
raster_tiling_quality:
  sla:
    tile_completeness: "> 0.99"
    tile_validity: "> 0.99"
    raster_validity: "> 0.99"
  monitoring:
    check_interval: "5 minutes"
    alert_threshold: "sla_violation"

Quality Observability

Grafana Quality Dashboard

PromQL Queries:

# Data quality metrics
data_quality_score{dataset="users"} > 0.95
data_completeness{dataset="users"} > 0.98
data_validity{dataset="users"} > 0.99

# Quality SLA compliance
data_quality_sla_compliance{dataset="users"} < 1.0

Loki Quality Logs

Log Queries:

# Quality violation logs
{job="data-quality"} |= "quality_violation"
{job="data-quality"} | json | quality_score < 0.95

Architecture Fitness Functions

DQ Stability Fitness Function

Definition:

# DQ stability fitness function
class DQStabilityFitnessFunction:
    def evaluate(self, system: System) -> float:
        """Evaluate DQ stability"""
        # Calculate quality variance
        quality_scores = self.get_quality_scores(system)
        quality_variance = np.var(quality_scores)

        # Calculate stability (lower variance = higher stability)
        if quality_variance == 0:
            stability = 1.0
        else:
            stability = 1.0 / (1.0 + quality_variance)

        return stability

Cross-Document Architecture

graph TB
    subgraph Quality["Data Quality<br/>(This Document)"]
        Validation["Multi-Layer Validation"]
        SLAs["Quality SLAs"]
        Observability["Quality Observability"]
    end

    subgraph Validation["Data Validation"]
        Contracts["Validation Contracts"]
    end

    subgraph Metadata["Metadata Governance"]
        Provenance["Provenance"]
    end

    subgraph Freshness["Data Freshness"]
        FreshnessSLAs["Freshness SLAs"]
    end

    Validation --> Contracts
    SLAs --> Provenance
    Observability --> FreshnessSLAs

    style Quality fill:#fff4e1
    style Validation fill:#e1f5ff
    style Metadata fill:#e8f5e9
    style Freshness fill:#ffebee

Checklists

Data Quality Checklist

  • Multi-layer validation implemented
  • Row-level DQ expectations defined
  • Tile-level DQ expectations defined
  • Raster-level DQ expectations defined
  • ML feature drift detection active
  • Quality SLAs defined
  • Quality monitors configured
  • Grafana dashboards created
  • Loki log queries defined
  • Fitness functions implemented
  • Regular quality reviews scheduled

Anti-Patterns

Quality Anti-Patterns

No Quality Validation:

# Bad: No validation
def process_data(data):
    return process(data)  # No quality checks

# Good: Validation
def process_data(data):
    validator = DataQualityValidator()
    if not validator.validate(data):
        raise ValueError("Data quality violation")
    return process(data)

See Also


This guide establishes comprehensive data quality patterns. Start with multi-layer validation, extend to quality SLAs, and continuously monitor quality observability.