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.
Files changed (37) hide show
  1. {sqlmesh_dag_generator-0.9.0/sqlmesh_dag_generator.egg-info → sqlmesh_dag_generator-0.9.6}/PKG-INFO +80 -148
  2. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/README.md +79 -147
  3. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/setup.py +1 -1
  4. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/__init__.py +3 -2
  5. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/config.py +65 -4
  6. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/dag_builder.py +129 -1
  7. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/generator.py +378 -6
  8. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6/sqlmesh_dag_generator.egg-info}/PKG-INFO +80 -148
  9. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/SOURCES.txt +0 -8
  10. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_airflow_utils.py +0 -26
  11. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_auto_schedule.py +14 -3
  12. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_config.py +10 -0
  13. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_generator.py +198 -1
  14. sqlmesh_dag_generator-0.9.0/tests/test_bug_fix_user_scenario.py +0 -148
  15. sqlmesh_dag_generator-0.9.0/tests/test_cache.py +0 -63
  16. sqlmesh_dag_generator-0.9.0/tests/test_enterprise_features.py +0 -240
  17. sqlmesh_dag_generator-0.9.0/tests/test_environment_default.py +0 -98
  18. sqlmesh_dag_generator-0.9.0/tests/test_environment_handling.py +0 -147
  19. sqlmesh_dag_generator-0.9.0/tests/test_plan_optimization.py +0 -310
  20. sqlmesh_dag_generator-0.9.0/tests/test_runtime_connections.py +0 -221
  21. sqlmesh_dag_generator-0.9.0/tests/test_source_tables.py +0 -211
  22. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/LICENSE +0 -0
  23. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/pyproject.toml +0 -0
  24. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/setup.cfg +0 -0
  25. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/airflow_utils.py +0 -0
  26. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/cli.py +0 -0
  27. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/models.py +0 -0
  28. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/security.py +0 -0
  29. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/utils.py +0 -0
  30. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator/validation.py +0 -0
  31. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/dependency_links.txt +0 -0
  32. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/entry_points.txt +0 -0
  33. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/requires.txt +0 -0
  34. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/sqlmesh_dag_generator.egg-info/top_level.txt +0 -0
  35. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_kubernetes.py +0 -0
  36. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_models.py +0 -0
  37. {sqlmesh_dag_generator-0.9.0 → sqlmesh_dag_generator-0.9.6}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh-dag-generator
3
- Version: 0.9.0
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 for SQLMesh virtual environments (testing)
72
+ # ❌ WRONG - environment parameter is deprecated
80
73
  generator = SQLMeshDAGGenerator(
81
74
  sqlmesh_project_path="/path/to/project",
82
- environment="prod", # ERROR: Environment 'prod' was not found
75
+ environment="prod", # This doesn't work!
83
76
  )
84
77
 
85
- # ✅ CORRECT - Use gateway to switch between dev/staging/prod
78
+ # ✅ CORRECT - Use gateway to switch environments
86
79
  generator = SQLMeshDAGGenerator(
87
80
  sqlmesh_project_path="/path/to/project",
88
- gateway="prod", # This is how you select your environment!
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
- **📚 Read [Understanding SQLMesh Environments](docs/SQLMESH_ENVIRONMENTS.md) for the complete explanation of the difference between `gateway` and `environment`.**
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
- - **[Examples](examples/)** - Code examples
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
- gateway: "prod"
229
- environment: "" # Empty for production
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 for SQLMesh virtual environments (testing)
25
+ # ❌ WRONG - environment parameter is deprecated
33
26
  generator = SQLMeshDAGGenerator(
34
27
  sqlmesh_project_path="/path/to/project",
35
- environment="prod", # ERROR: Environment 'prod' was not found
28
+ environment="prod", # This doesn't work!
36
29
  )
37
30
 
38
- # ✅ CORRECT - Use gateway to switch between dev/staging/prod
31
+ # ✅ CORRECT - Use gateway to switch environments
39
32
  generator = SQLMeshDAGGenerator(
40
33
  sqlmesh_project_path="/path/to/project",
41
- gateway="prod", # This is how you select your environment!
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
- **📚 Read [Understanding SQLMesh Environments](docs/SQLMESH_ENVIRONMENTS.md) for the complete explanation of the difference between `gateway` and `environment`.**
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
- - **[Examples](examples/)** - Code examples
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
- gateway: "prod"
182
- environment: "" # Empty for production
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.0",
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",
@@ -2,11 +2,11 @@
2
2
  SQLMesh DAG Generator - Open Source Airflow Integration for SQLMesh
3
3
  """
4
4
 
5
- __version__ = "0.8.3"
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",