A GCP-native, dbt-powered micro-batch data pipeline engine
Project description
Unified Data Engine
A GCP-native, dbt-powered micro-batch data pipeline engine with a full operator CLI. Register a pipeline with one YAML file. The engine handles the rest.
pip install unified-data-engine
ude up
What It Does
UDE is a self-contained data processing platform for platform data engineers who need production-grade SCD handling, schema drift detection, and full observability — without the overhead of enterprise-scale tools.
The core promise: drop a YAML file in config/pipelines/, the engine picks it up automatically. No code changes. No custom MERGE SQL. No schema wrangling.
What happens on every 30-second batch cycle:
Cloud Pub/Sub (MiniSky)
↓
Pull messages (30s window)
↓
Schema check → MATCH / EVOLVED / BROKEN
↓
Edge case gate → null check, dedup, type validation, late arrival
↓
Write clean records → BigQuery raw_staging
↓
dbt run → snapshot (SCD Type 2) → mart (SCD Type 1) → tests
↓
Checkpoint + Pub/Sub ack ← only after all tests pass
↓
Push metrics → Prometheus → Grafana
Failed batches are nacked and reprocessed automatically on the next cycle.
Pipelines Proven End to End
| Pipeline | SCD Type | Natural Key | Records/batch |
|---|---|---|---|
| customers | Type 2 (full history via snapshot) | customer_id | 20 |
| orders | Type 1 (overwrite) | order_id | 200 |
| products | Type 1 (overwrite) | product_id | 30 |
Adding a new pipeline = one YAML file + two dbt SQL files. Zero engine code changes.
Stack
| Component | Technology | Role |
|---|---|---|
| Message bus | Cloud Pub/Sub | Ingestion, micro-batch rhythm |
| Transformation | dbt Core | SCD via snapshots + incremental |
| Dev adapter | dbt-duckdb | Zero-config local development |
| Prod adapter | dbt-bigquery | Production GCP target |
| Batch processing | Polars | Schema inference, edge case validation |
| Hot state | Bigtable (local: JSON files) | Schema versions, offsets, checkpoints |
| Target store | BigQuery | Staging, snapshots, marts, quarantine |
| API | FastAPI | Control plane — 20+ REST endpoints |
| CLI | Typer + Rich | ude — operator CLI, pip-installable |
| Dashboard | Streamlit | Operator UI — 5 pages |
| Metrics | Prometheus + Pushgateway | Engine + dbt metrics pipeline |
| Dashboards | Grafana | 2 live dashboards (Engine Overview + dbt Health) |
| Local GCP | MiniSky | Emulates all GCP services locally |
| Infra-as-code | Terraform | Provisions MiniSky + real GCP |
100% open source. No vendor lock-in.
Prerequisites
- WSL2 / Ubuntu 24.04 (or macOS/Linux)
- Docker Desktop with WSL2 backend enabled
- Python 3.12+
Installation
1. Install MiniSky (local GCP emulator)
curl -sSL https://minisky.bmics.com.ng/install.sh | sh
minisky start
2. Clone and set up
git clone https://github.com/tycoach/unified-data-engine
cd unified-data-engine
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
This installs the engine dependencies and the ude CLI in one step.
3. Start the monitoring stack
docker compose up -d
Starts Prometheus on :9090, Pushgateway on :9091, and Grafana on :3000 (login: admin / admin).
4. Provision MiniSky resources
make provision
Creates Pub/Sub topics, subscriptions, and BigQuery datasets on MiniSky. Run this every time MiniSky restarts — it loses state on shutdown.
5. Start the stack
make up
Starts FastAPI on :8000, Streamlit on :8501, and the micro-batch engine loop.
6. Verify with the CLI
ude status
The CLI — ude
The ude CLI ships with pip install unified-data-engine and gives operators full control from the terminal.
Lifecycle
ude status # Stack health — all 6 components
ude up # Start the stack
ude down # Stop the stack
ude seed # Publish synthetic test data to Pub/Sub
ude init # Scaffold a new UDE project
Pipeline management
ude pipeline list # All pipelines with status + last batch
ude pipeline inspect customers # Full config, schema fields, last batch detail
ude pipeline new # Interactive scaffold: YAML + dbt model stubs
ude pipeline enable orders # Resume a paused pipeline
ude pipeline disable products # Pause without deleting
Schema operations
ude schema sync # Regenerate dbt contracts from registry
ude schema history customers # Version timeline — INITIAL → EVOLVED → BROKEN
ude schema diff customers # Locked schema vs what's arriving live
ude schema approve customers # Approve a BROKEN migration, unblock pipeline
Quarantine management
ude quarantine list # All quarantined batches
ude quarantine inspect <batch_id> # Full detail + schema diff + records
ude quarantine approve <batch_id> # Release for replay
ude quarantine reject <batch_id> # Discard permanently
ude quarantine replay <batch_id> # Force immediate replay
dbt commands
ude dbt run # Run all dbt models (auto-injects --profiles-dir, --vars)
ude dbt test # Run dbt tests
ude dbt snapshot # Run dbt snapshots
ude dbt docs # Generate + serve dbt docs
ude dbt lineage # Render model dependency DAG in terminal
Live observability
ude observe watch # Live batch feed — records, dbt, schema, quarantine rate
ude observe logs # Stream engine logs (filter by pipeline, level)
ude observe metrics # Prometheus metrics snapshot as a Rich table
CLI config
The CLI reads config from ~/.ude/config.yml:
host: localhost
port: 8000
env: local
minisky_url: http://localhost:8080
timeout: 30
Override with env vars (UDE_HOST, UDE_PORT) or --host/--port flags.
Quick Start (after installation)
# Publish synthetic test data to Pub/Sub
make seed
# Watch live batch cycles in the terminal
ude observe watch
# List all pipelines
ude pipeline list
# Open the operator dashboard
open http://localhost:8501
# View API docs
open http://localhost:8000/docs
# View live metrics in Grafana
open http://localhost:3000 # admin / admin
Registering a New Pipeline
Option A — Interactive CLI (recommended)
ude pipeline new
Walks through all inputs and generates:
config/pipelines/{id}.ymldbt/models/staging/{id}_staged.sqldbt/models/marts/dim_{id}.sqldbt/snapshots/{id}_snapshot.sql(SCD Type 2 only)
Option B — Manual YAML
Drop a YAML file in config/pipelines/. No code changes.
# config/pipelines/products.yml
pipeline_id: products
subscription_id: raw.products-sub
natural_key: product_id
scd_type: 1 # 1 = overwrite, 2 = full history
edge_case_mode: quarantine
null_threshold: 0.02
late_arrival_window: 24h
duplicate_window: 30m
dbt:
staging_model: products_staged
mart_model: dim_products
snapshot: null
fields:
product_id: { type: string, nullable: false }
sku: { type: string, nullable: false }
name: { type: string, nullable: true }
price: { type: float, nullable: false }
updated_at: { type: datetime, nullable: false }
Then add:
dbt/models/staging/products_staged.sqldbt/models/marts/dim_products.sql- Add
products_stagedtodbt/models/staging/_sources.yml
For SCD Type 2, also add dbt/snapshots/products_snapshot.sql.
Schema Deviation Handling
| Outcome | What happened | Engine action |
|---|---|---|
| MATCH | Schema identical | Fast path — continue |
| EVOLVED | New column added, type widened | Update registry, regenerate dbt contract, continue |
| BROKEN | Column removed, type incompatible | Quarantine batch, alert operator, hold schema |
Approving a BROKEN migration via CLI:
ude schema diff customers # Preview what changed
ude schema approve customers # Approve + unblock pipeline
Or via API:
POST /schema/{pipeline_id}/approve-migration
{ "reason": "Upstream removed column intentionally" }
Or use the Quarantine page in the Streamlit dashboard.
Operator Dashboard
Five pages at http://localhost:8501:
| Page | What it shows |
|---|---|
| Overview | Engine health, MiniSky status, pipeline summary |
| Pipeline Health | Checkpoint history, batch stats, schema fields |
| Quarantine | Dirty records with failure reasons, migration approval |
| Schema History | Locked schemas, version timeline, dbt source contracts |
| dbt Lineage | Model dependency DAG from manifest.json |
API — Control Plane
FastAPI at http://localhost:8000/docs — 20+ endpoints across 6 routers.
| Router | Key endpoints |
|---|---|
/health |
Stack health, MiniSky connectivity |
/pipeline |
List, inspect, enable/disable, batch history |
/schema |
Locked schemas, history, diff, sync, approve migration |
/quarantine |
List batches, inspect, approve, reject, replay |
/dbt |
Trigger runs, status, lineage, artifacts |
/metrics |
Raw Prometheus text (scraped by Prometheus) |
/metrics/structured |
JSON metrics for CLI rendering |
/logs/stream |
NDJSON log stream for ude observe logs |
Grafana Dashboards
Two pre-built dashboards at http://localhost:3000:
Dashboard 1 — Engine Overview
- Batch Throughput — records/batch per pipeline over time
- End-to-End Batch Duration (p95) — batch processing time trends
- Quarantine Rate — per pipeline, alerts if > 10%
- Active Pipelines — count of running pipelines
- Schema Version — current locked version per pipeline
- Staging Rows Written — rows written to BigQuery per batch
Dashboard 2 — dbt Health
- dbt Run Duration (p95) — snapshot vs mart run times
- dbt Test Failures — failures block checkpoint — zero = healthy
- Snapshot Records Opened vs Closed — SCD Type 2 change tracking
- dbt Run Status — 1=success, 0=failure per pipeline
- Contract Violations — edge case gate gap detection
Monitoring & Alerting
Prometheus scrapes http://localhost:8000/metrics + Pushgateway at :9091.
Key metrics
| Metric | What it tracks |
|---|---|
ude_batch_records_total |
Records pulled per batch |
ude_quarantine_rate |
Quarantine rate (0.0–1.0) |
ude_schema_deviation_total |
MATCH / EVOLVED / BROKEN counts |
ude_dbt_run_duration_seconds |
dbt run time histogram |
ude_dbt_test_failures_total |
Test failures — each blocks checkpoint |
ude_snapshot_records_opened_total |
SCD Type 2 changes per batch |
ude_dbt_run_status |
Last dbt run: 1=success, 0=failure |
ude_checkpoints_total |
Successful vs failed checkpoints |
Alert rules (7 total)
| Alert | Condition | Severity |
|---|---|---|
| HighQuarantineRate | quarantine_rate > 10% | Critical |
| DbtTestFailure | any not_null or unique failure | Critical |
| SchemaDeviationDetected | BROKEN deviation | Critical |
| SnapshotMismatch | opened != closed | Critical |
| SlowBatchProcessing | p95 > 60s | Warning |
| DbtRunExceedsWindow | p95 > 25s | Warning |
| ZeroRowsProcessed | 0 rows for 3 batches | Warning |
Make Commands
make up # Start everything
make down # Stop all services
make engine # Run engine only
make api # Start FastAPI only
make ui # Start Streamlit only
make seed # Publish synthetic data
make provision # Reprovision MiniSky after restart
make dbt-run # Run all dbt models
make dbt-test # Run dbt tests
make dbt-docs # Generate + serve dbt docs
make schema-sync # Regenerate dbt contracts from registry
make test # Run all unit + integration tests
make reset # Wipe all state, fresh start
make help # Show all commands
Project Structure
unified-data-engine/
├── config/
│ ├── engine.yml Global engine settings
│ ├── loader.py Config-driven pipeline loader
│ └── pipelines/ One YAML per pipeline
│ ├── customers.yml
│ ├── orders.yml
│ └── products.yml
├── engine/
│ ├── main.py Micro-batch loop
│ ├── ingestion/ Pub/Sub consumer + offset manager
│ ├── schema/ Inference, registry, deviation, contract writer
│ ├── staging/ Edge case gate + BigQuery staging writer
│ ├── dbt_runner/ dbt orchestration + results parser
│ ├── state/ Bigtable client + checkpoint manager
│ └── metrics/ Prometheus metric emitters
├── dbt/
│ ├── models/staging/ One view per dataset
│ ├── models/marts/ SCD Type 1 incremental models
│ └── snapshots/ SCD Type 2 snapshot declarations
├── api/ FastAPI — 20+ endpoints, 6 routers
├── cli/ ude CLI — Typer + Rich, pip-installable
│ ├── commands/ One file per command group (6 groups)
│ ├── client/ HTTP client wrapping FastAPI endpoints
│ ├── scaffold/ ude init + ude pipeline new generators
│ ├── output/ Rich tables, panels, live watch display
│ └── core/ Config, errors, checks, context
├── ui/ Streamlit — 5 operator pages
├── monitoring/
│ ├── prometheus/ prometheus.yml + alerts.yml (7 rules)
│ └── grafana/
│ ├── dashboards/ engine_overview.json + dbt_health.json
│ └── provisioning/ Auto-loaded datasources + dashboards
├── data-generator/
│ └── scenarios/ happy_path.py, products.py
├── scripts/ Phase test scripts + verify_install.sh
├── terraform/ Infra-as-code for MiniSky + real GCP
├── tests/
│ ├── unit/cli/ 92 passing unit tests (config, checks, scaffold)
│ └── integration/cli/ Integration test stubs (run with stack live)
├── assets/ CLI screenshots for documentation
├── docker-compose.yml Monitoring stack (Prometheus + Grafana)
├── pyproject.toml Package manifest — makes pip install work
├── Makefile
├── requirements.txt
└── .env.example
MiniSky — Important Notes
MiniSky loses all Pub/Sub and BigQuery state on restart. After every restart:
minisky start
make provision # recreate topics, subscriptions, datasets
The engine will get 404s on every pull until provisioning is complete. This is expected behaviour — make provision is idempotent and safe to run multiple times.
Deploying to Real GCP
No engine code changes needed:
- Set
GOOGLE_APPLICATION_CREDENTIALSto your service account key - Update
config/engine.yml→environment: production - Update
dbt/profiles.yml→target: prod - Run
terraform applyinterraform/
Why UDE?
| Problem | UDE solution |
|---|---|
| Writing SCD MERGE SQL for every dataset | dbt snapshots + incremental — zero custom SQL |
| Schema changes breaking pipelines silently | MATCH / EVOLVED / BROKEN on every batch |
| Nulls, duplicates, late arrivals handled inconsistently | Edge case gate — configurable per pipeline |
| New pipeline takes days to set up | ude pipeline new — interactive scaffold in 2 minutes |
| No visibility into what's happening | CLI + FastAPI + Streamlit + Prometheus + Grafana |
| Operator commands require SSH + curl | ude quarantine approve, ude schema diff from anywhere |
| Vendor lock-in to expensive platforms | 100% open source, GCP-native, MiniSky for local dev |
Releases
| Version | What shipped |
|---|---|
v1.2.0 |
Full end-to-end verified — live batch cycles, ude observe watch live |
v1.0.0 |
FastAPI endpoints wired for CLI — all commands connected to live stack |
v1.0.0-cli |
ude CLI complete — 92/92 unit tests, all 6 command groups |
License
MIT — use it, fork it, build on it.
Built by Taiwo Hassan · Powered by MiniSky
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unified_data_engine-2.0.0.tar.gz.
File metadata
- Download URL: unified_data_engine-2.0.0.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1c46b21ed4b0f312961518769f543618742f2e4555e800a126224f04ef34c2d
|
|
| MD5 |
4d892f43a671a23d9e69f253a1303b53
|
|
| BLAKE2b-256 |
dce76ca760f3a66a55d5bf84e7fd88bed27785c323176bd8d1232223b417eda5
|
File details
Details for the file unified_data_engine-2.0.0-py3-none-any.whl.
File metadata
- Download URL: unified_data_engine-2.0.0-py3-none-any.whl
- Upload date:
- Size: 64.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b11ba8db5c979703e833fcd13160172d9adf30fe6e36b42f43c38995a2387bdb
|
|
| MD5 |
02bb29931399a3c1cda3454bd50d4317
|
|
| BLAKE2b-256 |
df03f93d62388603a6e0f920d9d129f9527975ca51368c0d8cbe3bfa73ba3f89
|