|
| 1 | +# Dagster Multi-Tenant Example |
| 2 | + |
| 3 | +This example project is a runnable Dagster demo that shows how to: |
| 4 | + |
| 5 | +- split one project into multiple Dagster code locations |
| 6 | +- keep tenant pipelines isolated while coordinating them from Dagster |
| 7 | +- treat LLM work as data engineering and context engineering, not just prompt calls |
| 8 | +- run different model/runtime combinations per code location |
| 9 | +- run end to end with a bundled local LLM backend and optionally swap to Ollama |
| 10 | + |
| 11 | +## Getting Started |
| 12 | + |
| 13 | +Bootstrap your own Dagster project with this example: |
| 14 | + |
| 15 | +```bash |
| 16 | +dagster project from-example --name my-dagster-project --example project_multi_tenant |
| 17 | +``` |
| 18 | + |
| 19 | +To work with the copy that lives in this repository directly: |
| 20 | + |
| 21 | +```bash |
| 22 | +cd dagster/examples/project_multi_tenant |
| 23 | +``` |
| 24 | + |
| 25 | +Then continue with the local setup below. |
| 26 | + |
| 27 | +The example uses three business-facing code locations: |
| 28 | + |
| 29 | +- `harbor_outfitters`: retail catalog enrichment |
| 30 | +- `summit_financial`: transaction risk scoring |
| 31 | +- `beacon_hq`: executive reporting built from upstream business outputs |
| 32 | + |
| 33 | +## Architecture |
| 34 | + |
| 35 | +```mermaid |
| 36 | +flowchart LR |
| 37 | + subgraph Dagster["Dagster Workspace"] |
| 38 | + H["harbor_outfitters\nbronze -> silver -> gold"] |
| 39 | + S["summit_financial\nbronze -> silver -> gold"] |
| 40 | + B["beacon_hq\nreporting + executive summary"] |
| 41 | + end |
| 42 | +
|
| 43 | + subgraph Shared["Shared Runtime"] |
| 44 | + O["Embedded local backend\nor optional Ollama"] |
| 45 | + D["DuckDB\nper-code-location databases"] |
| 46 | + R["shared/resources.py\nshared/io_managers.py"] |
| 47 | + end |
| 48 | +
|
| 49 | + H -->|sales_summary| B |
| 50 | + S -->|account_summary| B |
| 51 | +
|
| 52 | + H --> R |
| 53 | + S --> R |
| 54 | + B --> R |
| 55 | +
|
| 56 | + R --> O |
| 57 | + R --> D |
| 58 | +``` |
| 59 | + |
| 60 | +## What The Demo Shows |
| 61 | + |
| 62 | +- `harbor_outfitters` builds catalog context from product, brand, and taxonomy data before generating enriched product copy. |
| 63 | +- `summit_financial` builds investigation context from transactions, accounts, and rules before generating transaction rationales. |
| 64 | +- `beacon_hq` depends on upstream business outputs and turns them into a short executive briefing. |
| 65 | + |
| 66 | +The local setup is intentionally opinionated: |
| 67 | + |
| 68 | +- one root development environment |
| 69 | +- one bundled deterministic LLM backend that works out of the box |
| 70 | +- one optional Ollama backend if you want live model calls |
| 71 | +- optional copied Python environments per code location for runtime marker demos |
| 72 | + |
| 73 | +## Repository Layout |
| 74 | + |
| 75 | +```text |
| 76 | +harbor_outfitters/ Retail assets, jobs, and schedules |
| 77 | +summit_financial/ Financial assets, jobs, and schedules |
| 78 | +beacon_hq/ Reporting assets, jobs, and sensors |
| 79 | +shared/ Shared LLM resource, IO manager, and metadata helpers |
| 80 | +scripts/ Local setup scripts for models and per-location envs |
| 81 | +vendor/ Tiny runtime marker packages installed per code location |
| 82 | +tests/ End-to-end unit tests with fake LLM resources |
| 83 | +workspace.yaml Multi-code-location Dagster workspace |
| 84 | +dagster_cloud.yaml Dagster+ style code location config |
| 85 | +``` |
| 86 | + |
| 87 | +## Local Stack |
| 88 | + |
| 89 | +The default backend is fully in-project: |
| 90 | + |
| 91 | +- `LLM_BACKEND=embedded` uses deterministic local responses and does not require Docker or model downloads. |
| 92 | + |
| 93 | +If you switch to Ollama, each code location uses its own default model: |
| 94 | + |
| 95 | +- `harbor_outfitters`: `qwen2.5:0.5b` |
| 96 | +- `summit_financial`: `qwen2.5:1.5b` |
| 97 | +- `beacon_hq`: `qwen2.5:0.5b` |
| 98 | + |
| 99 | +Business-facing environment variables: |
| 100 | + |
| 101 | +- `LLM_BACKEND` |
| 102 | +- `HARBOR_OUTFITTERS_MODEL` |
| 103 | +- `SUMMIT_FINANCIAL_MODEL` |
| 104 | +- `BEACON_HQ_MODEL` |
| 105 | + |
| 106 | +The project also includes optional runtime marker packages: |
| 107 | + |
| 108 | +- `harbor_outfitters`: `catalog_coach_runtime==1.4.0` |
| 109 | +- `summit_financial`: `risk_reviewer_runtime==2.2.0` |
| 110 | +- `beacon_hq`: `briefing_writer_runtime==0.9.0` |
| 111 | + |
| 112 | +Those markers are bundled under `vendor/` and can be installed into isolated code-location environments if you want to demonstrate per-location runtime differences. |
| 113 | + |
| 114 | +Assets are persisted to per-code-location DuckDB databases under `data/`. |
| 115 | + |
| 116 | +## Quickstart |
| 117 | + |
| 118 | +Copy the local environment file: |
| 119 | + |
| 120 | +```bash |
| 121 | +cp .env.example .env |
| 122 | +``` |
| 123 | + |
| 124 | +Create the root development environment and install the project with its vendor runtime |
| 125 | +marker packages: |
| 126 | + |
| 127 | +```bash |
| 128 | +python -m venv .venv |
| 129 | +source .venv/bin/activate |
| 130 | +pip install -e ".[dev]" |
| 131 | +pip install -e vendor/catalog_coach_runtime \ |
| 132 | + -e vendor/risk_reviewer_runtime \ |
| 133 | + -e vendor/briefing_writer_runtime |
| 134 | +``` |
| 135 | + |
| 136 | +Load the environment and start Dagster: |
| 137 | + |
| 138 | +```bash |
| 139 | +set -a |
| 140 | +source .env |
| 141 | +set +a |
| 142 | +export DAGSTER_HOME=$(pwd)/.dagster_home |
| 143 | +mkdir -p "$DAGSTER_HOME" |
| 144 | +dagster dev -w workspace.yaml |
| 145 | +``` |
| 146 | + |
| 147 | +Then open `http://127.0.0.1:3000`. The webserver and daemon (schedules, sensors, |
| 148 | +backfills) both start automatically. |
| 149 | + |
| 150 | +> **Note:** `dagster dev` is the standard way to start Dagster locally. If you have |
| 151 | +> `dagster-dg-cli` installed (included in the `[dev]` extras), you can also use |
| 152 | +> `dg dev -w workspace.yaml`. |
| 153 | +
|
| 154 | +## Optional Ollama Backend |
| 155 | + |
| 156 | +If you want live model calls instead of the bundled local backend: |
| 157 | + |
| 158 | +```bash |
| 159 | +source .venv/bin/activate |
| 160 | +export LLM_BACKEND=ollama |
| 161 | +docker compose up -d ollama |
| 162 | +./scripts/pull_ollama_models.sh |
| 163 | +dagster dev -w workspace.yaml |
| 164 | +``` |
| 165 | + |
| 166 | +## Optional Isolated Code-Location Environments |
| 167 | + |
| 168 | +If you want each code location to carry its own runtime marker package in a copied environment: |
| 169 | + |
| 170 | +```bash |
| 171 | +source .venv/bin/activate |
| 172 | +./scripts/setup_code_location_envs.sh |
| 173 | +``` |
| 174 | + |
| 175 | +## Validation |
| 176 | + |
| 177 | +Run the local checks with: |
| 178 | + |
| 179 | +```bash |
| 180 | +source .venv/bin/activate |
| 181 | +set -a |
| 182 | +source .env |
| 183 | +set +a |
| 184 | +export DAGSTER_HOME=$(pwd)/.dagster_home |
| 185 | +mkdir -p "$DAGSTER_HOME" |
| 186 | +ruff check . |
| 187 | +pytest -q -o cache_dir=/tmp/pytest-cache |
| 188 | +dagster definitions validate -w workspace.yaml |
| 189 | +``` |
| 190 | + |
| 191 | +## Demo Flow |
| 192 | + |
| 193 | +With `dagster dev` running, open the Dagster UI at `http://127.0.0.1:3000` and |
| 194 | +materialize assets through the UI, or launch the pipeline jobs in order from |
| 195 | +the command line using the GraphQL API or the Jobs page. |
| 196 | + |
| 197 | +The recommended order is: |
| 198 | + |
| 199 | +1. **Harbor Outfitters + Summit Financial** (can run in parallel): |
| 200 | + - `harbor_catalog_publish_job` materializes all Harbor assets (bronze through gold) |
| 201 | + - `summit_risk_scoring_job` materializes all Summit assets (bronze through gold) |
| 202 | +2. **Beacon HQ** (depends on upstream outputs from step 1): |
| 203 | + - `beacon_reporting_inputs_job` builds `consolidated_revenue`, `risk_overview`, and `briefing_highlights` |
| 204 | + - `beacon_executive_briefing_job` produces the `executive_summary` and `executive_llm_audit_log` |
| 205 | + |
| 206 | +If you enable Ollama, avoid launching the LLM-heavy runs in parallel unless you increase machine capacity. |
| 207 | + |
| 208 | +## Jobs And Automation |
| 209 | + |
| 210 | +The repo includes pipeline-shaped jobs for each business unit: |
| 211 | + |
| 212 | +- `harbor_catalog_publish_job` |
| 213 | +- `summit_risk_scoring_job` |
| 214 | +- `beacon_executive_briefing_job` |
| 215 | + |
| 216 | +It also includes simple cross-location orchestration: |
| 217 | + |
| 218 | +- `harbor_daily_refresh_schedule` |
| 219 | +- `summit_daily_refresh_schedule` |
| 220 | +- `beacon_after_upstream_success_sensor` |
| 221 | + |
| 222 | +The key point is that lineage crosses code locations, but execution stays per code location. Beacon reacts to Harbor and Summit outputs instead of collapsing everything into one monolithic job. |
| 223 | + |
| 224 | +## Notes |
| 225 | + |
| 226 | +- Runtime DataFrame metadata includes `row_count` and `top_5_rows` on asset materializations. |
| 227 | +- Tests use fake LLM resources from `tests/fakes.py`; the default project runtime uses the bundled local backend in `shared/resources.py`. |
| 228 | +- Ollama remains available as an opt-in backend for a live local-model demo. |
0 commit comments