DAG & Dependencies
Rocky builds a directed acyclic graph (DAG) from model dependencies to determine execution order. Dependencies come from two merged sources: explicit depends_on declarations in a model’s TOML, plus dependencies auto-resolved from bare table names in the model’s SQL that match another project model (surfaced as diagnostic I001). Rocky uses topological sorting over the merged set to produce a valid execution plan with parallel execution layers.
Declaring dependencies
Section titled “Declaring dependencies”Each model declares what it depends on using the depends_on field in its TOML configuration:
name = "fct_orders"depends_on = ["stg_orders", "dim_customers"]This means fct_orders cannot run until both stg_orders and dim_customers have completed.
Topological sort
Section titled “Topological sort”Rocky uses Kahn’s algorithm to produce a topological ordering of models. The output is deterministic: when multiple models have no remaining dependencies (i.e., they are tied), they are sorted alphabetically.
Execution layers
Section titled “Execution layers”Models are grouped into layers. All models in a layer can run in parallel because their dependencies have been satisfied by earlier layers.
Example dependency graph:
stg_customers ──→ dim_customers ──┐ ├──→ fct_ordersstg_orders ───────────────────────┘This produces three execution layers:
Layer 0: stg_customers, stg_orders (no dependencies, run in parallel)Layer 1: dim_customers (depends on stg_customers)Layer 2: fct_orders (depends on stg_orders + dim_customers)Rocky executes all models in Layer 0 concurrently, waits for them to finish, then executes Layer 1, and so on.
Validation
Section titled “Validation”Rocky validates the DAG at rocky validate time, catching problems before any SQL is executed.
Cycle detection
Section titled “Cycle detection”Circular dependencies are detected and reported as the set of models involved in the cycle:
name = "model_a"depends_on = ["model_b"]
# model_b.tomlname = "model_b"depends_on = ["model_a"]Error: DAG error: circular dependency detected involving: ["model_a", "model_b"]Unknown dependencies
Section titled “Unknown dependencies”References to models that don’t exist are caught:
name = "fct_orders"depends_on = ["stg_orders", "nonexistent_model"]Error: DAG error: unknown dependency 'nonexistent_model' referenced by 'fct_orders'When the unknown name is a near miss for a real model, the message appends a — did you mean '<model>'? suggestion.
External table references
Section titled “External table references”Not every table reference creates a dependency. Rocky classifies references based on how they are qualified in the SQL:
| SQL reference | Classification | DAG behavior |
|---|---|---|
stg_orders (matches a Rocky model) |
Model dependency | Execution edge in DAG |
stg_orders (no matching model) |
External reference | Ignored by DAG |
dbt_fivetran.stg_facebook_ads__ad_history |
Two-part external | Ignored by DAG |
analytics.dbt_fivetran.stg_facebook_ads__ad_history |
Three-part external | Ignored by DAG |
Rocky reads from external tables but does not manage, build, or schedule them.
This distinction enables hybrid workflows where Rocky models consume tables produced by other tools (dbt packages, Fivetran connectors, manual ETL) without needing to convert or import them. External tables appear in column-level lineage but are excluded from execution planning.
-- stg_orders is a Rocky model -> DAG dependency-- dbt_fivetran.stg_facebook_ads__ad_history is external -> no dependencySELECT o.order_id, f.ad_nameFROM stg_orders oJOIN dbt_fivetran.stg_facebook_ads__ad_history f ON o.campaign_id = f.campaign_idSee Using Rocky with dbt Packages for a full guide on this pattern.
How it differs from dbt Core
Section titled “How it differs from dbt Core”dbt Core uses Jinja’s {{ ref('model_name') }} macro inside SQL to create implicit dependencies. The dependency graph is extracted by parsing Jinja templates:
-- dbt modelSELECT *FROM {{ ref('stg_orders') }}JOIN {{ ref('dim_customers') }} USING (customer_id)Rocky uses explicit depends_on declarations in TOML:
depends_on = ["stg_orders", "dim_customers"]The differences:
| dbt Core | Rocky | |
|---|---|---|
| Declaration | Implicit via {{ ref() }} in SQL |
Explicit depends_on in TOML + inferred from plain SQL (no templating) |
| When validated | During parsing/compilation | At rocky validate time |
| SQL purity | SQL mixed with Jinja | Pure SQL, no template language |
| Editor support | Requires dbt LSP for ref() |
Standard SQL tooling works |
You can run rocky validate to check the entire dependency graph without connecting to any warehouse.