sqlmesh-dag-generator 0.9.0__tar.gz → 0.9.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlmesh_dag_generator-0.9.0/sqlmesh_dag_generator.egg-info → sqlmesh_dag_generator-0.9.6}/PKG-INFO +80 -148
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/README.md +79 -147
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/setup.py +1 -1
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/__init__.py +3 -2
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/config.py +65 -4
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/dag_builder.py +129 -1
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/generator.py +378 -6
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6/sqlmesh_dag_generator.egg-info}/PKG-INFO +80 -148
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/SOURCES.txt +0 -8
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_airflow_utils.py +0 -26
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_auto_schedule.py +14 -3
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_config.py +10 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_generator.py +198 -1
- sqlmesh_dag_generator-0.9.0/tests/test_bug_fix_user_scenario.py +0 -148
- sqlmesh_dag_generator-0.9.0/tests/test_cache.py +0 -63
- sqlmesh_dag_generator-0.9.0/tests/test_enterprise_features.py +0 -240
- sqlmesh_dag_generator-0.9.0/tests/test_environment_default.py +0 -98
- sqlmesh_dag_generator-0.9.0/tests/test_environment_handling.py +0 -147
- sqlmesh_dag_generator-0.9.0/tests/test_plan_optimization.py +0 -310
- sqlmesh_dag_generator-0.9.0/tests/test_runtime_connections.py +0 -221
- sqlmesh_dag_generator-0.9.0/tests/test_source_tables.py +0 -211
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/LICENSE +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/pyproject.toml +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/setup.cfg +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/airflow_utils.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/cli.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/models.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/security.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/utils.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/validation.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/dependency_links.txt +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/entry_points.txt +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/requires.txt +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/top_level.txt +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_kubernetes.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_models.py +0 -0
- {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_utils.py +0 -0
{sqlmesh_dag_generator-0.9.0/sqlmesh_dag_generator.egg-info → sqlmesh_dag_generator-0.9.6}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlmesh-dag-generator
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.6
|
|
4
4
|
Summary: Open-source Airflow DAG generator for SQLMesh projects
|
|
5
5
|
Home-page: https://github.com/kubolko/sqlmesh-dag-generator
|
|
6
6
|
Author: Jakub Sumionka
|
|
@@ -59,38 +59,30 @@ Transform your SQLMesh models into production-ready Airflow DAGs with **full dat
|
|
|
59
59
|
- ✅ **Full Lineage in Airflow**: Each SQLMesh model = One Airflow task with proper dependencies
|
|
60
60
|
- 🌍 **Multi-Environment Support**: Use Airflow Variables + SQLMesh gateways for dev/staging/prod
|
|
61
61
|
- ⚡ **Incremental Models**: Proper handling with `data_interval_start/end`
|
|
62
|
+
- 🩺 **Integrity Guardrails**: Warn when sub-hourly incremental models run with `catchup=False`, with optional bounded recovery helpers
|
|
62
63
|
- 🎯 **Enhanced Error Handling**: SQLMesh-specific error messages in Airflow logs
|
|
63
64
|
- 🛠️ **Dual Mode**: Dynamic (auto-discovery, default) or Static (full control)
|
|
64
65
|
- 🚫 **No Vendor Lock-in**: Open source, no cloud dependencies
|
|
65
66
|
|
|
66
|
-
### 🏢 Enterprise Features (NEW in v0.8.0)
|
|
67
|
-
|
|
68
|
-
- 🔔 **Callbacks**: `on_failure_callback`, `on_success_callback`, `sla_miss_callback` for alerting
|
|
69
|
-
- 🏷️ **Tag-Based Filtering**: `include_tags`, `exclude_tags` for Data Mesh team-specific DAGs
|
|
70
|
-
- 🎱 **Pool Configuration**: `pool`, `pool_slots` for resource management
|
|
71
|
-
- ⏩ **Trigger Downstream DAGs**: `trigger_dag_id` for ML pipeline integration
|
|
72
|
-
- 🎯 **Pattern Filtering**: `model_pattern` for regex-based model selection
|
|
73
|
-
|
|
74
67
|
## ⚠️ Important: Gateway vs Environment
|
|
75
68
|
|
|
76
69
|
**SQLMesh uses "gateways" to switch between environments, NOT an "environment" parameter.**
|
|
77
70
|
|
|
78
71
|
```python
|
|
79
|
-
# ❌ WRONG - environment parameter is
|
|
72
|
+
# ❌ WRONG - environment parameter is deprecated
|
|
80
73
|
generator = SQLMeshDAGGenerator(
|
|
81
74
|
sqlmesh_project_path="/path/to/project",
|
|
82
|
-
environment="prod", #
|
|
75
|
+
environment="prod", # This doesn't work!
|
|
83
76
|
)
|
|
84
77
|
|
|
85
|
-
# ✅ CORRECT - Use gateway to switch
|
|
78
|
+
# ✅ CORRECT - Use gateway to switch environments
|
|
86
79
|
generator = SQLMeshDAGGenerator(
|
|
87
80
|
sqlmesh_project_path="/path/to/project",
|
|
88
|
-
gateway="prod"
|
|
89
|
-
# environment defaults to "" (empty string) - perfect for production
|
|
81
|
+
gateway="prod" # This is how you select your environment!
|
|
90
82
|
)
|
|
91
83
|
```
|
|
92
84
|
|
|
93
|
-
|
|
85
|
+
**See [Multi-Environment Configuration Guide](docs/MULTI_ENVIRONMENT.md) for complete setup instructions.**
|
|
94
86
|
|
|
95
87
|
## 🚀 Quick Start (3 Steps)
|
|
96
88
|
|
|
@@ -127,6 +119,72 @@ cp my_pipeline.py /opt/airflow/dags/
|
|
|
127
119
|
|
|
128
120
|
**That's it! 🎉** Your SQLMesh models are now orchestrated by Airflow. The DAG will auto-discover models at runtime - no regeneration needed when models change!
|
|
129
121
|
|
|
122
|
+
## Recovery And Completeness
|
|
123
|
+
|
|
124
|
+
SQLMesh DAG Generator forwards Airflow's `data_interval_start` and `data_interval_end` into `ctx.run(start=..., end=...)`.
|
|
125
|
+
That means the package executes the interval Airflow gives it, but it does **not** invent missed Airflow runs on its own.
|
|
126
|
+
|
|
127
|
+
If you run sub-hourly incremental models with `catchup=False`, outages can leave completeness gaps unless you replay the missed windows.
|
|
128
|
+
|
|
129
|
+
The package now supports an explicit recovery policy:
|
|
130
|
+
|
|
131
|
+
- `recovery_mode="disabled"`: no runtime recovery tasks are added.
|
|
132
|
+
- `recovery_mode="warn"`: add an integrity guard task that detects missing intervals and logs them.
|
|
133
|
+
- `recovery_mode="bounded_auto"` (default): add the same guard task plus a bounded recovery task that replays missing intervals when the gap is within `recovery_max_intervals`.
|
|
134
|
+
|
|
135
|
+
Example:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
generator = SQLMeshDAGGenerator(
|
|
139
|
+
sqlmesh_project_path="/path/to/project",
|
|
140
|
+
dag_id="my_pipeline",
|
|
141
|
+
recovery_mode="bounded_auto",
|
|
142
|
+
recovery_max_intervals=6,
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
When `recovery_mode` is enabled, the package adds stable helper tasks to the DAG instead of mutating the graph at runtime:
|
|
147
|
+
|
|
148
|
+
- `sqlmesh_integrity_guard`
|
|
149
|
+
- `sqlmesh_recovery_backfill` in `bounded_auto` mode
|
|
150
|
+
|
|
151
|
+
This keeps recovery explicit and observable in Airflow while preserving the default "no surprise backfills" behavior.
|
|
152
|
+
|
|
153
|
+
### Manual Backfill Task
|
|
154
|
+
|
|
155
|
+
For larger historical gaps, the package also exposes a first-class manual backfill task helper.
|
|
156
|
+
Use it in a separate unscheduled DAG and trigger it only when you need to replay a broader window:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from datetime import datetime, timedelta
|
|
160
|
+
from airflow import DAG
|
|
161
|
+
from sqlmesh_dag_generator import SQLMeshDAGGenerator
|
|
162
|
+
|
|
163
|
+
generator = SQLMeshDAGGenerator(
|
|
164
|
+
sqlmesh_project_path="/path/to/project",
|
|
165
|
+
dag_id="my_pipeline",
|
|
166
|
+
gateway="prod",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
with DAG(
|
|
170
|
+
dag_id="my_pipeline_manual_backfill",
|
|
171
|
+
schedule=None,
|
|
172
|
+
start_date=datetime(2024, 1, 1),
|
|
173
|
+
catchup=False,
|
|
174
|
+
) as dag:
|
|
175
|
+
generator.create_manual_backfill_task(
|
|
176
|
+
dag,
|
|
177
|
+
default_start="2024-01-01T00:00:00",
|
|
178
|
+
execution_timeout=timedelta(hours=12),
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The task reads optional `dag_run.conf` overrides:
|
|
183
|
+
|
|
184
|
+
- `start`: ISO-8601 start boundary
|
|
185
|
+
- `end`: ISO-8601 end boundary. If omitted, the task backfills up to the current UTC time.
|
|
186
|
+
- `models`: one model name or a list of model names. If omitted, the task backfills all models.
|
|
187
|
+
|
|
130
188
|
## 💡 What You Get
|
|
131
189
|
|
|
132
190
|
### Your SQLMesh Project:
|
|
@@ -150,27 +208,17 @@ Airflow Graph View:
|
|
|
150
208
|
|
|
151
209
|
## 📚 Documentation
|
|
152
210
|
|
|
153
|
-
### Getting Started
|
|
154
211
|
- **[Quick Start Guide](docs/QUICKSTART.md)** - Step-by-step tutorial (start here!)
|
|
155
212
|
- **[Quick Reference](docs/QUICK_REFERENCE.md)** - One-page cheat sheet
|
|
156
|
-
- **[
|
|
157
|
-
|
|
158
|
-
### Configuration & Features
|
|
159
|
-
- **[Auto-Scheduling Guide](docs/AUTO_SCHEDULING.md)** - Automatic schedule detection 📅
|
|
213
|
+
- **[Auto-Scheduling Guide](docs/AUTO_SCHEDULING.md)** - Automatic schedule detection 📅 NEW!
|
|
160
214
|
- **[Runtime Configuration](docs/RUNTIME_CONFIGURATION.md)** - Pass credentials via Airflow Connections 🔐
|
|
161
215
|
- **[Multi-Environment Setup](docs/MULTI_ENVIRONMENT.md)** - Configure for dev/staging/prod ⚠️ IMPORTANT
|
|
216
|
+
- **[Migration Guide](docs/MIGRATION_GUIDE.md)** - Fix common configuration issues
|
|
217
|
+
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Common issues and solutions
|
|
218
|
+
- **[Usage Guide](docs/USAGE.md)** - Complete reference
|
|
162
219
|
- **[Dynamic DAGs](docs/DYNAMIC_DAGS.md)** - Fire-and-forget mode explained
|
|
163
|
-
|
|
164
|
-
### Deployment & Production
|
|
165
|
-
- **[Deployment Checklist](docs/DEPLOYMENT_CHECKLIST.md)** - Step-by-step deployment guide ✅ NEW!
|
|
166
|
-
- **[Container Deployment](docs/CONTAINER_DEPLOYMENT.md)** - Docker, Kubernetes, ECS, Cloud Composer 🐳 NEW!
|
|
167
|
-
- **[Common Issues](docs/COMMON_ISSUES.md)** - Real-world problems and solutions 🔧 NEW!
|
|
168
|
-
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Debug guide
|
|
169
220
|
- **[Deployment Warnings](docs/DEPLOYMENT_WARNINGS.md)** - Critical production considerations
|
|
170
|
-
|
|
171
|
-
### Reference
|
|
172
|
-
- **[Usage Guide](docs/USAGE.md)** - Complete reference
|
|
173
|
-
- **[Migration Guide](docs/MIGRATION_GUIDE.md)** - Fix common configuration issues
|
|
221
|
+
- **[Examples](examples/)** - Code examples
|
|
174
222
|
- **[Architecture](docs/ARCHITECTURE.md)** - Technical details
|
|
175
223
|
|
|
176
224
|
## 🔥 Why Dynamic Mode (Default)?
|
|
@@ -225,9 +273,8 @@ Create a `dag_generator_config.yaml`:
|
|
|
225
273
|
```yaml
|
|
226
274
|
sqlmesh:
|
|
227
275
|
project_path: "/path/to/sqlmesh/project"
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
default_catalog: "my_catalog" # Optional: For 3-part naming support
|
|
276
|
+
environment: "prod"
|
|
277
|
+
gateway: "local"
|
|
231
278
|
|
|
232
279
|
airflow:
|
|
233
280
|
dag_id: "sqlmesh_pipeline"
|
|
@@ -245,12 +292,6 @@ generation:
|
|
|
245
292
|
operator_type: "python" # or "bash"
|
|
246
293
|
include_tests: true
|
|
247
294
|
parallel_tasks: true
|
|
248
|
-
auto_replan_on_change: true # Automatically run 'sqlmesh plan' if models change
|
|
249
|
-
# Plan optimization options (for faster execution when no changes)
|
|
250
|
-
skip_backfill: false # Skip apply if backfill is required (use CI/CD for initial backfills)
|
|
251
|
-
plan_only: false # Generate plan without applying (for review/dry-run)
|
|
252
|
-
log_plan_details: true # Log detailed plan information (context, plan, apply phases)
|
|
253
|
-
return_value: true # Enable/disable XCom return values
|
|
254
295
|
```
|
|
255
296
|
|
|
256
297
|
## How It Works
|
|
@@ -370,115 +411,6 @@ black .
|
|
|
370
411
|
ruff check .
|
|
371
412
|
```
|
|
372
413
|
|
|
373
|
-
## 🔧 Troubleshooting
|
|
374
|
-
|
|
375
|
-
### Common Issues
|
|
376
|
-
|
|
377
|
-
#### 1. `TypeError: Object of type CompletionStatus is not JSON serializable`
|
|
378
|
-
**Cause:** Airflow cannot serialize the SQLMesh `CompletionStatus` enum returned by tasks.
|
|
379
|
-
**Fix:** Upgrade to v0.7.2+. This version automatically converts the status to a string.
|
|
380
|
-
Alternatively, use `return_value=False` in your generator config:
|
|
381
|
-
```python
|
|
382
|
-
generator = SQLMeshDAGGenerator(
|
|
383
|
-
...,
|
|
384
|
-
return_value=False # Disable XCom return values
|
|
385
|
-
)
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
#### 2. `ConfigError: A query is required and must be a SELECT statement` (External Models)
|
|
389
|
-
**Cause:** Defining external models in `.sql` files is not supported by the generator.
|
|
390
|
-
**Fix:** Define external models in `external_models.yaml` instead.
|
|
391
|
-
```yaml
|
|
392
|
-
# external_models.yaml
|
|
393
|
-
models:
|
|
394
|
-
- name: raw.users
|
|
395
|
-
kind: EXTERNAL
|
|
396
|
-
columns:
|
|
397
|
-
id: INT
|
|
398
|
-
name: TEXT
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
#### 3. "Environment 'prod' was not found"
|
|
402
|
-
**Cause:** You are using `environment="prod"` instead of `gateway="prod"`.
|
|
403
|
-
**Fix:** Set `gateway="prod"` and leave `environment` as empty string (default).
|
|
404
|
-
```python
|
|
405
|
-
generator = SQLMeshDAGGenerator(
|
|
406
|
-
...,
|
|
407
|
-
gateway="prod",
|
|
408
|
-
environment="" # Correct for production
|
|
409
|
-
)
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
#### 4. Changes to models are not picked up
|
|
413
|
-
**Cause:** SQLMesh requires a plan/apply step to register changes.
|
|
414
|
-
**Fix:** Enable `auto_replan_on_change` in your generator config:
|
|
415
|
-
```python
|
|
416
|
-
generator = SQLMeshDAGGenerator(
|
|
417
|
-
...,
|
|
418
|
-
auto_replan_on_change=True # Automatically runs 'sqlmesh plan --auto-apply'
|
|
419
|
-
)
|
|
420
|
-
```
|
|
421
|
-
Or run `sqlmesh plan --auto-apply` manually.
|
|
422
|
-
|
|
423
|
-
#### 5. `sqlmesh_plan_apply` task takes 7+ minutes even with no changes
|
|
424
|
-
**Cause:** When `auto_replan_on_change=True`, the plan task may run full backfills even when there are no model changes.
|
|
425
|
-
**Fix (v0.9.0+):** The plan optimization now:
|
|
426
|
-
- ✅ Detects "no changes" scenarios and skips apply (~15 seconds instead of 7+ minutes)
|
|
427
|
-
- ✅ Logs detailed timing for each phase (context load, plan compute, apply)
|
|
428
|
-
|
|
429
|
-
For additional control, use these options:
|
|
430
|
-
```python
|
|
431
|
-
generator = SQLMeshDAGGenerator(
|
|
432
|
-
...,
|
|
433
|
-
auto_replan_on_change=True,
|
|
434
|
-
skip_backfill=True, # Skip apply if backfill is required (use CI/CD for initial backfills)
|
|
435
|
-
plan_only=True, # Generate plan without applying (for review/dry-run)
|
|
436
|
-
log_plan_details=True # Log detailed plan information (default: True)
|
|
437
|
-
)
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
**Recommended CI/CD approach:**
|
|
441
|
-
- Run `sqlmesh plan --auto-apply` in CI/CD on model changes (handles backfills)
|
|
442
|
-
- Set `skip_backfill=True` in Airflow DAG (only applies non-backfill changes)
|
|
443
|
-
- This gives you fast DAG runs (~15s) while ensuring changes are still applied
|
|
444
|
-
|
|
445
|
-
#### 5. 3-Part Table Naming Issues (Cross-Database)
|
|
446
|
-
**Cause:** SQLMesh generates queries with 3-part names (catalog.schema.table) which might fail if the catalog name doesn't match.
|
|
447
|
-
**Fix:** Set `default_catalog` in your config to tell SQLMesh which catalog to omit or use.
|
|
448
|
-
```python
|
|
449
|
-
generator = SQLMeshDAGGenerator(
|
|
450
|
-
...,
|
|
451
|
-
default_catalog="my_catalog"
|
|
452
|
-
)
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
### Advanced Features
|
|
456
|
-
|
|
457
|
-
#### Health Check Task
|
|
458
|
-
Add a pre-flight check to verify database connectivity before running models:
|
|
459
|
-
```python
|
|
460
|
-
generator = SQLMeshDAGGenerator(
|
|
461
|
-
...,
|
|
462
|
-
enable_health_check=True
|
|
463
|
-
)
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
#### Skip Audits
|
|
467
|
-
For faster development iterations, you can skip audit checks:
|
|
468
|
-
```python
|
|
469
|
-
generator = SQLMeshDAGGenerator(
|
|
470
|
-
...,
|
|
471
|
-
skip_audits=True
|
|
472
|
-
)
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
#### Partial DAG Runs
|
|
476
|
-
You can generate tasks for a subset of models (useful for testing):
|
|
477
|
-
```python
|
|
478
|
-
# In your DAG file
|
|
479
|
-
generator.create_tasks_in_dag(dag, models=["model_a", "model_b"])
|
|
480
|
-
```
|
|
481
|
-
|
|
482
414
|
## Contributing
|
|
483
415
|
|
|
484
416
|
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
@@ -12,38 +12,30 @@ Transform your SQLMesh models into production-ready Airflow DAGs with **full dat
|
|
|
12
12
|
- ✅ **Full Lineage in Airflow**: Each SQLMesh model = One Airflow task with proper dependencies
|
|
13
13
|
- 🌍 **Multi-Environment Support**: Use Airflow Variables + SQLMesh gateways for dev/staging/prod
|
|
14
14
|
- ⚡ **Incremental Models**: Proper handling with `data_interval_start/end`
|
|
15
|
+
- 🩺 **Integrity Guardrails**: Warn when sub-hourly incremental models run with `catchup=False`, with optional bounded recovery helpers
|
|
15
16
|
- 🎯 **Enhanced Error Handling**: SQLMesh-specific error messages in Airflow logs
|
|
16
17
|
- 🛠️ **Dual Mode**: Dynamic (auto-discovery, default) or Static (full control)
|
|
17
18
|
- 🚫 **No Vendor Lock-in**: Open source, no cloud dependencies
|
|
18
19
|
|
|
19
|
-
### 🏢 Enterprise Features (NEW in v0.8.0)
|
|
20
|
-
|
|
21
|
-
- 🔔 **Callbacks**: `on_failure_callback`, `on_success_callback`, `sla_miss_callback` for alerting
|
|
22
|
-
- 🏷️ **Tag-Based Filtering**: `include_tags`, `exclude_tags` for Data Mesh team-specific DAGs
|
|
23
|
-
- 🎱 **Pool Configuration**: `pool`, `pool_slots` for resource management
|
|
24
|
-
- ⏩ **Trigger Downstream DAGs**: `trigger_dag_id` for ML pipeline integration
|
|
25
|
-
- 🎯 **Pattern Filtering**: `model_pattern` for regex-based model selection
|
|
26
|
-
|
|
27
20
|
## ⚠️ Important: Gateway vs Environment
|
|
28
21
|
|
|
29
22
|
**SQLMesh uses "gateways" to switch between environments, NOT an "environment" parameter.**
|
|
30
23
|
|
|
31
24
|
```python
|
|
32
|
-
# ❌ WRONG - environment parameter is
|
|
25
|
+
# ❌ WRONG - environment parameter is deprecated
|
|
33
26
|
generator = SQLMeshDAGGenerator(
|
|
34
27
|
sqlmesh_project_path="/path/to/project",
|
|
35
|
-
environment="prod", #
|
|
28
|
+
environment="prod", # This doesn't work!
|
|
36
29
|
)
|
|
37
30
|
|
|
38
|
-
# ✅ CORRECT - Use gateway to switch
|
|
31
|
+
# ✅ CORRECT - Use gateway to switch environments
|
|
39
32
|
generator = SQLMeshDAGGenerator(
|
|
40
33
|
sqlmesh_project_path="/path/to/project",
|
|
41
|
-
gateway="prod"
|
|
42
|
-
# environment defaults to "" (empty string) - perfect for production
|
|
34
|
+
gateway="prod" # This is how you select your environment!
|
|
43
35
|
)
|
|
44
36
|
```
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
**See [Multi-Environment Configuration Guide](docs/MULTI_ENVIRONMENT.md) for complete setup instructions.**
|
|
47
39
|
|
|
48
40
|
## 🚀 Quick Start (3 Steps)
|
|
49
41
|
|
|
@@ -80,6 +72,72 @@ cp my_pipeline.py /opt/airflow/dags/
|
|
|
80
72
|
|
|
81
73
|
**That's it! 🎉** Your SQLMesh models are now orchestrated by Airflow. The DAG will auto-discover models at runtime - no regeneration needed when models change!
|
|
82
74
|
|
|
75
|
+
## Recovery And Completeness
|
|
76
|
+
|
|
77
|
+
SQLMesh DAG Generator forwards Airflow's `data_interval_start` and `data_interval_end` into `ctx.run(start=..., end=...)`.
|
|
78
|
+
That means the package executes the interval Airflow gives it, but it does **not** invent missed Airflow runs on its own.
|
|
79
|
+
|
|
80
|
+
If you run sub-hourly incremental models with `catchup=False`, outages can leave completeness gaps unless you replay the missed windows.
|
|
81
|
+
|
|
82
|
+
The package now supports an explicit recovery policy:
|
|
83
|
+
|
|
84
|
+
- `recovery_mode="disabled"`: no runtime recovery tasks are added.
|
|
85
|
+
- `recovery_mode="warn"`: add an integrity guard task that detects missing intervals and logs them.
|
|
86
|
+
- `recovery_mode="bounded_auto"` (default): add the same guard task plus a bounded recovery task that replays missing intervals when the gap is within `recovery_max_intervals`.
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
generator = SQLMeshDAGGenerator(
|
|
92
|
+
sqlmesh_project_path="/path/to/project",
|
|
93
|
+
dag_id="my_pipeline",
|
|
94
|
+
recovery_mode="bounded_auto",
|
|
95
|
+
recovery_max_intervals=6,
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
When `recovery_mode` is enabled, the package adds stable helper tasks to the DAG instead of mutating the graph at runtime:
|
|
100
|
+
|
|
101
|
+
- `sqlmesh_integrity_guard`
|
|
102
|
+
- `sqlmesh_recovery_backfill` in `bounded_auto` mode
|
|
103
|
+
|
|
104
|
+
This keeps recovery explicit and observable in Airflow while preserving the default "no surprise backfills" behavior.
|
|
105
|
+
|
|
106
|
+
### Manual Backfill Task
|
|
107
|
+
|
|
108
|
+
For larger historical gaps, the package also exposes a first-class manual backfill task helper.
|
|
109
|
+
Use it in a separate unscheduled DAG and trigger it only when you need to replay a broader window:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from datetime import datetime, timedelta
|
|
113
|
+
from airflow import DAG
|
|
114
|
+
from sqlmesh_dag_generator import SQLMeshDAGGenerator
|
|
115
|
+
|
|
116
|
+
generator = SQLMeshDAGGenerator(
|
|
117
|
+
sqlmesh_project_path="/path/to/project",
|
|
118
|
+
dag_id="my_pipeline",
|
|
119
|
+
gateway="prod",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
with DAG(
|
|
123
|
+
dag_id="my_pipeline_manual_backfill",
|
|
124
|
+
schedule=None,
|
|
125
|
+
start_date=datetime(2024, 1, 1),
|
|
126
|
+
catchup=False,
|
|
127
|
+
) as dag:
|
|
128
|
+
generator.create_manual_backfill_task(
|
|
129
|
+
dag,
|
|
130
|
+
default_start="2024-01-01T00:00:00",
|
|
131
|
+
execution_timeout=timedelta(hours=12),
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The task reads optional `dag_run.conf` overrides:
|
|
136
|
+
|
|
137
|
+
- `start`: ISO-8601 start boundary
|
|
138
|
+
- `end`: ISO-8601 end boundary. If omitted, the task backfills up to the current UTC time.
|
|
139
|
+
- `models`: one model name or a list of model names. If omitted, the task backfills all models.
|
|
140
|
+
|
|
83
141
|
## 💡 What You Get
|
|
84
142
|
|
|
85
143
|
### Your SQLMesh Project:
|
|
@@ -103,27 +161,17 @@ Airflow Graph View:
|
|
|
103
161
|
|
|
104
162
|
## 📚 Documentation
|
|
105
163
|
|
|
106
|
-
### Getting Started
|
|
107
164
|
- **[Quick Start Guide](docs/QUICKSTART.md)** - Step-by-step tutorial (start here!)
|
|
108
165
|
- **[Quick Reference](docs/QUICK_REFERENCE.md)** - One-page cheat sheet
|
|
109
|
-
- **[
|
|
110
|
-
|
|
111
|
-
### Configuration & Features
|
|
112
|
-
- **[Auto-Scheduling Guide](docs/AUTO_SCHEDULING.md)** - Automatic schedule detection 📅
|
|
166
|
+
- **[Auto-Scheduling Guide](docs/AUTO_SCHEDULING.md)** - Automatic schedule detection 📅 NEW!
|
|
113
167
|
- **[Runtime Configuration](docs/RUNTIME_CONFIGURATION.md)** - Pass credentials via Airflow Connections 🔐
|
|
114
168
|
- **[Multi-Environment Setup](docs/MULTI_ENVIRONMENT.md)** - Configure for dev/staging/prod ⚠️ IMPORTANT
|
|
169
|
+
- **[Migration Guide](docs/MIGRATION_GUIDE.md)** - Fix common configuration issues
|
|
170
|
+
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Common issues and solutions
|
|
171
|
+
- **[Usage Guide](docs/USAGE.md)** - Complete reference
|
|
115
172
|
- **[Dynamic DAGs](docs/DYNAMIC_DAGS.md)** - Fire-and-forget mode explained
|
|
116
|
-
|
|
117
|
-
### Deployment & Production
|
|
118
|
-
- **[Deployment Checklist](docs/DEPLOYMENT_CHECKLIST.md)** - Step-by-step deployment guide ✅ NEW!
|
|
119
|
-
- **[Container Deployment](docs/CONTAINER_DEPLOYMENT.md)** - Docker, Kubernetes, ECS, Cloud Composer 🐳 NEW!
|
|
120
|
-
- **[Common Issues](docs/COMMON_ISSUES.md)** - Real-world problems and solutions 🔧 NEW!
|
|
121
|
-
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Debug guide
|
|
122
173
|
- **[Deployment Warnings](docs/DEPLOYMENT_WARNINGS.md)** - Critical production considerations
|
|
123
|
-
|
|
124
|
-
### Reference
|
|
125
|
-
- **[Usage Guide](docs/USAGE.md)** - Complete reference
|
|
126
|
-
- **[Migration Guide](docs/MIGRATION_GUIDE.md)** - Fix common configuration issues
|
|
174
|
+
- **[Examples](examples/)** - Code examples
|
|
127
175
|
- **[Architecture](docs/ARCHITECTURE.md)** - Technical details
|
|
128
176
|
|
|
129
177
|
## 🔥 Why Dynamic Mode (Default)?
|
|
@@ -178,9 +226,8 @@ Create a `dag_generator_config.yaml`:
|
|
|
178
226
|
```yaml
|
|
179
227
|
sqlmesh:
|
|
180
228
|
project_path: "/path/to/sqlmesh/project"
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
default_catalog: "my_catalog" # Optional: For 3-part naming support
|
|
229
|
+
environment: "prod"
|
|
230
|
+
gateway: "local"
|
|
184
231
|
|
|
185
232
|
airflow:
|
|
186
233
|
dag_id: "sqlmesh_pipeline"
|
|
@@ -198,12 +245,6 @@ generation:
|
|
|
198
245
|
operator_type: "python" # or "bash"
|
|
199
246
|
include_tests: true
|
|
200
247
|
parallel_tasks: true
|
|
201
|
-
auto_replan_on_change: true # Automatically run 'sqlmesh plan' if models change
|
|
202
|
-
# Plan optimization options (for faster execution when no changes)
|
|
203
|
-
skip_backfill: false # Skip apply if backfill is required (use CI/CD for initial backfills)
|
|
204
|
-
plan_only: false # Generate plan without applying (for review/dry-run)
|
|
205
|
-
log_plan_details: true # Log detailed plan information (context, plan, apply phases)
|
|
206
|
-
return_value: true # Enable/disable XCom return values
|
|
207
248
|
```
|
|
208
249
|
|
|
209
250
|
## How It Works
|
|
@@ -323,115 +364,6 @@ black .
|
|
|
323
364
|
ruff check .
|
|
324
365
|
```
|
|
325
366
|
|
|
326
|
-
## 🔧 Troubleshooting
|
|
327
|
-
|
|
328
|
-
### Common Issues
|
|
329
|
-
|
|
330
|
-
#### 1. `TypeError: Object of type CompletionStatus is not JSON serializable`
|
|
331
|
-
**Cause:** Airflow cannot serialize the SQLMesh `CompletionStatus` enum returned by tasks.
|
|
332
|
-
**Fix:** Upgrade to v0.7.2+. This version automatically converts the status to a string.
|
|
333
|
-
Alternatively, use `return_value=False` in your generator config:
|
|
334
|
-
```python
|
|
335
|
-
generator = SQLMeshDAGGenerator(
|
|
336
|
-
...,
|
|
337
|
-
return_value=False # Disable XCom return values
|
|
338
|
-
)
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
#### 2. `ConfigError: A query is required and must be a SELECT statement` (External Models)
|
|
342
|
-
**Cause:** Defining external models in `.sql` files is not supported by the generator.
|
|
343
|
-
**Fix:** Define external models in `external_models.yaml` instead.
|
|
344
|
-
```yaml
|
|
345
|
-
# external_models.yaml
|
|
346
|
-
models:
|
|
347
|
-
- name: raw.users
|
|
348
|
-
kind: EXTERNAL
|
|
349
|
-
columns:
|
|
350
|
-
id: INT
|
|
351
|
-
name: TEXT
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
#### 3. "Environment 'prod' was not found"
|
|
355
|
-
**Cause:** You are using `environment="prod"` instead of `gateway="prod"`.
|
|
356
|
-
**Fix:** Set `gateway="prod"` and leave `environment` as empty string (default).
|
|
357
|
-
```python
|
|
358
|
-
generator = SQLMeshDAGGenerator(
|
|
359
|
-
...,
|
|
360
|
-
gateway="prod",
|
|
361
|
-
environment="" # Correct for production
|
|
362
|
-
)
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
#### 4. Changes to models are not picked up
|
|
366
|
-
**Cause:** SQLMesh requires a plan/apply step to register changes.
|
|
367
|
-
**Fix:** Enable `auto_replan_on_change` in your generator config:
|
|
368
|
-
```python
|
|
369
|
-
generator = SQLMeshDAGGenerator(
|
|
370
|
-
...,
|
|
371
|
-
auto_replan_on_change=True # Automatically runs 'sqlmesh plan --auto-apply'
|
|
372
|
-
)
|
|
373
|
-
```
|
|
374
|
-
Or run `sqlmesh plan --auto-apply` manually.
|
|
375
|
-
|
|
376
|
-
#### 5. `sqlmesh_plan_apply` task takes 7+ minutes even with no changes
|
|
377
|
-
**Cause:** When `auto_replan_on_change=True`, the plan task may run full backfills even when there are no model changes.
|
|
378
|
-
**Fix (v0.9.0+):** The plan optimization now:
|
|
379
|
-
- ✅ Detects "no changes" scenarios and skips apply (~15 seconds instead of 7+ minutes)
|
|
380
|
-
- ✅ Logs detailed timing for each phase (context load, plan compute, apply)
|
|
381
|
-
|
|
382
|
-
For additional control, use these options:
|
|
383
|
-
```python
|
|
384
|
-
generator = SQLMeshDAGGenerator(
|
|
385
|
-
...,
|
|
386
|
-
auto_replan_on_change=True,
|
|
387
|
-
skip_backfill=True, # Skip apply if backfill is required (use CI/CD for initial backfills)
|
|
388
|
-
plan_only=True, # Generate plan without applying (for review/dry-run)
|
|
389
|
-
log_plan_details=True # Log detailed plan information (default: True)
|
|
390
|
-
)
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
**Recommended CI/CD approach:**
|
|
394
|
-
- Run `sqlmesh plan --auto-apply` in CI/CD on model changes (handles backfills)
|
|
395
|
-
- Set `skip_backfill=True` in Airflow DAG (only applies non-backfill changes)
|
|
396
|
-
- This gives you fast DAG runs (~15s) while ensuring changes are still applied
|
|
397
|
-
|
|
398
|
-
#### 5. 3-Part Table Naming Issues (Cross-Database)
|
|
399
|
-
**Cause:** SQLMesh generates queries with 3-part names (catalog.schema.table) which might fail if the catalog name doesn't match.
|
|
400
|
-
**Fix:** Set `default_catalog` in your config to tell SQLMesh which catalog to omit or use.
|
|
401
|
-
```python
|
|
402
|
-
generator = SQLMeshDAGGenerator(
|
|
403
|
-
...,
|
|
404
|
-
default_catalog="my_catalog"
|
|
405
|
-
)
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
### Advanced Features
|
|
409
|
-
|
|
410
|
-
#### Health Check Task
|
|
411
|
-
Add a pre-flight check to verify database connectivity before running models:
|
|
412
|
-
```python
|
|
413
|
-
generator = SQLMeshDAGGenerator(
|
|
414
|
-
...,
|
|
415
|
-
enable_health_check=True
|
|
416
|
-
)
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
#### Skip Audits
|
|
420
|
-
For faster development iterations, you can skip audit checks:
|
|
421
|
-
```python
|
|
422
|
-
generator = SQLMeshDAGGenerator(
|
|
423
|
-
...,
|
|
424
|
-
skip_audits=True
|
|
425
|
-
)
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
#### Partial DAG Runs
|
|
429
|
-
You can generate tasks for a subset of models (useful for testing):
|
|
430
|
-
```python
|
|
431
|
-
# In your DAG file
|
|
432
|
-
generator.create_tasks_in_dag(dag, models=["model_a", "model_b"])
|
|
433
|
-
```
|
|
434
|
-
|
|
435
367
|
## Contributing
|
|
436
368
|
|
|
437
369
|
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
@@ -20,7 +20,7 @@ if requirements_file.exists():
|
|
|
20
20
|
|
|
21
21
|
setup(
|
|
22
22
|
name="sqlmesh-dag-generator",
|
|
23
|
-
version="0.9.
|
|
23
|
+
version="0.9.6",
|
|
24
24
|
description="Open-source Airflow DAG generator for SQLMesh projects",
|
|
25
25
|
long_description=long_description,
|
|
26
26
|
long_description_content_type="text/markdown",
|
{sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/__init__.py
RENAMED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
SQLMesh DAG Generator - Open Source Airflow Integration for SQLMesh
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.9.6"
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
from sqlmesh_dag_generator.generator import SQLMeshDAGGenerator
|
|
9
|
-
from sqlmesh_dag_generator.config import DAGGeneratorConfig
|
|
9
|
+
from sqlmesh_dag_generator.config import DAGGeneratorConfig, RecoveryConfig
|
|
10
10
|
from sqlmesh_dag_generator.airflow_utils import (
|
|
11
11
|
resolve_credentials,
|
|
12
12
|
register_credential_resolver,
|
|
@@ -16,6 +16,7 @@ from sqlmesh_dag_generator.airflow_utils import (
|
|
|
16
16
|
__all__ = [
|
|
17
17
|
"SQLMeshDAGGenerator",
|
|
18
18
|
"DAGGeneratorConfig",
|
|
19
|
+
"RecoveryConfig",
|
|
19
20
|
"resolve_credentials",
|
|
20
21
|
"register_credential_resolver",
|
|
21
22
|
"CredentialResolver",
|