Dbt starrocks performance
Skill ivanshamaev/de-agent-skills/group_skills/starrocks_group_skills/dbt_starrocks_performance
dbt + StarRocks performance tuning — model DAG optimization (fan-in/fan-out), incremental merge window sizing, partition-aware incremental filters, avoiding full table rebuilds, query plan hints in dbt SQL (LEADING/JOIN hints), materialized view as dbt model target, concurrent model execution with threads, pre/post hooks for ANALYZE TABLE, dbt run selectors to minimize rebuilt modelsFrom its SKILL.md
npx -y skills add ivanshamaev/de-agent-skills --skill dbt_starrocks_performanceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 15 stars15 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.0 KB, ~2.2k tokens by cl100k_base, as published. Nobody here has run it
dbt + StarRocks Performance Tuning
When to Use
- Incremental dbt models that run slowly due to poor partition filtering
- dbt DAG with wide fan-out causing excessive table rebuilds
- Need to run ANALYZE TABLE after dbt model refreshes
- Optimizing join order and query plans for complex dbt models
- Tuning thread count and model concurrency for large dbt projects
Partition-Aware Incremental Filters
The most impactful optimization: ensure incremental models only scan new partitions.
-- BAD: scans entire source table even in incremental mode
{{ config(
materialized='incremental',
incremental_strategy='append',
partition_by={"field": "created_at", "data_type": "date", "granularity": "day"}
) }}
SELECT * FROM {{ source('raw', 'events') }}
{% if is_incremental() %}
WHERE event_ts > (SELECT MAX(event_ts) FROM {{ this }})
{% endif %}
The MAX(event_ts) subquery scans this — fine for small tables but expensive for billions of rows.
-- GOOD: use partition column for both filter and watermark
{{ config(
materialized='incremental',
incremental_strategy='insert_overwrite',
partition_by={"field": "event_date", "data_type": "date", "granularity": "day"}
) }}
{% set lookback_days = 3 %}
SELECT
DATE(event_ts) AS event_date,
user_id,
event_type,
event_ts
FROM {{ source('raw', 'events') }}
{% if is_incremental() %}
-- Reprocess last N days to handle late-arriving data
WHERE DATE(event_ts) >= DATE_SUB(CURDATE(), INTERVAL {{ lookback_days }} DAY)
{% endif %}
With insert_overwrite, dbt replaces only the matching partitions — no full scan of this.
Late-Arriving Data Window
Balance completeness vs performance:
{% set incremental_lookback_days = var('lookback_days', 3) %}
{% if is_incremental() %}
WHERE DATE(created_at) >= DATE_SUB(
-- Use a fixed reference point, not NOW(), for reproducibility
DATE('{{ var("ds", modules.datetime.date.today().isoformat()) }}'),
INTERVAL {{ incremental_lookback_days }} DAY
)
{% endif %}
Run with override:
dbt run --vars '{"lookback_days": 7, "ds": "2024-01-15"}'
ANALYZE TABLE via Post-Hook
Always refresh CBO statistics after materializing a table:
-- models/gold/orders_daily.sql
{{ config(
materialized='table',
post_hook=[
"ANALYZE TABLE {{ this }} WITH ASYNC MODE",
]
) }}
SELECT ...
For incremental models with partition granularity:
{{ config(
materialized='incremental',
incremental_strategy='insert_overwrite',
partition_by={"field": "dt", "data_type": "date", "granularity": "day"},
post_hook=[
-- Analyze only today's partition (much faster than full table)
"ANALYZE TABLE {{ this }} PARTITION (p{{ run_started_at.strftime('%Y%m%d') }}) WITH ASYNC MODE",
]
) }}
Bulk ANALYZE in post-hook for large projects:
# dbt_project.yml
models:
my_project:
gold:
+post-hook: "ANALYZE TABLE {{ this }} WITH ASYNC MODE"
Query Hints in dbt Models
Embed StarRocks query hints directly in model SQL for complex join optimization:
-- models/gold/revenue_report.sql
{{ config(materialized='table') }}
-- Hint: force join order (orders first, then products)
SELECT /*+ LEADING(o p c) JOIN_METHOD(o, p, BROADCAST) */
p.category,
c.region,
COUNT(*) AS order_count,
SUM(o.amount) AS revenue
FROM {{ ref('orders') }} o
JOIN {{ ref('dim_products') }} p USING (product_id)
JOIN {{ ref('dim_customers') }} c USING (customer_id)
WHERE o.dt = CURDATE()
GROUP BY p.category, c.region
Pre-Hook: Ensure Partition Exists
For models that INSERT OVERWRITE into manually managed partitions:
{{ config(
materialized='incremental',
incremental_strategy='insert_overwrite',
pre_hook=[
"""
ALTER TABLE {{ this }}
ADD PARTITION IF NOT EXISTS p{{ run_started_at.strftime('%Y%m%d') }}
VALUES [("{{ run_started_at.strftime('%Y-%m-%d') }}"),
("{{ (run_started_at + modules.datetime.timedelta(days=1)).strftime('%Y-%m-%d') }}"))
"""
]
) }}
Materialized View as dbt Model
Create a StarRocks async MV using a dbt custom materialization or post-hook:
-- Approach 1: Create MV in a post-hook on the base table
{{ config(
materialized='table',
post_hook=[
"""
CREATE MATERIALIZED VIEW IF NOT EXISTS {{ this }}_mv
DISTRIBUTED BY HASH(region) BUCKETS 8
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
AS SELECT region, COUNT(*) AS orders, SUM(amount) AS revenue
FROM {{ this }}
GROUP BY region
"""
]
) }}
Thread and Concurrency Tuning
profiles.yml Thread Config
starrocks_project:
outputs:
prod:
type: starrocks
threads: 8 # concurrent model builds (default: 1)
# ...
Which Models Can Run in Parallel
dbt parallelizes models with no dependencies between them. Ensure your DAG is as wide as possible:
staging/stg_orders ─┐
staging/stg_customers ─┼──► silver/orders (sequential join)
staging/stg_products ─┘
silver/orders_by_region ─┐
silver/orders_by_product ─┼──► gold/revenue_report
silver/customer_segments ─┘
Avoid chains where each model depends on the previous — this prevents parallelism.
Model Selection for Minimal Rebuilds
# Only run models that changed + their downstream dependents
dbt run --select state:modified+
# Run only changed models, no downstream
dbt run --select state:modified
# Run only a specific model and its upstream dependencies
dbt run --select +orders
# Run a specific tag group
dbt run --select tag:daily
# Exclude expensive models from CI
dbt run --exclude tag:expensive
var() for Environment-Specific Config
{{ config(
materialized='incremental',
incremental_strategy='insert_overwrite',
partition_by={
"field": "dt",
"data_type": "date",
"granularity": var('partition_granularity', 'day')
},
properties={
"replication_num": var('replication_num', '3')
}
) }}
# dbt_project.yml
vars:
partition_granularity: "day"
replication_num: "3" # override to "1" in dev
dbt run --vars '{"replication_num": "1"}' --target dev
Materialization Choice vs Performance
| Model Type | Materialization | Rebuild Frequency | Performance |
|---|---|---|---|
| Staging (raw cleanup) | table | Daily | Full rebuild is cheap |
| Silver (CDC upsert) | incremental (unique_key) | Continuous | Only processes delta |
| Intermediate heavy joins | table | Daily | Avoid ephemeral for >100M rows |
| Gold partitions | incremental (insert_overwrite) | Daily | Replace partitions, not full table |
| Real-time metrics | MV via post-hook | Auto (async) | Near-real-time without DAG run |
| Rarely queried reports | view | On query | No storage cost |
Profiling Slow Models
# Show timing for each model
dbt run --select orders_daily --log-level debug 2>&1 | grep "Completed"
# Use dbt artifacts for timing analysis
cat target/run_results.json | python3 -c "
import json, sys
r = json.load(sys.stdin)
for n in sorted(r['results'], key=lambda x: x.get('execution_time', 0), reverse=True)[:10]:
print(f\"{n['execution_time']:.1f}s {n['unique_id']}\")
"
Anti-Patterns
incremental_strategy='append'on a growing table without watermark — re-runs append all historical rows repeatedly; always add anis_incremental()filter.MAX(event_ts) FROM {{ this }}for watermark on billion-row tables — scans the entire previous model; use partition column bounds instead.threads: 1— serial execution for large dbt projects can take hours; set to 4-16 based on BE count.- No post-hook ANALYZE — CBO uses stale stats after model rebuild, producing bad plans for downstream queries; always ANALYZE after table materialization.
- Using
viewfor heavy aggregations — view recomputes the aggregation on every BI query; materialize as table or MV. on_schema_change='sync_all_columns'on large tables — triggers full table rebuild every time a column is added; useappend_new_columnsinstead.
References
- dbt-starrocks incremental:
github.com/StarRocks/starrocks/tree/main/contrib/dbt-connector - ANALYZE TABLE:
docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/ANALYZE_TABLE/ - dbt model selection:
docs.getdbt.com/reference/node-selection/syntax - Related skills:
[[dbt-starrocks-models]],[[starrocks-cbo]],[[starrocks-query-optimizer]],[[dbt-core]]
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.